Ошибка при установке CoffeeScript в Windows и как ее бороть
Устанавливал себе в винду CoffeeScript согласно ману на офсайте.
После запуска команды npm install -g coffee-script
появлялось сообщение npm http GET https://registry.npmjs.org/coffee-script и все надолго останавливалось.
Несколько раз прекращал по Ctrl-C, а в последний раз вывалился стек сообщений об ошибках. Оказалось, решение простое - указать npm-у где лежит реестр (приложений?):
npm config set registry http://registry.npmjs.org/
После этого установилось:
C:\InstantRails\ruby_apps\coffee_script>npm --version
1.1.0-beta-4
C:\InstantRails\ruby_apps\coffee_script>npm install -g coffee-script
npm http GET https://registry.npmjs.org/coffee-script
npm ERR! Error: failed to fetch from registry: coffee-script
npm ERR! at C:\Program Files\nodejs\node_modules\npm\lib\utils\npm-registry-client\get.js:139:12
npm ERR! at cb (C:\Program Files\nodejs\node_modules\npm\lib\utils\npm-registry-client\request.js:32:9)
npm ERR! at Request._callback (C:\Program Files\nodejs\node_modules\npm\lib\utils\npm-registry-client\request.js:137:18)
npm ERR! at Request.callback (C:\Program Files\nodejs\node_modules\npm\node_modules\request\main.js:104:22)
npm ERR! at Request.<anonymous> (C:\Program Files\nodejs\node_modules\npm\node_modules\request\main.js:181:58)
npm ERR! at Request.emit (events.js:88:20)
npm ERR! at ClientRequest.<anonymous> (C:\Program Files\nodejs\node_modules\npm\node_modules\request\main.js:178:10)
npm ERR! at ClientRequest.emit (events.js:67:17)
npm ERR! at CleartextStream.<anonymous> (http.js:1174:13)
npm ERR! at CleartextStream.emit (events.js:88:20)
npm ERR! Report this *entire* log at:
npm ERR! <http://github.com/isaacs/npm/issues>
npm ERR! or email it to:
npm ERR! <npm-@googlegroups.com>
npm ERR!
npm ERR! System Windows_NT 5.1.2600
npm ERR! command "C:\\Program Files\\nodejs\\\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "instal
l" "-g" "coffee-script"
npm ERR! cwd C:\InstantRails\ruby_apps\coffee_script
npm ERR! node -v v0.6.6
npm ERR! npm -v 1.1.0-beta-4
npm ERR! message failed to fetch from registry: coffee-script
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR! C:\InstantRails\ruby_apps\coffee_script\npm-debug.log
npm not ok
C:\InstantRails\ruby_apps\coffee_script>npm config set registry http://registry.npmjs.org/
C:\InstantRails\ruby_apps\coffee_script>npm install -g coffee-script
npm http GET http://registry.npmjs.org/coffee-script
npm http 200 http://registry.npmjs.org/coffee-script
npm http GET http://registry.npmjs.org/coffee-script/-/coffee-script-1.2.0.tgz
npm http 200 http://registry.npmjs.org/coffee-script/-/coffee-script-1.2.0.tgz
C:\Documents and Settings\Sony\Application Data\npm\coffee -> C:\Documents and Settings\Sony\Application Data\npm\node_modules\c
offee-script\bin\coffee
C:\Documents and Settings\Sony\Application Data\npm\cake -> C:\Documents and Settings\Sony\Application Data\npm\node_modules\cof
fee-script\bin\cake
coffee-script@1.2.0 C:\Documents and Settings\Sony\Application Data\npm\node_modules\coffee-script
C:\InstantRails\ruby_apps\coffee_script>
Сохранение контекста блока при использовании yield
Наткнулся в коде на два способа вызова блока в методе, куда он передан: yield и instance_eval. Зацените:
class A
def do_smth opts={}, &block
if opts[:yield_self]
yield self
else
instance_eval &block
end
end
end
a = A.new
a.do_smth :yield_self => false do
puts "self: %s"%self.inspect
end
a.do_smth :yield_self => true do
puts "self: %s"%self.inspect
end
Выводит в консоль:
C:\InstantRails\ruby_apps\yield>ruby test_yield.rb
self: #\<A:0x2852688\>
self: main
Вывод такой - yield выполняет блок в контексте, внешнем по отношению к методу, в котором вызывается yield, а instance_eval выполняет блок непосредственно в контексте того метода, к котором вызывается сам instance_eval.
Тестирование коммуникационной части на основе eventmachine при помощи сucumber
4.0 из 1 гол.Например, хочется подвергнуть cucumber-тестированию не только бизнес логику, но и коммуникационную часть.
Примерно так:
# language: ru
Функционал: Коммуникация через socket_connection
Чтобы быть уверенным, что сервер получает от socket_connection правильные
объекты, когда socket_connection получает из сокета правильные строки
Контекст: Поднят сервер EventMachine
Допустим используется протокол "DataExchange::ProtocolXml"
И поднят сервер EventMachine на порту "10001"
Сценарий: Приходит строка connect, должно быть создано событие
Если от клиента приходит строка "<command token='connect' key='секретный ключ'/>"
То сокет драйвер передает серверу событие:
|атрибут|значение |
|class |DataExchange::TokenConnect|
|key |секретный ключ |
@wip
Сценарий: Приходит строка с raise, должно сгенерироваться событие
Если от клиента приходит строка "<command token='raise' key='x' amount=100/>"
То сокет драйвер передает серверу событие:
|атрибут|значение |
|class |DataExchange::TokenRaise |
|key |x |
|amount |100 |
Если коммуникационная часть построена на основе EventMachine, то должен быть задействован основной цикл реагирования EM:
EventMachine.run do
...
end
Запускаем его в параллельной нити. Эта нить завершится как только завершится основная нить с тестами.
Допустим /^поднят сервер EventMachine на порту "(.*)"$/ do |port_str|
@port = port_str.to_i
Thread.new do
EventMachine.run do
puts "EventMachine called"
# hit Control + C to stop
Signal.trap("INT") { EventMachine.stop }
Signal.trap("TERM") { EventMachine.stop }
end
end
EventMachine::start_server( "0.0.0.0", @port, SocketConnection )
end
Вот шаги steps для использования в тестах:
Допустим /^подключается клиент$/ do #"
@socket ||= TCPSocket.open( 'localhost', @port )
end
Если /^от клиента приходит строка "(.*)"$/ do |data_str| #"
Допустим 'подключается клиент'
@socket<< data_str # отправили в сокет
sleep 0.1 # дать пакету время пройти стек протоколов
# и время другой нити для приема пакета
end
То /^сокет драйвер передает серверу событие:$/ do |table|
# создать событие из данных в таблице
hash = {}
table.hashes.each do |item|
hash[item["атрибут"]] = item["значение"]
end
# тут в hash доступны параметры, указанные в таблице в тесте
# мой код, а будет ваш ->
token = eval "#{hash['class']}.new"
token.set_attributes! hash
# <-
#сравнить с тем, что получил сервер
@mock_server.events.shift.should == token
end
О применении cucumber для тестирования не web приложения.
Cucumber - превосходный фреймворк для тестирования web-приложения. Он поддерживает Rails, Sinatra, Merb.
Я хочу попробовать приспособить его для тестирования серверного не web приложения. А точнее для тестирования самописного игрового сервера Texas Holdem. Суть эксперимента состоит в том чтобы тестировать только движок без коммуникационной части, чтобы можно было писать примерно такие сценарии:
# language: ru
Функционал: Работа с блайндами в Техасском Холдеме
Чтобы протестировать работу с блайндами
Являясь разработчиком, использующим cucumber
Я хочу быть уверенным, что блайнды назначаются корректно
Сценарий: Дилер наместе 1, блайнды на 2,3
Допустим создана игра для игроков:
| место | никнейм | стек |
| 1 | Иван | 20000 |
| 2 | Роман | 20000 |
| 3 | Дэн | 20000 |
| 4 | Злой | 20000 |
| 5 | Абель | 20000 |
И дилер Иван
И сумма малого блайнда 1
Если на малом блайнде Роман
То на большом блайнде Дэн
И сумма малого блайнда 1
И сумма большого блайнда 2
И стек игрока Иван 20000
И стек игрока Роман 19999
И стек игрока Дэн 19998
И стек игрока Злой 20000
И стек игрока Абель 20000.0
И ожидается ход игрока Злой
Чтобы реализовать это, планировал разбираться с геммой aruba, надеясь там почерпнуть вдохновение.
Вдохновение почерпнул не из aruba, а из примера calculator, приложенного к cucumber (cucumber-1.1.0\examples\i18n\ru\features\step_definitions\calculator_steps.rb)
Пришлось в ходе исследования немного переписать сценарий. Дело в том, что создать игру можно только когда известно на каких местах сидят игроки, с какими стеками, кто дилер и каков размер малого блайнда.
| Исходный | Модифицированный |
| Допустим создана игра для игроков: | место | никнейм | стек | | 1 | Иван | 20000 | | 2 | Роман | 20000 | | 3 | Дэн | 20000 | | 4 | Злой | 20000 | | 5 | Абель | 20000 | И дилер Иван И сумма малого блайнда 1 |
Допустим за столом сидят игроки: | место | никнейм | стек | | 1 | Иван | 20000 | | 2 | Роман | 20000 | | 3 | Дэн | 20000 | | 4 | Злой | 20000 | | 5 | Абель | 20000 | Если создана игра, где дилер Иван, а сумма малого блайнда 2 |
| Видно, что в исходном сценарии только в строке И сумма малого блайнда 1 становится известен последний необходимый параметр. Поэтому строфа Допустим создана игра ... должна идти после строфы И сумма малого блайнда 1. |
В модифицированном сценарии сначала задается список игроков, а затем создается игра с передачей необходимых ей параметров. |
Таким образом, если есть желание протестировать вашу суперовую логику, то начинаете именно с написания теста. Для начала всего одного, вот например с такого
Сценарий: Дилер на месте 1, блайнды на 2,1 и всего двое игроков
Допустим за столом сидят игроки:
| место | никнейм | стек |
| 1 | Иван | 20000 |
| 2 | Роман | 20000 |
Если создана игра, где дилер Иван, а сумма малого блайнда 1
То на малом блайнде Роман
А на большом блайнде Иван
И сумма малого блайнда 1
И сумма большого блайнда 2
И стек игрока Иван 19998
И стек игрока Роман 19999
И ожидается ход игрока Роман
И помещаете его в файл PROJECT_ROOT/features/blinds.feature. Также создаете папки, которые ожидает найти кукумбер:
PROJECT_ROOT/features/support
PROJECT_ROOT/features/step_definitions
Создаете файл PROJECT_ROOT/features/support/env.rb с таким содержанием:
# encoding: utf-8
begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end
require 'cucumber/formatter/unicode'
$:.unshift(File.dirname(__FILE__) + '/../../lib')
И в корневой папке проекта запускаете cucumber. Получаете такой вывод:


