demeters_revenge 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2010-03-09
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,18 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ demeters_revenge.gemspec
6
+ examples/example_spec_helper.rb
7
+ examples/has_and_belongs_to_many_examples.rb
8
+ examples/has_many_examples.rb
9
+ lib/demeters_revenge.rb
10
+ lib/demeters_revenge/has_and_belongs_to_many_extensions.rb
11
+ lib/demeters_revenge/has_many_extensions.rb
12
+ script/console
13
+ script/destroy
14
+ script/generate
15
+ spec/has_and_belongs_to_many_spec.rb
16
+ spec/has_many_extensions_spec.rb
17
+ spec/spec_autotest.rb
18
+ spec/spec_helper.rb
@@ -0,0 +1,48 @@
1
+ = demeters_revenge
2
+
3
+ * http://github.com/elabs/demeters_revenge
4
+
5
+ == DESCRIPTION:
6
+
7
+ Demeter is back with a vengeance. Prevent your models from violating his sacred law and you shall be spared.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ Adds methods when declaring has_many and has_and_belongs_to_many.
12
+
13
+ == SYNOPSIS:
14
+
15
+ FIX (code sample of usage)
16
+
17
+ == REQUIREMENTS:
18
+
19
+ * FIX (list of requirements)
20
+
21
+ == INSTALL:
22
+
23
+ gem install demeters_revenge
24
+
25
+ == LICENSE:
26
+
27
+ (The MIT License)
28
+
29
+ Copyright (c) 2007 Luke Redpath
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/demeters_revenge'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'demeters_revenge' do
14
+ self.developer 'Luke Redpath', 'wearenotlukeredpath@elabs.se'
15
+ self.rubyforge_name = self.name # TODO this is default value
16
+ end
17
+
18
+ require 'newgem/tasks'
19
+ Dir['tasks/**/*.rake'].each { |t| load t }
20
+
21
+ # TODO - want other tests/tasks run by default? Add them to the list
22
+ # remove_task :default
23
+ # task :default => [:spec, :features]
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{demeters_revenge}
5
+ s.version = "0.2"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Luke Redpath"]
9
+ s.date = %q{2010-03-09}
10
+ s.description = %q{Demeter is back with a vengeance. Prevent your models from violating his sacred law and you shall be spared.}
11
+ s.email = ["wearenotlukeredpath@elabs.se"]
12
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
13
+ s.files = ["History.txt", "Manifest.txt", "README.rdoc", "Rakefile", "demeters_revenge.gemspec", "examples/example_spec_helper.rb", "examples/has_and_belongs_to_many_examples.rb", "examples/has_many_examples.rb", "lib/demeters_revenge.rb", "lib/demeters_revenge/has_and_belongs_to_many_extensions.rb", "lib/demeters_revenge/has_many_extensions.rb", "script/console", "script/destroy", "script/generate", "spec/has_and_belongs_to_many_spec.rb", "spec/has_many_extensions_spec.rb", "spec/spec_autotest.rb", "spec/spec_helper.rb"]
14
+ s.homepage = %q{http://github.com/elabs/demeters_revenge}
15
+ s.rdoc_options = ["--main", "README.rdoc"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{demeters_revenge}
18
+ s.rubygems_version = %q{1.3.6}
19
+ s.summary = %q{Demeter is back with a vengeance}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
+ s.add_development_dependency(%q<rubyforge>, [">= 2.0.3"])
27
+ s.add_development_dependency(%q<gemcutter>, [">= 0.3.0"])
28
+ s.add_development_dependency(%q<hoe>, [">= 2.5.0"])
29
+ else
30
+ s.add_dependency(%q<rubyforge>, [">= 2.0.3"])
31
+ s.add_dependency(%q<gemcutter>, [">= 0.3.0"])
32
+ s.add_dependency(%q<hoe>, [">= 2.5.0"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<rubyforge>, [">= 2.0.3"])
36
+ s.add_dependency(%q<gemcutter>, [">= 0.3.0"])
37
+ s.add_dependency(%q<hoe>, [">= 2.5.0"])
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'active_record'
4
+ require 'fileutils'
5
+
6
+ require File.join(File.dirname(__FILE__), *%w[../lib/demeters_revenge])
7
+
8
+ module Kernel
9
+ def with_silent_output(&block)
10
+ $stdout = StringIO.new
11
+ yield
12
+ $stdout = STDOUT
13
+ end
14
+ end
15
+
16
+ module ExampleSpecHelper
17
+ def self.create_example_database(&schema_block)
18
+ ActiveRecord::Base.establish_connection(
19
+ :adapter => 'sqlite3',
20
+ :database => 'examples.db'
21
+ )
22
+ with_silent_output do
23
+ ActiveRecord::Schema.define(&schema_block) if block_given?
24
+ end
25
+ end
26
+
27
+ def self.destroy_example_database
28
+ FileUtils.rm('examples.db')
29
+ end
30
+ end
@@ -0,0 +1,81 @@
1
+ require File.join(File.dirname(__FILE__), *%w[example_spec_helper])
2
+
3
+ describe "Person that has_and_belongs_to_many Widgets" do
4
+
5
+ before(:all) do
6
+ ExampleSpecHelper.create_example_database do
7
+ create_table :people, :force => true do |t|
8
+ t.string :name
9
+ end
10
+
11
+ create_table :widgets, :force => true do |t|
12
+ t.string :name
13
+ end
14
+
15
+ create_table :people_widgets, :force => true do |t|
16
+ t.integer :person_id
17
+ t.integer :widget_id
18
+ end
19
+ end
20
+
21
+ Person = Class.new(ActiveRecord::Base)
22
+ Widget = Class.new(ActiveRecord::Base)
23
+
24
+ Person.send(:include, DemetersRevenge::HasAndBelongsToManyExtensions)
25
+ end
26
+
27
+ after(:all) do
28
+ ExampleSpecHelper.destroy_example_database
29
+ Object.send(:remove_const, :Person)
30
+ Object.send(:remove_const, :Widget)
31
+ end
32
+
33
+ before(:each) do
34
+ Person.send(:has_and_belongs_to_many, :widgets)
35
+ @person = Person.create
36
+ end
37
+
38
+ after(:each) do
39
+ Person.delete_all
40
+ Widget.delete_all
41
+ end
42
+
43
+ it "should be able to build widgets and report how many widgets it has" do
44
+ @person.build_widget(:name => 'Widget One')
45
+ @person.build_widget(:name => 'Widget Two')
46
+ @person.number_of_widgets.should == 2
47
+ end
48
+
49
+ it "should be able to create widgets in the database and return a count" do
50
+ @person.create_widget(:name => 'Widget One')
51
+ @person.create_widget(:name => 'Widget Two')
52
+ @person.widget_count.should == 2
53
+ end
54
+
55
+ it "should clear all widgets from the collection" do
56
+ @person.build_widget(:name => 'Widget One')
57
+ @person.clear_widgets
58
+ @person.number_of_widgets.should == 0
59
+ end
60
+
61
+ it "should have no widgets by default" do
62
+ @person.should have_no_widgets
63
+ end
64
+
65
+ it "should have widgets once at least on has been added" do
66
+ @person.create_widget(:name => 'Widget One')
67
+ @person.should have_widgets
68
+ end
69
+
70
+ it "should be able to delete created widgets" do
71
+ widget = @person.create_widget
72
+ @person.delete_widget(widget)
73
+ @person.widget_count.should == 0
74
+ end
75
+
76
+ it "should be able to find an existing widget" do
77
+ widget = @person.create_widget
78
+ @person.find_widgets(:first).should == widget
79
+ end
80
+
81
+ end
@@ -0,0 +1,77 @@
1
+ require File.join(File.dirname(__FILE__), *%w[example_spec_helper])
2
+
3
+ describe "Person that has_many Widgets" do
4
+
5
+ before(:all) do
6
+ ExampleSpecHelper.create_example_database do
7
+ create_table :people, :force => true do |t|
8
+ t.string :name
9
+ end
10
+
11
+ create_table :widgets, :force => true do |t|
12
+ t.integer :person_id
13
+ t.string :name
14
+ end
15
+ end
16
+
17
+ Person = Class.new(ActiveRecord::Base)
18
+ Widget = Class.new(ActiveRecord::Base)
19
+
20
+ Person.send(:include, DemetersRevenge::HasManyExtensions)
21
+ end
22
+
23
+ after(:all) do
24
+ ExampleSpecHelper.destroy_example_database
25
+ Object.send(:remove_const, :Person)
26
+ Object.send(:remove_const, :Widget)
27
+ end
28
+
29
+ before(:each) do
30
+ Person.send(:has_many, :widgets)
31
+ @person = Person.create
32
+ end
33
+
34
+ after(:each) do
35
+ Person.delete_all
36
+ Widget.delete_all
37
+ end
38
+
39
+ it "should be able to build widgets and report how many widgets it has" do
40
+ @person.build_widget(:name => 'Widget One')
41
+ @person.build_widget(:name => 'Widget Two')
42
+ @person.number_of_widgets.should == 2
43
+ end
44
+
45
+ it "should be able to create widgets in the database and return a count" do
46
+ @person.create_widget(:name => 'Widget One')
47
+ @person.create_widget(:name => 'Widget Two')
48
+ @person.widget_count.should == 2
49
+ end
50
+
51
+ it "should clear all widgets from the collection" do
52
+ @person.build_widget(:name => 'Widget One')
53
+ @person.clear_widgets
54
+ @person.number_of_widgets.should == 0
55
+ end
56
+
57
+ it "should have no widgets by default" do
58
+ @person.should have_no_widgets
59
+ end
60
+
61
+ it "should have widgets once at least on has been added" do
62
+ @person.create_widget(:name => 'Widget One')
63
+ @person.should have_widgets
64
+ end
65
+
66
+ it "should be able to delete created widgets" do
67
+ widget = @person.create_widget
68
+ @person.delete_widget(widget)
69
+ @person.widget_count.should == 0
70
+ end
71
+
72
+ it "should be able to find an existing widget" do
73
+ widget = @person.create_widget
74
+ @person.find_widgets(:first).should == widget
75
+ end
76
+
77
+ end
@@ -0,0 +1,18 @@
1
+ unless defined?(RAILS_ROOT)
2
+ require 'rubygems'
3
+ require 'active_support'
4
+ end
5
+
6
+ module DemetersRevenge
7
+ VERSION = "0.2"
8
+ end
9
+
10
+ Dir[File.join(File.dirname(__FILE__), "demeters_revenge/**/*.rb")].each do |lib|
11
+ require lib
12
+ end
13
+
14
+ if defined?(ActiveRecord::Base)
15
+ class ActiveRecord::Base
16
+ include DemetersRevenge::HasManyExtensions
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module DemetersRevenge
2
+ module HasAndBelongsToManyExtensions
3
+
4
+ def self.included(base)
5
+ base.instance_eval do
6
+ class << self
7
+
8
+ def has_and_belongs_to_many_with_transmogrifiers(name, *args)
9
+ has_and_belongs_to_many_without_transmogrifiers(name, *args)
10
+ HasManyExtensions.inject_transmogrifiers(self, name)
11
+ end
12
+
13
+ alias_method_chain(:has_and_belongs_to_many, :transmogrifiers)
14
+
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,63 @@
1
+ module DemetersRevenge
2
+ module HasManyExtensions
3
+
4
+ def self.included(base)
5
+ base.instance_eval do
6
+ class << self
7
+
8
+ def has_many_with_transmogrifiers(name, *args)
9
+ has_many_without_transmogrifiers(name, *args)
10
+ HasManyExtensions.inject_transmogrifiers(self, name)
11
+ end
12
+
13
+ alias_method_chain(:has_many, :transmogrifiers)
14
+
15
+ end
16
+ end
17
+ end
18
+
19
+ def self.inject_transmogrifiers(klass, association_name)
20
+ plural_association_name = association_name.to_s
21
+ singular_association_name = plural_association_name.singularize
22
+
23
+ klass.instance_eval do
24
+
25
+ define_method("build_#{singular_association_name}") do |*args|
26
+ send(plural_association_name).build(*args)
27
+ end
28
+
29
+ define_method("create_#{singular_association_name}") do |*args|
30
+ send(plural_association_name).create(*args)
31
+ end
32
+
33
+ define_method("delete_#{singular_association_name}") do |object|
34
+ send(plural_association_name).delete(object)
35
+ end
36
+
37
+ define_method("clear_#{plural_association_name}") do
38
+ send(plural_association_name).clear
39
+ end
40
+
41
+ define_method("number_of_#{plural_association_name}") do
42
+ send(plural_association_name).length
43
+ end
44
+
45
+ define_method("#{singular_association_name}_count") do |*args|
46
+ send(plural_association_name).count(*args)
47
+ end
48
+
49
+ define_method("has_#{plural_association_name}?") do
50
+ send(plural_association_name).any?
51
+ end
52
+
53
+ define_method("has_no_#{plural_association_name}?") do
54
+ send(plural_association_name).empty?
55
+ end
56
+
57
+ define_method("find_#{plural_association_name}") do |*args|
58
+ send(plural_association_name).find(*args)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/demeters_revenge.rb'}"
9
+ puts "Loading demeters_revenge gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,128 @@
1
+ require File.join(File.dirname(__FILE__), *%w[spec_helper])
2
+
3
+ describe "ActiveRecord class with has_and_belongs_to_many extension mixed in" do
4
+
5
+ before do
6
+ @klass = ActiveRecordStub.dup
7
+ @klass.send(:include, DemetersRevenge::HasAndBelongsToManyExtensions)
8
+ end
9
+
10
+ it "should delegate to original has_and_belongs_to_many then inject has_many transmogrifiers when calling has_and_belongs_to_many" do
11
+ @klass.expects(:has_and_belongs_to_many_without_transmogrifiers).with(:widgets)
12
+ DemetersRevenge::HasManyExtensions.expects(:inject_transmogrifiers).with(@klass, :widgets)
13
+ @klass.send(:has_and_belongs_to_many, :widgets)
14
+ end
15
+
16
+ end
17
+
18
+ describe "ActiveRecord object after transmogrifiers injected for has_and_belongs_to_many :widgets" do
19
+
20
+ before do
21
+ @klass = ActiveRecordStub.dup
22
+ DemetersRevenge::HasManyExtensions.inject_transmogrifiers(@klass, :widgets)
23
+ @object = @klass.new
24
+ end
25
+
26
+ it "should respond to build_widget" do
27
+ @object.should respond_to(:build_widget)
28
+ end
29
+
30
+ it "should respond to create_widget" do
31
+ @object.should respond_to(:create_widget)
32
+ end
33
+
34
+ it "should respond to delete_widget" do
35
+ @object.should respond_to(:delete_widget)
36
+ end
37
+
38
+ it "should respond to clear_widgets" do
39
+ @object.should respond_to(:clear_widgets)
40
+ end
41
+
42
+ it "should respond to number_of_widgets" do
43
+ @object.should respond_to(:number_of_widgets)
44
+ end
45
+
46
+ it "should respond to widget_count" do
47
+ @object.should respond_to(:widget_count)
48
+ end
49
+
50
+ it "should respond to has_widgets?" do
51
+ @object.should respond_to(:has_widgets?)
52
+ end
53
+
54
+ it "should respond to has_no_widgets?" do
55
+ @object.should respond_to(:has_no_widgets?)
56
+ end
57
+
58
+ it "should respond to find_widgets" do
59
+ @object.should respond_to(:find_widgets)
60
+ end
61
+
62
+ end
63
+
64
+ describe "ActiveRecord object with HasAndBelongsToManyExtensions mixed in that has_and_belongs_to_many :widgets" do
65
+
66
+ before(:all) do
67
+ @klass = ActiveRecordStub.dup
68
+ @klass.send(:include, DemetersRevenge::HasManyExtensions)
69
+ end
70
+
71
+ before do
72
+ @klass.send(:has_many, :widgets)
73
+
74
+ @object = @klass.new
75
+ @association_proxy = mock('widgets association proxy')
76
+ @object.stubs(:widgets).returns(@association_proxy)
77
+ end
78
+
79
+ it "should call build on association proxy with arguments when calling build_widget" do
80
+ @association_proxy.expects(:build).with('arg1', 'arg2', 'arg3')
81
+ @object.build_widget('arg1', 'arg2', 'arg3')
82
+ end
83
+
84
+ it "should call create on association proxy with arguments when calling create_widget" do
85
+ @association_proxy.expects(:create).with('arg1', 'arg2', 'arg3')
86
+ @object.create_widget('arg1', 'arg2', 'arg3')
87
+ end
88
+
89
+ it "should return the count from the association proxy when calling widget_count" do
90
+ @association_proxy.expects(:count).returns(3)
91
+ @object.widget_count.should == 3
92
+ end
93
+
94
+ it "should pass arguments to association proxy count method when calling widget_count with args" do
95
+ @association_proxy.expects(:count).with(:conditions => 'foo')
96
+ @object.widget_count(:conditions => 'foo')
97
+ end
98
+
99
+ it "should return the number of loaded widgets in the collection when calling number_of_widgets" do
100
+ @association_proxy.stubs(:length).returns(10)
101
+ @object.number_of_widgets.should == 10
102
+ end
103
+
104
+ it "should call clear on association proxy when calling clear_widgets" do
105
+ @association_proxy.expects(:clear)
106
+ @object.clear_widgets
107
+ end
108
+
109
+ it "should call delete on association proxy with given object when calling delete_widget" do
110
+ @association_proxy.expects(:delete).with(another_object = stub)
111
+ @object.delete_widget(another_object)
112
+ end
113
+
114
+ it "should return true for has_widgets? if association_proxy has any objects in its collection" do
115
+ @association_proxy.stubs(:any?).returns(true)
116
+ @object.has_widgets?.should be_true
117
+ end
118
+
119
+ it "should return true for has_no_widgets? if association_proxy has no objects in its collection" do
120
+ @association_proxy.stubs(:empty?).returns(true)
121
+ @object.has_no_widgets?.should be_true
122
+ end
123
+
124
+ it "should call find on association proxy with all arguments when calling find_widgets" do
125
+ @association_proxy.expects(:find).with('arg1', 'arg2', 'arg3')
126
+ @object.find_widgets('arg1', 'arg2', 'arg3')
127
+ end
128
+ end
@@ -0,0 +1,127 @@
1
+ require File.join(File.dirname(__FILE__), *%w[spec_helper])
2
+
3
+ describe "ActiveRecord class with has_many extension mixed in" do
4
+
5
+ before(:each) do
6
+ @klass = ActiveRecordStub.dup
7
+ @klass.send(:include, DemetersRevenge::HasManyExtensions)
8
+ end
9
+
10
+ it "should delegate to original has_many then inject transmogrifiers when calling has_many" do
11
+ @klass.expects(:has_many_without_transmogrifiers).with(:widgets)
12
+ DemetersRevenge::HasManyExtensions.expects(:inject_transmogrifiers).with(@klass, :widgets)
13
+ @klass.send(:has_many, :widgets)
14
+ end
15
+ end
16
+
17
+ describe "ActiveRecord object after transmogrifiers injected for has_many :widgets" do
18
+
19
+ before do
20
+ @klass = ActiveRecordStub.dup
21
+ DemetersRevenge::HasManyExtensions.inject_transmogrifiers(@klass, :widgets)
22
+ @object = @klass.new
23
+ end
24
+
25
+ it "should respond to build_widget" do
26
+ @object.should respond_to(:build_widget)
27
+ end
28
+
29
+ it "should respond to create_widget" do
30
+ @object.should respond_to(:create_widget)
31
+ end
32
+
33
+ it "should respond to delete_widget" do
34
+ @object.should respond_to(:delete_widget)
35
+ end
36
+
37
+ it "should respond to clear_widgets" do
38
+ @object.should respond_to(:clear_widgets)
39
+ end
40
+
41
+ it "should respond to number_of_widgets" do
42
+ @object.should respond_to(:number_of_widgets)
43
+ end
44
+
45
+ it "should respond to widget_count" do
46
+ @object.should respond_to(:widget_count)
47
+ end
48
+
49
+ it "should respond to has_widgets?" do
50
+ @object.should respond_to(:has_widgets?)
51
+ end
52
+
53
+ it "should respond to has_no_widgets?" do
54
+ @object.should respond_to(:has_no_widgets?)
55
+ end
56
+
57
+ it "should respond to find_widgets" do
58
+ @object.should respond_to(:find_widgets)
59
+ end
60
+
61
+ end
62
+
63
+ describe "ActiveRecord object with HasManyExtensions mixed in that has_many :widgets" do
64
+
65
+ before(:all) do
66
+ @klass = ActiveRecordStub.dup
67
+ @klass.send(:include, DemetersRevenge::HasManyExtensions)
68
+ end
69
+
70
+ before do
71
+ @klass.send(:has_many, :widgets)
72
+
73
+ @object = @klass.new
74
+ @association_proxy = mock('widgets association proxy')
75
+ @object.stubs(:widgets).returns(@association_proxy)
76
+ end
77
+
78
+ it "should call build on association proxy with arguments when calling build_widget" do
79
+ @association_proxy.expects(:build).with('arg1', 'arg2', 'arg3')
80
+ @object.build_widget('arg1', 'arg2', 'arg3')
81
+ end
82
+
83
+ it "should call create on association proxy with arguments when calling create_widget" do
84
+ @association_proxy.expects(:create).with('arg1', 'arg2', 'arg3')
85
+ @object.create_widget('arg1', 'arg2', 'arg3')
86
+ end
87
+
88
+ it "should return the count from the association proxy when calling widget_count" do
89
+ @association_proxy.expects(:count).returns(3)
90
+ @object.widget_count.should == 3
91
+ end
92
+
93
+ it "should pass arguments to association proxy count method when calling widget_count with args" do
94
+ @association_proxy.expects(:count).with(:conditions => 'foo')
95
+ @object.widget_count(:conditions => 'foo')
96
+ end
97
+
98
+ it "should return the number of loaded widgets in the collection when calling number_of_widgets" do
99
+ @association_proxy.stubs(:length).returns(10)
100
+ @object.number_of_widgets.should == 10
101
+ end
102
+
103
+ it "should call clear on association proxy when calling clear_widgets" do
104
+ @association_proxy.expects(:clear)
105
+ @object.clear_widgets
106
+ end
107
+
108
+ it "should call delete on association proxy with given object when calling delete_widget" do
109
+ @association_proxy.expects(:delete).with(another_object = stub)
110
+ @object.delete_widget(another_object)
111
+ end
112
+
113
+ it "should return true for has_widgets? if association_proxy has any objects in its collection" do
114
+ @association_proxy.stubs(:any?).returns(true)
115
+ @object.has_widgets?.should be_true
116
+ end
117
+
118
+ it "should return true for has_no_widgets? if association_proxy has no objects in its collection" do
119
+ @association_proxy.stubs(:empty?).returns(true)
120
+ @object.has_no_widgets?.should be_true
121
+ end
122
+
123
+ it "should call find on association proxy with all arguments when calling find_widgets" do
124
+ @association_proxy.expects(:find).with('arg1', 'arg2', 'arg3')
125
+ @object.find_widgets('arg1', 'arg2', 'arg3')
126
+ end
127
+ end
@@ -0,0 +1,149 @@
1
+ # (c) Copyright 2006-2007 Nick Sieger <nicksieger@gmail.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person
4
+ # obtaining a copy of this software and associated documentation files
5
+ # (the "Software"), to deal in the Software without restriction,
6
+ # including without limitation the rights to use, copy, modify, merge,
7
+ # publish, distribute, sublicense, and/or sell copies of the Software,
8
+ # and to permit persons to whom the Software is furnished to do so,
9
+ # subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18
+ # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19
+ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'rubygems'
24
+ gem 'ZenTest'
25
+ require 'autotest'
26
+
27
+ class RspecAutotest < Autotest
28
+ attr_accessor :spec_command
29
+ def initialize # :nodoc:
30
+ @spec_command = "spec --diff unified"
31
+ super
32
+ @exceptions = %r%^\./(?:coverage|doc)%
33
+ end
34
+
35
+ def tests_for_file(filename)
36
+ case filename
37
+ when /^lib\/.*\.rb$/ then
38
+ impl = File.basename(filename).gsub('_', '_?').sub(/\.rb$/, '')
39
+ @files.keys.select do |k|
40
+ k =~ %r%^spec/.*#{impl}_spec\.rb$%
41
+ end
42
+ when %r%^spec/spec_helper.rb% then
43
+ @files.keys.select do |f|
44
+ f =~ %r%^spec/.*_spec\.rb$%
45
+ end
46
+ when /^spec\/.*_spec\.rb$/ then
47
+ [filename]
48
+ when /#{Regexp.quote(File.basename(__FILE__))}/
49
+ # Don't respond to changes to this file
50
+ []
51
+ else
52
+ @output.puts "Dunno! #{filename}" if $TESTING
53
+ []
54
+ end
55
+ end
56
+
57
+ def handle_results(results)
58
+ failed = results.scan(/^\d+\)\n(?:\e\[\d*m)?(?:.*?Error in )?'([^\n]*)'(?: FAILED)?(?:\e\[\d*m)?\n(.*?)\n\n/m)
59
+ @files_to_test = consolidate_failures failed
60
+ unless @files_to_test.empty? then
61
+ hook :red
62
+ else
63
+ hook :green
64
+ end unless $TESTING
65
+ @tainted = true unless @files_to_test.empty?
66
+ end
67
+
68
+ def consolidate_failures(failed)
69
+ filters = Hash.new { |h,k| h[k] = [] }
70
+ failed.each do |spec, failed_trace|
71
+ @files.keys.select{|f| f =~ /spec\//}.each do |f|
72
+ if failed_trace =~ Regexp.new(f)
73
+ filters[f] << spec
74
+ break
75
+ end
76
+ end
77
+ end
78
+ return filters
79
+ end
80
+
81
+ def make_test_cmd(files_to_test)
82
+ cmds = []
83
+ full, partial = files_to_test.partition { |k,v| v.empty? }
84
+
85
+ unless full.empty? then
86
+ classes = full.map {|k,v| k}.flatten.join(' ')
87
+ cmds << "#{spec_command} #{classes}"
88
+ end
89
+
90
+ partial.each do |klass, methods|
91
+ methods.each { |meth| cmds << "#{spec_command} -s #{meth.inspect} #{klass}" }
92
+ end
93
+
94
+ return cmds.join('; ')
95
+ end
96
+ end
97
+
98
+ class RspecOnRailsAutotest < RspecAutotest
99
+ def initialize # :nodoc:
100
+ super
101
+ @exceptions = %r%^\./(?:coverage|db|doc|log|public|script|vendor)%
102
+ end
103
+
104
+ def tests_for_file(filename)
105
+ case filename
106
+ when %r%^spec/fixtures/(.*)s.yml% then
107
+ ["spec/models/#{$1}_spec.rb",
108
+ "spec/controllers/#{$1}_controller_spec.rb"]
109
+ when %r%^spec/models/.*rb$% then
110
+ [filename]
111
+ when %r%^spec/controllers/.*\.rb$% then
112
+ [filename]
113
+ when %r%^spec/views/.*\.rb$% then
114
+ [filename]
115
+ when %r%^spec/helpers/.*\.rb$% then
116
+ [filename]
117
+ when %r%^app/models/(.*)\.rb$% then
118
+ ["spec/models/#{$1}_spec.rb"]
119
+ when %r%^app/helpers/application_helper.rb% then
120
+ @files.keys.select { |f|
121
+ f =~ %r%^spec/controllers/.*_spec\.rb$%
122
+ }
123
+ when %r%^app/helpers/(.*)_helper.rb% then
124
+ ["spec/controllers/#{$1}_controller_spec.rb", "spec/helpers/#{$1}_spec.rb"]
125
+ when %r%^app/controllers/application.rb$% then
126
+ @files.keys.select { |f|
127
+ f =~ %r%^spec/controllers/.*_spec\.rb$%
128
+ }
129
+ when %r%^app/controllers/(.*)\.rb$% then
130
+ ["spec/controllers/#{$1}_spec.rb"]
131
+ when %r%^app/views/layouts/% then
132
+ []
133
+ when %r%^app/views/(.*)/% then
134
+ ["spec/controllers/#{$1}_controller_spec.rb", "spec/views/#{$1}_spec.rb"]
135
+ when %r%^config/routes.rb$% then
136
+ @files.keys.select do |f|
137
+ f =~ %r%^spec/controllers/.*_spec\.rb$%
138
+ end
139
+ when %r%^spec/spec_helper.rb%,
140
+ %r%^config/((boot|environment(s/test)?).rb|database.yml)% then
141
+ @files.keys.select do |f|
142
+ f =~ %r%^spec/(models|controllers)/.*_spec\.rb$%
143
+ end
144
+ else
145
+ @output.puts "Dunno! #{filename}" if $TESTING
146
+ []
147
+ end.uniq.select { |f| @files.has_key? f }
148
+ end
149
+ end
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'rubygems'
3
+ require 'spec'
4
+
5
+ rescue LoadError
6
+ puts "Please install rspec and mocha to run the tests."
7
+ exit 1
8
+ end
9
+
10
+ require File.join(File.dirname(__FILE__), *%w[../lib/demeters_revenge])
11
+
12
+ Spec::Runner.configure do |config|
13
+ config.mock_with :mocha
14
+ end
15
+
16
+ class ActiveRecordStub
17
+ class << self
18
+ def has_many(name, *args); end
19
+ def has_and_belongs_to_many(name, *args); end
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: demeters_revenge
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ version: "0.2"
9
+ platform: ruby
10
+ authors:
11
+ - Luke Redpath
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2010-03-09 00:00:00 +01:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: rubyforge
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ segments:
27
+ - 2
28
+ - 0
29
+ - 3
30
+ version: 2.0.3
31
+ type: :development
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: gemcutter
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ - 3
43
+ - 0
44
+ version: 0.3.0
45
+ type: :development
46
+ version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: hoe
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 2
56
+ - 5
57
+ - 0
58
+ version: 2.5.0
59
+ type: :development
60
+ version_requirements: *id003
61
+ description: Demeter is back with a vengeance. Prevent your models from violating his sacred law and you shall be spared.
62
+ email:
63
+ - wearenotlukeredpath@elabs.se
64
+ executables: []
65
+
66
+ extensions: []
67
+
68
+ extra_rdoc_files:
69
+ - History.txt
70
+ - Manifest.txt
71
+ files:
72
+ - History.txt
73
+ - Manifest.txt
74
+ - README.rdoc
75
+ - Rakefile
76
+ - demeters_revenge.gemspec
77
+ - examples/example_spec_helper.rb
78
+ - examples/has_and_belongs_to_many_examples.rb
79
+ - examples/has_many_examples.rb
80
+ - lib/demeters_revenge.rb
81
+ - lib/demeters_revenge/has_and_belongs_to_many_extensions.rb
82
+ - lib/demeters_revenge/has_many_extensions.rb
83
+ - script/console
84
+ - script/destroy
85
+ - script/generate
86
+ - spec/has_and_belongs_to_many_spec.rb
87
+ - spec/has_many_extensions_spec.rb
88
+ - spec/spec_autotest.rb
89
+ - spec/spec_helper.rb
90
+ has_rdoc: true
91
+ homepage: http://github.com/elabs/demeters_revenge
92
+ licenses: []
93
+
94
+ post_install_message:
95
+ rdoc_options:
96
+ - --main
97
+ - README.rdoc
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ segments:
112
+ - 0
113
+ version: "0"
114
+ requirements: []
115
+
116
+ rubyforge_project: demeters_revenge
117
+ rubygems_version: 1.3.6
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Demeter is back with a vengeance
121
+ test_files: []
122
+