Тестирование и интеграция простых объектов ruby ​​в приложение Rails 3.2 - PullRequest
2 голосов
/ 15 марта 2012

После просмотра выступления Fasts Rails выступления Кори и чтения Объекта на Rails Avdi Я нахожусь в процессе настройки моего нового приложения Rails 3.2, чтобы воспользоваться концепции.

Чтобы все заработало, я начал с этого примера файла спецификации.

# spec_no_rails/projects/financials_spec.rb
require_relative '../spec_no_rails_helper'

class DummyProject
  include Modules::Projects::Financials
end

describe Modules::Projects::Financials do
  it 'should have some method' do
    DummyProject.new.foo.should == 'bar'
  end
end

И это был начальный файл spec_no_rails_helper.rb, который используется для запроса модулей

# spec_no_rails/spec_no_rails_helper.rb
Dir["#{Dir.pwd}/app/pimms/**/*.rb"].each { |file| require file }

Затем я приступил к созданию нового примера модуля.

# app/pimms/modules/projects/financials.rb
module Modules::Projects::Financials
  def foo
    'bar'
  end
end

Чтобы увидеть, что все будет работать, когда я включил новый автономный модуль в один из моих классов ActiveRecord, я добавил следующую строку в одну из моих моделей.

# app/models/project.rb
class Project < ActiveRecord::Base
  include Modules::Projects::Financials
end

Это позволило мне открыть консоль и увидеть, что все работает как положено.

> Project.first.foo
=> "bar"

Итак, на этом этапе я определил автономный модуль пространства имен, определенный в app / pimms / modules / projects / financials.rb, который я могу включить в модель Rails, и все работает как положено.

Проблема, с которой я сталкиваюсь, заключается в том, что, когда я пытаюсь запустить спецификации, я получаю следующее.

> bundle exec rspec spec_no_rails/
/Users/scott/Code/pimms/spec_no_rails/projects/financials_spec.rb:5:in `<class:DummyProject>': uninitialized constant DummyProject::Modules (NameError)
  from /Users/scott/Code/pimms/spec_no_rails/projects/financials_spec.rb:4:in `<top (required)>'
  from /Users/scott/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.8.0/lib/rspec/core/configuration.rb:698:in `load'
  from /Users/scott/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.8.0/lib/rspec/core/configuration.rb:698:in `block in load_spec_files'
  from /Users/scott/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.8.0/lib/rspec/core/configuration.rb:698:in `map'
  from /Users/scott/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.8.0/lib/rspec/core/configuration.rb:698:in `load_spec_files'
  from /Users/scott/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.8.0/lib/rspec/core/command_line.rb:22:in `run'
  from /Users/scott/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.8.0/lib/rspec/core/runner.rb:80:in `run_in_process'
  from /Users/scott/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.8.0/lib/rspec/core/runner.rb:69:in `run'
  from /Users/scott/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.8.0/lib/rspec/core/runner.rb:10:in `block in autorun'

Таким образом, причина этого в том, что пространства имен Modules :: Projects не были определены во время выполнения тестов. Я предполагаю, что мне не нужно было определять пространство имен Modules :: Projects, когда я использовал модуль с приложением Rails, потому что Rails обработал это для меня.

Чтобы тест выполнялся должным образом, мне нужно было определить пространство имен в файле spec_no_rails_helper.rb следующим образом.

# spec_no_rails/spec_no_rails_helper.rb
module Modules
  module Projects
  end
end

Dir["#{Dir.pwd}/app/pimms/**/*.rb"].each { |file| require file }

Это, очевидно, не идеально, поскольку мне пришлось бы вручную создавать все пространства имен для любых автономных модулей или классов, которые я не тестировал вне Rails.

Есть ли лучший способ настроить приложение Rails, чтобы я мог легко запускать набор тестов, не полагаясь на Rails?

1 Ответ

5 голосов
/ 15 марта 2012

Я думаю, это из-за того, как вы определили модуль. Вы пытаетесь определить это так:

module Modules::Projects::Financials
  def foo
   'bar'
  end
end

Но Ruby интерпретирует это, пытаясь сначала найти модуль Modules, затем модуль Projects и, наконец, определит модуль Financials, но ТОЛЬКО если он сможет найти первые два.

В вашем случае, не может найти первые два, и поэтому бомбы вот так.

Продолжайте, запустите irb, скопируйте и вставьте в него приведенный выше пример кода. Вы увидите точно такую ​​же ошибку, что и при запуске своих спецификаций.


Способ исправить это - просто определить / заново открыть модуль в каждом файле:

module Modules
  module Projects
    module Financials
      def foo
        'bar'
      end
    end
  end
end

Теперь, когда каждый из этих файлов загружается, он либо определяет, либо заново открывает модули, добавляя к ним функциональность. Самое приятное в этом то, что не имеет значения, если Modules определен или не определен первым, он просто определит его в любом случае.


Теперь обратимся к вопросу: почему это работает в Rails?

О, чувак, Rails делает совершенно потрясающие вещи!

На самом деле я рассказал о том, как Rails работает с автоматическим определением модулей в моем скринкасте "неправильный тип аргумента" . Ну, я не совсем понимаю, как модули загружаются автоматически, но ( spoiler alert ) это виновник того, что там происходит не так.

Я не собираюсь заставлять вас смотреть это. Проблема заключается в этих строках внутри activesupport/lib/active_support/dependencies.rb.

Метод load_missing_constant используется Rails, когда он не может найти константу. Именно тогда волшебная самозарядная система Rails включается. Он вызывает этот метод и пытается найти файл, который определяет этот модуль.

Если он не может этого сделать, он делает это:

mod = Module.new
into.const_set const_name, mod
autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
return mod

Это внутри autoload_module! метода в этом dependencies.rb файле .

То, что делает этот код, просто: создает новый модуль, устанавливает его постоянное имя на тот, который отсутствует, добавляет qualified_name к autoloaded_constants и возвращает этот модуль.

Именно поэтому Rails определяет ваши модули , хотя их на самом деле не существует . Вы полностью игнорируете это в своей спецификации (по уважительной причине, вам не нужен весь этот неприятный мусор Rails), и поэтому он не загружает модули автоматически.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...