Копируете весь вывод после фразы: You can implement step definitions for undefined steps with these snippets:
в файл PROJECT_ROOT/features/step_definitions/blinds_steps.rb Это заготовка наших шагов:
Затем надо их реализовать один за другим, запуская кук после реализации каждого метода.Причем кол-во шагов должно сократиться за счет однотипных:
То /^стек игрока Иван (\d+)$/ do |arg1|
То /^стек игрока Роман (\d+)$/ do |arg1|
Получится примерно так:
require File.expand_path(File.join(File.dirname(__FILE__), '../../lib/server_config_test'))
require File.expand_path(File.join(File.dirname(__FILE__), '../../lib/texas_holdem'))
GameServer::ServerConfig.TestConfigBlackHole
Допустим /^за столом сидят игроки:$/ do |table|
@players= []
id = 0
table.hashes.each do |item|
seat = item['место'].to_i
nickname = item['никнейм']
stack_amount = item['стек'].to_i
player = PokerServer::Player.new(nickname, id += 1).take_seat( seat, stack_amount )
@players<< player
end
end
Если /^создана игра, где дилер Иван, а сумма малого блайнда (\d+)$/ do |arg1|
pending # express the regexp above with the code you wish you had
end
То /^на малом блайнде (.*)$/ do | player_nickname |
pending # express the regexp above with the code you wish you had
end
То /^на большом блайнде (.*)$/ do | player_nickname |
pending # express the regexp above with the code you wish you had
end
То /^сумма малого блайнда (\d+)$/ do |arg1|
pending # express the regexp above with the code you wish you had
end
То /^сумма большого блайнда (\d+)$/ do |arg1|
pending # express the regexp above with the code you wish you had
end
То /^стек игрока (.*) (\d+)$/ do | player_nickname, stack_amount |
pending # express the regexp above with the code you wish you had
end
То /^ожидается ход игрока (.*)$/ do | player_nickname |
pending # express the regexp above with the code you wish you had
end
После реализации первого шага вывод изменится:

