rails-ioc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ **/.DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rails-ioc.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,155 @@
1
+ ## Quickstart
2
+
3
+ Gemfile:
4
+
5
+ ```ruby
6
+ gem 'rails-ioc'
7
+ ```
8
+
9
+ config/dependencies/development.rb:
10
+
11
+ ```ruby
12
+ RailsIOC::Dependencies.define do
13
+ prototype :ccv, CreditCardValidator, "Visa", "Mastercard"
14
+ prototype :payment_gateway, DummyPaymentGateway, ref(:ccv)
15
+
16
+ controller PaymentsController, {
17
+ payment_gateway: ref(:payment_gateway),
18
+ }
19
+ end
20
+ ```
21
+
22
+ app/controllers/payments_controller.rb:
23
+
24
+ ```ruby
25
+ class PaymentsController < ApplicationController
26
+ def accept_payment
27
+ @payment_gateway.process(params[:credit_card])
28
+ end
29
+ end
30
+ ```
31
+
32
+ ## Background
33
+
34
+ Inversion of control by dependency injection is widely used in large software systems and is generally accepted as being a "good idea". If the terms are unfamiliar, do some research before going any further. These are good places to start:
35
+
36
+ - http://en.wikipedia.org/wiki/Inversion_of_control
37
+ - http://martinfowler.com/articles/injection.html
38
+
39
+ The practice as described in the articles above is not particularly common in Ruby applications. Jamis Buck, author of the erstwhile DI framework [Copland](http://copland.rubyforge.org) makes a [compelling argument](http://weblog.jamisbuck.org/2008/11/9/legos-play-doh-and-programming) against it. If you have to ask the question "will I need IOC for my next Rails app", the answer is almost certainly "No". The core components of a standard Rails app usually interact politely and can generally be tested reasonably easily in most apps.
40
+
41
+ Still, having run into several Rails projects which dealt with dependencies beyond a simple ActiveRecord database connection (payment gateways, email services, version control systems, image processing services and more) I realised that I was reminiscing fondly about Java and .NET's various containers for two reasons:
42
+
43
+ 1. I really hate mocking calls to MyService#new to force a controller to use a stub implementation in a unit test.
44
+ 2. There is no clean way to switch dependencies out on a per-environment basis (to use the testable version of a payment gateway in the staging environment, for example).
45
+
46
+ RailsIOC attempts to make these problems less painful for applications with complex interactions with external services by providing a very lightweight way to define dependencies and inject them into ActionController. The patterns will be familiar to anyone who has used [Spring](http://www.springsource.org/documentation) or [Unity](http://msdn.microsoft.com/en-us/library/dd203319.aspx). Where possible, RailsIOC enforces constructor injection. The exception to this rule is the creation of controllers, where it avoids interfering with Rails' own instantiation and injects dependencies as local @variables.
47
+
48
+ ## Cleaner Testing
49
+ ### Before
50
+
51
+ ```ruby
52
+ #Controller
53
+ @gateway = Gateway.new(ServiceA.new, ServiceB.new)
54
+ @gateway.do_something!
55
+
56
+ #RSpec
57
+ @svc_a = mock(ServiceA)
58
+ @svc_b = mock(ServiceB)
59
+ ServiceA.stub(:new).and_return(@svc_a)
60
+ ServiceB.stub(:new).and_return(@svc_b)
61
+ @gateway = mock(Gateway)
62
+ @gateway.should_receive(:do_something!).and_return(12345)
63
+ Gateway.stub(:new).with(@svc_a, @svc_b).and_return(@gateway)
64
+ ```
65
+
66
+ ### After
67
+
68
+ ```ruby
69
+ #Controller
70
+ @gateway.do_something!
71
+
72
+ #RSpec
73
+ @gateway = mock(Gateway)
74
+ @gateway.should_receive(:do_something!).and_return(12345)
75
+ controller_dependencies(gateway: @gateway)
76
+ ```
77
+
78
+ ## Customise Dependencies Per-Environment
79
+ ### Before
80
+
81
+ PaymentController:
82
+
83
+ ```ruby
84
+ class PaymentsController < ApplicationController
85
+ def accept_payment
86
+ if Rails.env.development? || Rails.env.test?
87
+ @credit_card_validator = BogusCardValidator.new
88
+ else
89
+ @credit_card_validator = RealCardValidator.new
90
+ end
91
+ if Rails.env.production?
92
+ @gateway = RealPaymentGateway.new
93
+ elsif Rails.env.staging?
94
+ @gateway = RealPaymentGateway.new(:use_testing_url)
95
+ else
96
+ @gateway = BogusPaymentGateway.new
97
+ end
98
+ card = @credit_card_validator.validate(params[:card])
99
+ @gateway.process(card)
100
+ end
101
+ end
102
+ ```
103
+
104
+ ### After
105
+
106
+
107
+ app/controllers/payments_controller:
108
+
109
+ ```ruby
110
+ class PaymentsController < ApplicationController
111
+ def accept_payment
112
+ card = @credit_card_validator.validate(params[:card])
113
+ @gateway.process(card)
114
+ end
115
+ end
116
+ ```
117
+
118
+ config/dependencies/production.rb:
119
+
120
+ ```ruby
121
+ RailsIOC::Dependencies.define do
122
+ prototype :payment_gateway, RealPaymentGateway
123
+ prototype :credit_card_validator, RealCardValidator
124
+
125
+ controller MyController, {
126
+ gateway: ref(:payment_gateway)
127
+ credit_card_validator: ref(:credit_card_validator)
128
+ }
129
+ end
130
+ ```
131
+
132
+ config/dependencies/staging.rb:
133
+
134
+ ```ruby
135
+ RailsIOC::Dependencies.define do
136
+ inherit_environment(:production)
137
+
138
+ controller MyController, {
139
+ gateway: prototype(:payment_gateway, RealPaymentGateway, :use_test_url)
140
+ }
141
+ end
142
+ ```
143
+
144
+ config/dependencies/development.rb:
145
+
146
+ ```ruby
147
+ RailsIOC::Dependencies.define do
148
+ inherit_environment(:production)
149
+
150
+ controller MyController, {
151
+ gateway: singleton(BogusPaymentGateway),
152
+ credit_card_validator: singleton(BogusCardValidator)
153
+ }
154
+ end
155
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task :default => :spec
data/lib/rails-ioc.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "rails-ioc/version"
2
+ require "rails-ioc/errors"
3
+ require "rails-ioc/dependencies"
4
+ require "rails-ioc/dependency_constructor"
5
+ require "rails-ioc/dependency_injector"
6
+
7
+ raise RailsIOC::NoRailsError.new(:Rails) unless defined?(Rails)
8
+ raise RailsIOC::NoRailsError.new(:ActionController) unless defined?(ActionController)
9
+ require "rails-ioc/controller_extension"
10
+ ActionController::Base.send(:include, RailsIOC::ControllerExtension)
11
+
12
+ if defined?(RSpec)
13
+ require "rails-ioc/rspec_extension"
14
+ Object.send(:include, RailsIOC::RSpecExtension)
15
+ end
@@ -0,0 +1,13 @@
1
+ module RailsIOC
2
+ module ControllerExtension
3
+ def self.included(controller)
4
+ controller.before_filter :inject_dependencies
5
+ end
6
+
7
+ def inject_dependencies
8
+ RailsIOC::Dependencies.load!
9
+ dependencies = RailsIOC::Dependencies.controllers[self.class] || {}
10
+ RailsIOC::DependencyInjector.new(self).inject(dependencies)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,66 @@
1
+ module RailsIOC
2
+ class Dependencies
3
+ def self.reset!
4
+ @dependencies = {}
5
+ @controllers = {}
6
+ @loaded = false
7
+ end
8
+ self.reset!
9
+
10
+ def self.load!
11
+ self.reset! unless Rails.application.config.cache_classes
12
+ if !@loaded
13
+ inherit_environment(Rails.env)
14
+ @loaded = true
15
+ end
16
+ end
17
+
18
+ def self.define(&definition)
19
+ class_exec(&definition)
20
+ end
21
+
22
+ def self.inherit_environment(env)
23
+ load File.join(Rails.root, "config", "dependencies", "#{env}.rb")
24
+ end
25
+
26
+ def self.controller(klass, dependencies)
27
+ @controllers[klass] ||= {}
28
+ @controllers[klass].merge!(dependencies)
29
+ end
30
+
31
+ def self.singleton(*args)
32
+ store_dependency(args) do |klass, constructor_args|
33
+ if klass.instance_of?(Class)
34
+ DependencyConstructor.new(klass, caller).construct(constructor_args)
35
+ else
36
+ klass
37
+ end
38
+ end
39
+ end
40
+
41
+ def self.prototype(*args)
42
+ store_dependency(args) do |klass, constructor_args|
43
+ definition_backtrace = caller
44
+ -> { DependencyConstructor.new(klass, definition_backtrace).construct(constructor_args) }
45
+ end
46
+ end
47
+
48
+ def self.ref(name)
49
+ @dependencies[name] || raise(MissingReferenceError.new(name))
50
+ end
51
+
52
+ def self.controllers
53
+ @controllers
54
+ end
55
+
56
+ private
57
+
58
+ def self.store_dependency(args)
59
+ if args.first.is_a?(Symbol)
60
+ @dependencies[args.shift] = yield(args.shift, args)
61
+ else
62
+ yield(args.shift, args)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,17 @@
1
+ module RailsIOC
2
+ class DependencyConstructor
3
+ def initialize(klass, definition_backtrace)
4
+ @klass = klass
5
+ @definition_backtrace = definition_backtrace
6
+ end
7
+
8
+ def construct(dependencies)
9
+ begin
10
+ @klass.new(*dependencies.map { |x| x.is_a?(Proc) ? x.call : x })
11
+ rescue ArgumentError => e
12
+ e.set_backtrace(@definition_backtrace)
13
+ raise e
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module RailsIOC
2
+ class DependencyInjector
3
+ def initialize(target)
4
+ @target = target
5
+ end
6
+
7
+ def inject(dependencies)
8
+ dependencies.each do |field, value|
9
+ value = value.call if value.is_a?(Proc)
10
+ @target.instance_variable_set("@#{field}", value)
11
+ end
12
+ @target
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module RailsIOC
2
+ class BaseError < StandardError; end
3
+ class NoRailsError < BaseError
4
+ def initialize(missing_class)
5
+ super("#{missing_class} is not defined. RailsIOC can only be used within a Rails application.")
6
+ end
7
+ end
8
+
9
+ class MissingReferenceError < BaseError
10
+ def initialize(name)
11
+ super("No dependency defined with name :#{name}.")
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module RailsIOC
2
+ module RSpecExtension
3
+ def self.included(klass)
4
+ RSpec.configure do |config|
5
+ config.before(:each) { RailsIOC::Dependencies.reset! }
6
+ end
7
+ end
8
+
9
+ def controller_dependencies(dependencies)
10
+ klass = controller.class
11
+ Dependencies.define do
12
+ controller klass, dependencies
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module RailsIOC
2
+ VERSION = "0.1.0"
3
+ end
data/rails-ioc.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rails-ioc/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rails-ioc"
7
+ s.version = RailsIOC::VERSION
8
+ s.authors = ["Rick Grundy"]
9
+ s.email = ["rick@rickgrundy.com"]
10
+ s.homepage = "http://www.github.com/rickgrundy/rails-ioc"
11
+ s.summary = "Simple dependency injection for Rails."
12
+ s.description = "Simple dependency injection for Rails."
13
+
14
+ s.rubyforge_project = "rails-ioc"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ # specify any dependencies here; for example:
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
@@ -0,0 +1,5 @@
1
+ GlobalCounter.increment!
2
+
3
+ RailsIOC::Dependencies.define do
4
+ singleton :counter, GlobalCounter.count
5
+ end
@@ -0,0 +1,5 @@
1
+ RailsIOC::Dependencies.define do
2
+ singleton :env_string, "Production"
3
+ singleton :foo_string, "Foo"
4
+ singleton :production_only_string, "This comes from production.rb"
5
+ end
@@ -0,0 +1,5 @@
1
+ RailsIOC::Dependencies.define do
2
+ singleton :env_string, "Test"
3
+ singleton :foo_string, "Foo"
4
+ singleton :test_only_string, "This comes from test.rb"
5
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path("../../spec_helper.rb", __FILE__)
2
+
3
+ describe RailsIOC::ControllerExtension do
4
+ class ExtendedController
5
+ def self.before_filter(method_name)
6
+ @@before_filter_method = method_name
7
+ end
8
+
9
+ def trigger_before_filter!
10
+ self.send(@@before_filter_method)
11
+ end
12
+
13
+ include RailsIOC::ControllerExtension
14
+ end
15
+
16
+ it "injects pre-defined dependencies" do
17
+ Rails.application.config.cache_classes = true
18
+ RailsIOC::Dependencies.instance_variable_set :@loaded, true
19
+
20
+ RailsIOC::Dependencies.define do
21
+ controller ExtendedController, {
22
+ a_number: 123,
23
+ a_string: "Hello"
24
+ }
25
+ end
26
+
27
+ controller = ExtendedController.new
28
+ controller.trigger_before_filter!
29
+ controller.instance_variable_get(:@a_number).should == 123
30
+ controller.instance_variable_get(:@a_string).should == "Hello"
31
+ end
32
+ end
@@ -0,0 +1,163 @@
1
+ require File.expand_path("../../spec_helper.rb", __FILE__)
2
+
3
+ describe RailsIOC::Dependencies do
4
+ class TestListNode
5
+ attr_reader :next_node
6
+ def initialize(next_node=nil)
7
+ @next_node = next_node
8
+ end
9
+ end
10
+
11
+ class TestController
12
+ attr_reader :foo, :bar
13
+ end
14
+
15
+ describe "singletons" do
16
+ it "creates and references singletons from a class and constructor args" do
17
+ RailsIOC::Dependencies.define do
18
+ singleton :second_node, TestListNode
19
+ singleton :first_node, TestListNode, ref(:second_node)
20
+ end
21
+
22
+ first_node = RailsIOC::Dependencies.ref(:first_node)
23
+ first_node.should be_a TestListNode
24
+ first_node.should === RailsIOC::Dependencies.ref(:first_node)
25
+ first_node.next_node.should === RailsIOC::Dependencies.ref(:second_node)
26
+ end
27
+
28
+ it "creates unreferenced singletons from a class and constructor args" do
29
+ RailsIOC::Dependencies.define do
30
+ singleton :first_node, TestListNode, singleton(TestListNode)
31
+ end
32
+
33
+ first_node = RailsIOC::Dependencies.ref(:first_node)
34
+ first_node.should be_a(TestListNode)
35
+ first_node.next_node.should be_a TestListNode
36
+ end
37
+
38
+ it "does not require a class for simple types" do
39
+ RailsIOC::Dependencies.define do
40
+ singleton :a_number, 12345
41
+ end
42
+
43
+ RailsIOC::Dependencies.ref(:a_number).should == 12345
44
+ end
45
+
46
+ it "raises an error containing information about original definition if the wrong number of constructor args are defined" do
47
+ defining_line = __LINE__ + 3
48
+ -> {
49
+ RailsIOC::Dependencies.define do
50
+ singleton :a_string, String, "Too", "many", "args"
51
+ end
52
+ }.should raise_error(ArgumentError) { |e|
53
+ e.backtrace.find { |l| l =~ /dependencies_spec.rb:#{defining_line}/ }.should_not be_nil
54
+ }
55
+ end
56
+ end
57
+
58
+ describe "prototypes" do
59
+ it "creates and references prototypes from a class and constructor args" do
60
+ RailsIOC::Dependencies.define do
61
+ prototype :second_node, TestListNode
62
+ prototype :first_node, TestListNode, ref(:second_node)
63
+ end
64
+
65
+ ref = RailsIOC::Dependencies.ref(:first_node)
66
+ ref.should be_a Proc
67
+ first_node = ref.call
68
+ first_node.should be_a TestListNode
69
+ first_node.should_not === ref.call
70
+
71
+ first_node.next_node.should be_a TestListNode
72
+ first_node.next_node.should_not === RailsIOC::Dependencies.ref(:second_node).call
73
+ end
74
+
75
+ it "creates unreferenced prototypes from a class and constructor args" do
76
+ RailsIOC::Dependencies.define do
77
+ prototype :first_node, TestListNode, prototype(TestListNode)
78
+ end
79
+
80
+ ref = RailsIOC::Dependencies.ref(:first_node)
81
+ ref.should be_a Proc
82
+ first_node = ref.call
83
+ first_node.should be_a(TestListNode)
84
+ first_node.next_node.should be_a TestListNode
85
+ end
86
+
87
+ it "raises an error containing information about original definition if the wrong number of constructor args are defined" do
88
+ defining_line = __LINE__ + 2
89
+ RailsIOC::Dependencies.define do
90
+ prototype :a_string, String, "Too", "many", "args"
91
+ end
92
+
93
+ -> { RailsIOC::Dependencies.ref(:a_string).call }.should raise_error(ArgumentError) { |e|
94
+ e.backtrace.find { |l| l =~ /dependencies_spec.rb:#{defining_line}/ }.should_not be_nil
95
+ }
96
+ end
97
+ end
98
+
99
+ describe "controllers" do
100
+ it "stores controller dependencies for later injection by Rails' before_filter" do
101
+ RailsIOC::Dependencies.define do
102
+ prototype :second_node, TestListNode
103
+ singleton :first_node, TestListNode, ref(:second_node)
104
+
105
+ controller TestController, {
106
+ foo: ref(:first_node),
107
+ bar: singleton("The Sheep Says Bar")
108
+ }
109
+ end
110
+
111
+ controller = TestController.new
112
+ dependencies = RailsIOC::Dependencies.controllers[TestController]
113
+ RailsIOC::DependencyInjector.new(controller).inject(dependencies)
114
+
115
+ controller.foo.should === RailsIOC::Dependencies.ref(:first_node)
116
+ controller.bar.should == "The Sheep Says Bar"
117
+ end
118
+ end
119
+
120
+ describe "loading dependencies file" do
121
+ before(:each) do
122
+ GlobalCounter.zero!
123
+ end
124
+
125
+ it "reloads the file if cache_classes is false" do
126
+ Rails.env = "incrementing_test"
127
+ Rails.application.config.cache_classes = false
128
+
129
+ RailsIOC::Dependencies.load!
130
+ RailsIOC::Dependencies.ref(:counter).should == 1
131
+
132
+ RailsIOC::Dependencies.load!
133
+ RailsIOC::Dependencies.ref(:counter).should == 2
134
+ end
135
+
136
+ it "does not reload the file if cache_classes is true" do
137
+ Rails.env = "incrementing_test"
138
+ Rails.application.config.cache_classes = true
139
+
140
+ RailsIOC::Dependencies.load!
141
+ RailsIOC::Dependencies.ref(:counter).should == 1
142
+
143
+ RailsIOC::Dependencies.load!
144
+ RailsIOC::Dependencies.ref(:counter).should == 1
145
+ end
146
+ end
147
+
148
+ describe "inheriting environments" do
149
+ it "loads the corresponding dependencies file" do
150
+ RailsIOC::Dependencies.inherit_environment("production")
151
+ RailsIOC::Dependencies.ref(:env_string).should == "Production"
152
+ RailsIOC::Dependencies.ref(:foo_string).should == "Foo"
153
+ RailsIOC::Dependencies.ref(:production_only_string).should == "This comes from production.rb"
154
+ -> { RailsIOC::Dependencies.ref(:test_only_string) }.should raise_error RailsIOC::MissingReferenceError
155
+
156
+ RailsIOC::Dependencies.inherit_environment("test")
157
+ RailsIOC::Dependencies.ref(:env_string).should == "Test"
158
+ RailsIOC::Dependencies.ref(:foo_string).should == "Foo"
159
+ RailsIOC::Dependencies.ref(:production_only_string).should == "This comes from production.rb"
160
+ RailsIOC::Dependencies.ref(:test_only_string).should == "This comes from test.rb"
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,35 @@
1
+ require File.expand_path("../../spec_helper.rb", __FILE__)
2
+
3
+ describe RailsIOC::DependencyConstructor do
4
+ class ConstructorTestClass
5
+ attr_reader :foo, :bar
6
+ def initialize(foo, bar)
7
+ @foo, @bar = foo, bar
8
+ end
9
+ end
10
+
11
+ it "constructs the given class" do
12
+ instance = RailsIOC::DependencyConstructor.new(ConstructorTestClass, []).construct([123, :abc])
13
+ instance.foo.should == 123
14
+ instance.bar.should == :abc
15
+ end
16
+
17
+ it "calls any procs which are provided in constructor args" do
18
+ returns_7 = -> { 3 + 4 }
19
+ instance = RailsIOC::DependencyConstructor.new(ConstructorTestClass, []).construct([returns_7, :abc])
20
+ instance.foo.should == 7
21
+ instance.bar.should == :abc
22
+ end
23
+
24
+ it "sets the provided backtrace in the event of an ArgumentError" do
25
+ constructor = RailsIOC::DependencyConstructor.new(ConstructorTestClass, ["my_backtrace"])
26
+
27
+ -> { constructor.construct([]) }.should raise_error(ArgumentError) do |e|
28
+ e.backtrace.should == ["my_backtrace"]
29
+ end
30
+
31
+ -> { constructor.construct(nil) }.should raise_error do |e|
32
+ e.backtrace.should_not == ["my_backtrace"]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path("../../spec_helper.rb", __FILE__)
2
+
3
+ describe RailsIOC::DependencyInjector do
4
+ class InjectorTestClass
5
+ attr_reader :foo, :bar
6
+ end
7
+
8
+ it "injects named variables for the given class" do
9
+ instance = RailsIOC::DependencyInjector.new(InjectorTestClass.new).inject(foo: 123, bar: :abc)
10
+ instance.foo.should == 123
11
+ instance.bar.should == :abc
12
+ end
13
+
14
+ it "calls any procs which are provided in constructor args" do
15
+ returns_7 = -> { 3 + 4 }
16
+ instance = RailsIOC::DependencyInjector.new(InjectorTestClass.new).inject(foo: returns_7, bar: :abc)
17
+ instance.foo.should == 7
18
+ instance.bar.should == :abc
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path("../../spec_helper.rb", __FILE__)
2
+
3
+ describe RailsIOC::RSpecExtension do
4
+ class MyController; end
5
+ def controller; MyController.new; end
6
+
7
+ describe "resetting dependencies between tests" do
8
+ after(:each) do
9
+ RailsIOC::Dependencies.define { singleton :defined?, true }
10
+ end
11
+
12
+ it "resets before this test" do
13
+ -> { RailsIOC::Dependencies.ref(:defined?) }.should raise_error RailsIOC::MissingReferenceError
14
+ end
15
+
16
+ it "also resets before this test" do
17
+ -> { RailsIOC::Dependencies.ref(:defined?) }.should raise_error RailsIOC::MissingReferenceError
18
+ end
19
+ end
20
+
21
+ it "provides a shorthand way to define controller dependencies" do
22
+ controller_dependencies(who_am_i: "The Walrus")
23
+ RailsIOC::Dependencies.controllers[MyController][:who_am_i].should == "The Walrus"
24
+ end
25
+ end
@@ -0,0 +1,62 @@
1
+ def ignoring_warnings
2
+ verbosity = $VERBOSE
3
+ $VERBOSE = nil
4
+ yield
5
+ ensure
6
+ $VERBOSE = verbosity
7
+ end
8
+
9
+ class GlobalCounter
10
+ def self.zero!
11
+ @count = 0
12
+ end
13
+
14
+ def self.increment!
15
+ @count += 1
16
+ end
17
+
18
+ def self.count
19
+ @count
20
+ end
21
+ end
22
+
23
+ class AdHocObject
24
+ def initialize
25
+ @store = {}
26
+ end
27
+
28
+ def method_missing(name, *args)
29
+ if name =~ /=$/
30
+ @store[name.to_s.sub(/=|\s/, "").to_sym] = args.first
31
+ else
32
+ @store[name]
33
+ end
34
+ end
35
+ end
36
+
37
+ module FakeActionController
38
+ class Base
39
+ def self.before_filter(method_name); end
40
+ end
41
+ end
42
+
43
+ def reset_fake_dependencies!
44
+ ignoring_warnings do
45
+ Object.send(:const_set, :Rails, AdHocObject.new)
46
+ Rails.application = AdHocObject.new
47
+ Rails.application.config = AdHocObject.new
48
+ Rails.root = File.expand_path("../fixtures", __FILE__)
49
+
50
+ Object.send(:const_set, :ActionController, FakeActionController)
51
+ end
52
+ end
53
+
54
+ RSpec.configure do |config|
55
+ config.before(:each) do
56
+ GlobalCounter.zero!
57
+ reset_fake_dependencies!
58
+ end
59
+ end
60
+
61
+ reset_fake_dependencies!
62
+ require File.expand_path("../../lib/rails-ioc.rb", __FILE__)
@@ -0,0 +1,46 @@
1
+ require File.expand_path("../spec_helper.rb", __FILE__)
2
+
3
+ describe "Spec Helper" do
4
+ it "provides a global counter" do
5
+ 3.times { GlobalCounter.increment! }
6
+ GlobalCounter.count.should == 3
7
+ GlobalCounter.zero!
8
+ GlobalCounter.count.should == 0
9
+ end
10
+
11
+ describe "zeroing the global counter between tests" do
12
+ after(:each) { GlobalCounter.increment! }
13
+
14
+ it "zeroes before this test" do
15
+ GlobalCounter.count.should == 0
16
+ end
17
+
18
+ it "also zeroes before this test" do
19
+ GlobalCounter.count.should == 0
20
+ end
21
+ end
22
+
23
+ it "simulates Rails.application.config to allow testing outside a Rails app" do
24
+ Rails.application.config.kittens = "Small Cats"
25
+ Rails.application.config.kittens.should == "Small Cats"
26
+ end
27
+
28
+ describe "resetting fake Rails environment between tests" do
29
+ after(:each) do
30
+ ignoring_warnings do
31
+ Rails = nil
32
+ ActionController = nil
33
+ end
34
+ end
35
+
36
+ it "resets Rails before this test" do
37
+ ActionController.should_not be_nil
38
+ Rails.application.should_not be_nil
39
+ end
40
+
41
+ it "resets Rails before this test too" do
42
+ ActionController.should_not be_nil
43
+ Rails.application.should_not be_nil
44
+ end
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-ioc
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Rick Grundy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-08-29 00:00:00 +10:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ type: :development
26
+ version_requirements: *id001
27
+ description: Simple dependency injection for Rails.
28
+ email:
29
+ - rick@rickgrundy.com
30
+ executables: []
31
+
32
+ extensions: []
33
+
34
+ extra_rdoc_files: []
35
+
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - README.markdown
40
+ - Rakefile
41
+ - lib/rails-ioc.rb
42
+ - lib/rails-ioc/controller_extension.rb
43
+ - lib/rails-ioc/dependencies.rb
44
+ - lib/rails-ioc/dependency_constructor.rb
45
+ - lib/rails-ioc/dependency_injector.rb
46
+ - lib/rails-ioc/errors.rb
47
+ - lib/rails-ioc/rspec_extension.rb
48
+ - lib/rails-ioc/version.rb
49
+ - rails-ioc.gemspec
50
+ - spec/fixtures/config/dependencies/incrementing_test.rb
51
+ - spec/fixtures/config/dependencies/production.rb
52
+ - spec/fixtures/config/dependencies/test.rb
53
+ - spec/rails-ioc/controller_extension_spec.rb
54
+ - spec/rails-ioc/dependencies_spec.rb
55
+ - spec/rails-ioc/dependency_constructor_spec.rb
56
+ - spec/rails-ioc/dependency_injector_spec.rb
57
+ - spec/rails-ioc/rspec_extension_spec.rb
58
+ - spec/spec_helper.rb
59
+ - spec/spec_helper_spec_spec_spec_spec.rb
60
+ has_rdoc: true
61
+ homepage: http://www.github.com/rickgrundy/rails-ioc
62
+ licenses: []
63
+
64
+ post_install_message:
65
+ rdoc_options: []
66
+
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project: rails-ioc
84
+ rubygems_version: 1.5.0
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Simple dependency injection for Rails.
88
+ test_files:
89
+ - spec/fixtures/config/dependencies/incrementing_test.rb
90
+ - spec/fixtures/config/dependencies/production.rb
91
+ - spec/fixtures/config/dependencies/test.rb
92
+ - spec/rails-ioc/controller_extension_spec.rb
93
+ - spec/rails-ioc/dependencies_spec.rb
94
+ - spec/rails-ioc/dependency_constructor_spec.rb
95
+ - spec/rails-ioc/dependency_injector_spec.rb
96
+ - spec/rails-ioc/rspec_extension_spec.rb
97
+ - spec/spec_helper.rb
98
+ - spec/spec_helper_spec_spec_spec_spec.rb