Один шаг успешно пройден! После реализации всех шагов картинка станет такой:

Что получается?
Чтобы тестировать произвольное приложение, достаточно определить использованные в сценариях тестирования шаги в rb-файлах в папке PROJECT_ROOT/features/step_definitions. И все, вот так просто!
ps
Кук целесообразно использовать для тестирования тех компонентов, которые содержат основную, так называемую "бизнес-логику", описать которую эффективно получится на человеческом языке, а какие-нибудь вспомогательные классы, работу которых запаришься описывать словами, целесообразнее тестировать TestUnit-ом.
pps
если в сценарии заключать параметры шагов в кавычки, то заготовки шагов, которые генерирует кук, будут с учетом параметров.
То на малом блайнде "Роман"
А на большом блайнде Иван"
И сумма малого блайнда "1"
И сумма большого блайнда "2"
И стек игрока "Иван" "19998"
И стек игрока "Роман" "19999"
И ожидается ход "игрока" "Роман"
Простое средство для создания гуевой (GUI) оболочки к консольной утилите
На случай, если вашу полезную консольную утилиту надо по быстрому адаптировать к гуевому интерфейсу, в Linux нашел спецовое средство - zenity. В винде тоже есть.
Это консольный утил, который получает параметры и рисует пользователю определенный параметрами Диалог. Диалогами могут быть запросы текста, вывод соообщений/предупреждений/ошибок, выбор даты, выбор значения из списка, и т.п. - все стандартные элементы интерфейса, можно даже выводить прогресс бар.
Выбранное пользователем значение zenity печатает в stdout.
Можно забацать уютный класс-обертку, а можно использовать совсем просто:
input_date_str = `zenity --calendar --text Введите\ дату\ документа`
Это позволяет супер просто реализовать мастер ввода параметров вашей полезной утилиты.
Конечно, у такого подхода есть и недостатки - последовательный показ нескольких диалогов, вместо одного насыщенного элементами диалога, невозможность проверить введенные значения непосредственно в диалоге, мелькание диалогов, если параметров надо ввести более 3-х. Но в простых случаях вполне юзабельно.


Atom