superdupe 0.4.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.
Files changed (43) hide show
  1. data/README.markdown +10 -0
  2. data/lib/superdupe/active_resource_extensions.rb +161 -0
  3. data/lib/superdupe/attribute_template.rb +71 -0
  4. data/lib/superdupe/cucumber_hooks.rb +16 -0
  5. data/lib/superdupe/custom_mocks.rb +116 -0
  6. data/lib/superdupe/database.rb +69 -0
  7. data/lib/superdupe/dupe.rb +534 -0
  8. data/lib/superdupe/hash_pruner.rb +37 -0
  9. data/lib/superdupe/log.rb +38 -0
  10. data/lib/superdupe/mock.rb +127 -0
  11. data/lib/superdupe/model.rb +59 -0
  12. data/lib/superdupe/network.rb +56 -0
  13. data/lib/superdupe/record.rb +42 -0
  14. data/lib/superdupe/rest_validation.rb +16 -0
  15. data/lib/superdupe/schema.rb +52 -0
  16. data/lib/superdupe/sequence.rb +20 -0
  17. data/lib/superdupe/singular_plural_detection.rb +9 -0
  18. data/lib/superdupe/string.rb +7 -0
  19. data/lib/superdupe/symbol.rb +3 -0
  20. data/lib/superdupe.rb +20 -0
  21. data/rails_generators/dupe/dupe_generator.rb +20 -0
  22. data/rails_generators/dupe/templates/custom_mocks.rb +4 -0
  23. data/rails_generators/dupe/templates/definitions.rb +9 -0
  24. data/rails_generators/dupe/templates/load_dupe.rb +9 -0
  25. data/spec/lib_specs/active_resource_extensions_spec.rb +141 -0
  26. data/spec/lib_specs/attribute_template_spec.rb +173 -0
  27. data/spec/lib_specs/database_spec.rb +163 -0
  28. data/spec/lib_specs/dupe_spec.rb +522 -0
  29. data/spec/lib_specs/hash_pruner_spec.rb +73 -0
  30. data/spec/lib_specs/log_spec.rb +78 -0
  31. data/spec/lib_specs/logged_request_spec.rb +22 -0
  32. data/spec/lib_specs/mock_definitions_spec.rb +58 -0
  33. data/spec/lib_specs/mock_spec.rb +194 -0
  34. data/spec/lib_specs/model_spec.rb +95 -0
  35. data/spec/lib_specs/network_spec.rb +130 -0
  36. data/spec/lib_specs/record_spec.rb +70 -0
  37. data/spec/lib_specs/rest_validation_spec.rb +17 -0
  38. data/spec/lib_specs/schema_spec.rb +109 -0
  39. data/spec/lib_specs/sequence_spec.rb +39 -0
  40. data/spec/lib_specs/string_spec.rb +31 -0
  41. data/spec/lib_specs/symbol_spec.rb +17 -0
  42. data/spec/spec_helper.rb +8 -0
  43. metadata +142 -0
@@ -0,0 +1,58 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "Mock Definition Methods" do
4
+ before do
5
+ Dupe.reset
6
+ end
7
+
8
+ describe "Get" do
9
+ it "should require a url pattern that is a regex" do
10
+ proc { Get() }.should raise_error(ArgumentError)
11
+ proc { Get 'not a regexp' }.should raise_error(ArgumentError)
12
+ proc { Get %r{/some_url} }.should_not raise_error
13
+ end
14
+
15
+ it "should create and return a Dupe::Network::Mock of type :get" do
16
+ Dupe.network.mocks[:get].should be_empty
17
+ @book = Dupe.create :book, :label => 'rooby'
18
+ Dupe.network.mocks[:get].should_not be_empty
19
+ Dupe.network.mocks[:get].length.should == 2
20
+
21
+ mock = Get %r{/books/([^&]+)\.xml} do |label|
22
+ Dupe.find(:book) {|b| b.label == label}
23
+ end
24
+
25
+ Dupe.network.mocks[:get].length.should == 3
26
+ Dupe.network.mocks[:get].last.should == mock
27
+ Dupe.network.mocks[:get].last.url_pattern.should == %r{/books/([^&]+)\.xml}
28
+ book = Dupe.find(:book)
29
+ Dupe.network.request(:get, '/books/rooby.xml').should == book.to_xml_safe(:root => 'book')
30
+ end
31
+ end
32
+
33
+ describe "Post" do
34
+ it "should require a url pattern that is a regex" do
35
+ proc { Post() }.should raise_error(ArgumentError)
36
+ proc { Post 'not a regexp' }.should raise_error(ArgumentError)
37
+ proc { Post %r{/some_url} }.should_not raise_error
38
+ end
39
+
40
+ it "should create and return a Dupe::Network::Mock of type :post" do
41
+ @book = Dupe.create :book, :label => 'rooby'
42
+ Dupe.network.mocks[:post].should_not be_empty
43
+ Dupe.network.mocks[:post].length.should == 1
44
+
45
+ mock = Post %r{/books\.xml} do |post_data|
46
+ Dupe.create(:book, post_data)
47
+ end
48
+
49
+ Dupe.network.mocks[:post].length.should == 2
50
+ Dupe.network.mocks[:post].first.should == mock
51
+ Dupe.network.mocks[:post].first.url_pattern.should == %r{/books\.xml}
52
+ book_post = Dupe.create(:book, {:title => "Rooby", :label => "rooby"})
53
+ book_post.delete(:id)
54
+ book_response = Dupe.create(:book, {:title => "Rooby", :label => "rooby"})
55
+ Dupe.network.request(:post, '/books.xml', book_post).should == [Dupe.find(:book) {|b| b.id == 4}.to_xml_safe(:root => 'book'), "/books/4.xml"]
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,194 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Dupe::Network::Mock do
4
+ before do
5
+ Dupe.reset
6
+ end
7
+
8
+ describe "new" do
9
+ it "should require the url be a kind of regular expression" do
10
+ proc { Dupe::Network::Mock.new '', proc {} }.should raise_error(
11
+ ArgumentError,
12
+ "The URL pattern parameter must be a type of regular expression."
13
+ )
14
+ end
15
+
16
+ it "should set the, @url, and @response parameters accordingly" do
17
+ url_pattern = /\//
18
+ response = proc {}
19
+ mock = Dupe::Network::Mock.new url_pattern, response
20
+ mock.url_pattern.should == url_pattern
21
+ mock.response.should == response
22
+ end
23
+ end
24
+
25
+ describe "match?" do
26
+ it "should determine if a given string matches the mock's url pattern" do
27
+ url = %r{/blogs/(\d+).xml}
28
+ response = proc {}
29
+ mock = Dupe::Network::Mock.new url, response
30
+ mock.match?('/blogs/1.xml').should == true
31
+ mock.match?('/bogs/1.xml').should == false
32
+ end
33
+ end
34
+ end
35
+
36
+ describe Dupe::Network::GetMock do
37
+ before do
38
+ Dupe.reset
39
+ end
40
+
41
+ describe "mocked_response" do
42
+ describe "on a mock object whose response returns a Dupe.find with actual results" do
43
+ it "should convert the response result to xml" do
44
+ url_pattern = %r{/books/(\d+)\.xml}
45
+ response = proc {|id| Dupe.find(:book) {|b| b.id == id.to_i}}
46
+ book = Dupe.create :book
47
+ mock = Dupe::Network::GetMock.new url_pattern, response
48
+ mock.mocked_response('/books/1.xml').should == book.to_xml(:root => 'book')
49
+
50
+ proc { mock.mocked_response('/books/2.xml') }.should raise_error(Dupe::Network::GetMock::ResourceNotFoundError)
51
+
52
+ Dupe.define :author
53
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
54
+ mock.mocked_response('/authors.xml').should == [].to_xml(:root => 'results')
55
+ end
56
+
57
+ it "should add a request to the Dupe::Network#log" do
58
+ url_pattern = %r{/books/([a-zA-Z0-9-]+)\.xml}
59
+ response = proc {|label| Dupe.find(:book) {|b| b.label == label}}
60
+ book = Dupe.create :book, :label => 'rooby'
61
+ mock = Dupe::Network::GetMock.new url_pattern, response
62
+ Dupe.network.log.requests.length.should == 0
63
+ mock.mocked_response('/books/rooby.xml')
64
+ Dupe.network.log.requests.length.should == 1
65
+ end
66
+ end
67
+
68
+ describe "on a mock object whose response returns nil" do
69
+ it "should raise an error" do
70
+ url_pattern = %r{/authors/(\d+)\.xml}
71
+ response = proc { |id| Dupe.find(:author) {|a| a.id == id.to_i}}
72
+ Dupe.define :author
73
+ mock = Dupe::Network::GetMock.new url_pattern, response
74
+ proc {mock.mocked_response('/authors/1.xml')}.should raise_error(Dupe::Network::GetMock::ResourceNotFoundError)
75
+ end
76
+ end
77
+
78
+ describe "on a mock object whose response returns an empty array" do
79
+ it "should convert the empty array to an xml array record set with root 'results'" do
80
+ Dupe.define :author
81
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
82
+ mock.mocked_response('/authors.xml').should == [].to_xml(:root => 'results')
83
+ end
84
+
85
+ it "should add a request to the Dupe::Network#log" do
86
+ Dupe.define :author
87
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
88
+ Dupe.network.log.requests.length.should == 0
89
+ mock.mocked_response('/authors.xml')
90
+ Dupe.network.log.requests.length.should == 1
91
+ end
92
+ end
93
+
94
+ describe "on a mock object whose response returns an array of duped records" do
95
+ it "should convert the array to xml" do
96
+ Dupe.create :author
97
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
98
+ mock.mocked_response('/authors.xml').should == Dupe.find(:authors).to_xml(:root => 'authors')
99
+ end
100
+
101
+ it "should add a request to the Dupe::Network#log" do
102
+ Dupe.create :author
103
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
104
+ Dupe.network.log.requests.length.should == 0
105
+ mock.mocked_response('/authors.xml')
106
+ Dupe.network.log.requests.length.should == 1
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ describe Dupe::Network::PostMock do
113
+ before do
114
+ Dupe.reset
115
+ end
116
+
117
+ describe "mocked_response" do
118
+ describe "on a mock object whose response returns a location of a new record" do
119
+ it "should convert the new post to xml" do
120
+ Dupe.define :author
121
+ mock = Dupe::Network::PostMock.new %r{/authors\.xml$}, proc {|post_data| Dupe.create(:author, post_data)}
122
+ resp, url = mock.mocked_response('/authors.xml', {:name => "Rachel"})
123
+ resp.should == Dupe.find(:authors).first.to_xml_safe(:root => 'author')
124
+ url.should == "/authors/1.xml"
125
+ end
126
+
127
+ it "should add a request to the Dupe::Network#log" do
128
+ Dupe.define :author
129
+ mock = Dupe::Network::PostMock.new %r{/authors\.xml$}, proc {|post_data| Dupe.create(:author, post_data)}
130
+ Dupe.network.log.requests.length.should == 0
131
+ mock.mocked_response('/authors.xml', {:name => "Rachel"})
132
+ Dupe.network.log.requests.length.should == 1
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ describe Dupe::Network::PutMock do
139
+ before do
140
+ Dupe.reset
141
+ end
142
+
143
+ describe "mocked_response" do
144
+ describe "on a mock object whose response returns a location of a new record" do
145
+ before do
146
+ Dupe.define :author
147
+ @a = Dupe.create :author, :name => "Matt"
148
+ @mock = Dupe::Network::PutMock.new %r{/authors/(\d+)\.xml$}, proc {|id, put_data| Dupe.find(:author) {|a| a.id == id.to_i}.merge!(put_data)}
149
+ end
150
+
151
+ it "should convert the put to xml" do
152
+ resp, url = @mock.mocked_response('/authors/1.xml', {:name => "Rachel"})
153
+ resp.should == nil
154
+ @a.name.should == "Rachel"
155
+ url.should == "/authors/1.xml"
156
+ end
157
+
158
+ it "should add a request to the Dupe::Network#log" do
159
+ Dupe.network.log.requests.length.should == 0
160
+ @mock.mocked_response('/authors/1.xml', {:name => "Rachel"})
161
+ Dupe.network.log.requests.length.should == 1
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ describe Dupe::Network::DeleteMock do
168
+ before do
169
+ Dupe.reset
170
+ end
171
+
172
+ describe "mocked_response" do
173
+ describe "on a mock object whose response returns a location of a new record" do
174
+ before do
175
+ Dupe.define :author
176
+ @a = Dupe.create :author, :name => "Matt"
177
+ @mock = Dupe::Network::DeleteMock.new %r{/authors/(\d+)\.xml$}, proc {|id| Dupe.delete(:author) {|a| a.id == id.to_i}}
178
+ end
179
+
180
+ it "should convert the put to xml" do
181
+ Dupe.find(:authors).length.should == 1
182
+ resp = @mock.mocked_response('/authors/1.xml')
183
+ resp.should == nil
184
+ Dupe.find(:authors).length.should == 0
185
+ end
186
+
187
+ it "should add a request to the Dupe::Network#log" do
188
+ Dupe.network.log.requests.length.should == 0
189
+ @mock.mocked_response('/authors/1.xml')
190
+ Dupe.network.log.requests.length.should == 1
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,95 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Dupe::Model do
4
+ describe "new" do
5
+ it "should require a model name" do
6
+ proc { Dupe::Model.new }.should raise_error(ArgumentError)
7
+ end
8
+
9
+ it "should set the model name to what was passed in during initialization" do
10
+ m = Dupe::Model.new :book
11
+ m.name.should == :book
12
+ end
13
+
14
+ it "should initialize an empty schema" do
15
+ m = Dupe::Model.new :book
16
+ m.schema.should be_kind_of(Dupe::Model::Schema)
17
+ end
18
+
19
+ it "should setup an id_sequence initialized to 0" do
20
+ m = Dupe::Model.new :book
21
+ m.id_sequence.current_value.should == 1
22
+ end
23
+ end
24
+
25
+ describe "define" do
26
+ describe "when passed a proc" do
27
+ before do
28
+ @model = Dupe::Model.new :book
29
+ @definition = proc { |attrs|
30
+ attrs.author('Anonymous') do |dont_care|
31
+ 'Flying Spaghetti Monster'
32
+ end
33
+ }
34
+ end
35
+
36
+ it "should pass that proc off to it's schema" do
37
+ @model.schema.should_receive(:method_missing).once
38
+ @model.define @definition
39
+ end
40
+
41
+ it "should result in a schema with the desired attribute templates" do
42
+ @model.define @definition
43
+ @model.schema.attribute_templates[:author].name.should == :author
44
+ @model.schema.attribute_templates[:author].default.should == 'Anonymous'
45
+ @model.schema.attribute_templates[:author].transformer.call(
46
+ 'dont care'
47
+ ).should == 'Flying Spaghetti Monster'
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "create" do
53
+ before do
54
+ Dupe.define :book do |attrs|
55
+ attrs.title 'Untitled'
56
+ attrs.author 'Anon' do |author|
57
+ "Author: #{author}"
58
+ end
59
+ attrs.after_create do |book|
60
+ book.label = book.title.downcase.gsub(/\ +/, '-')
61
+ end
62
+ end
63
+
64
+ @book_model = Dupe.models[:book]
65
+ end
66
+
67
+ it "shouldn't require any parameters" do
68
+ proc {
69
+ @book_model.create
70
+ }.should_not raise_error
71
+ end
72
+
73
+ it "should return a Dupe::Database::Record instance with the desired parameters" do
74
+ book = @book_model.create
75
+ book.should be_kind_of(Dupe::Database::Record)
76
+ book.__model__.name.should == :book
77
+ book.id.should == 1
78
+ book.title.should == 'Untitled'
79
+ book.author.should == 'Anon'
80
+
81
+ # the callback shouldn't get run until the database record is inserted into the duped 'database'
82
+ book.label.should == nil
83
+
84
+ book = @book_model.create :title => 'Rooby On Rails', :author => 'Matt Parker'
85
+ book.__model__.name.should == :book
86
+ book.id.should == 2
87
+ book.title.should == 'Rooby On Rails'
88
+
89
+ # the callback shouldn't get run until the database record is inserted into the duped 'database'
90
+ book.label.should == nil
91
+
92
+ book.author.should == 'Author: Matt Parker'
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,130 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Dupe::Network do
4
+
5
+ describe "new" do
6
+ before do
7
+ @network = Dupe::Network.new
8
+ end
9
+
10
+ it "should initialize @mocks to a hash of empty arrays keyed with valid REST verbs" do
11
+ Dupe::Network::VERBS.each do |verb|
12
+ @network.mocks[verb].should == []
13
+ end
14
+ end
15
+
16
+ it "should initialize @log to a new Dupe::Network::Log" do
17
+ @network.log.should be_kind_of(Dupe::Network::Log)
18
+ end
19
+ end
20
+
21
+ describe "request" do
22
+ before do
23
+ @network = Dupe::Network.new
24
+ end
25
+
26
+ it "should require a valid REST verb" do
27
+ proc { @network.request }.should raise_error
28
+ proc { @network.request :invalid_rest_verb, '/some_url' }.should raise_error(Dupe::Network::UnknownRestVerbError)
29
+ proc { @network.request :get, '/some_url' }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
30
+ proc { @network.request :post, '/some_url', 'some body' }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
31
+ proc { @network.request :put, '/some_url' }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
32
+ proc { @network.request :delete, '/some_url' }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
33
+ end
34
+
35
+ it "should require a URL" do
36
+ proc { @network.request :get }.should raise_error(ArgumentError)
37
+ proc { @network.request :get, 'some_url'}.should_not raise_error(ArgumentError)
38
+ proc { @network.request :post, 'some_url', 'some body'}.should_not raise_error(ArgumentError)
39
+ proc { @network.request :put, 'some_url'}.should_not raise_error(ArgumentError)
40
+ proc { @network.request :delete, 'some_url'}.should_not raise_error(ArgumentError)
41
+ end
42
+
43
+ it "should raise an exception if the network has no mocks that match the url" do
44
+ proc { @network.request(:get, '/some_url')}.should raise_error(Dupe::Network::RequestNotFoundError)
45
+ proc { @network.request(:post, '/some_url', 'some body')}.should raise_error(Dupe::Network::RequestNotFoundError)
46
+ proc { @network.request(:put, '/some_url')}.should raise_error(Dupe::Network::RequestNotFoundError)
47
+ proc { @network.request(:delete, '/some_url')}.should raise_error(Dupe::Network::RequestNotFoundError)
48
+ end
49
+
50
+ it "should return the appropriate mock response if a mock matches the url" do
51
+ @network.define_service_mock :get, %r{/greeting$}, proc { "hello" }
52
+ @network.request(:get, '/greeting').should == 'hello'
53
+
54
+ @network.define_service_mock :post, %r{/greeting$}, proc { |post_data| Dupe.create(:greeting, post_data) }
55
+ resp, url = @network.request(:post, '/greeting', {} )
56
+ resp.should == Dupe.find(:greeting).to_xml_safe(:root => 'greeting')
57
+ url.should == "/greetings/1.xml"
58
+ end
59
+ end
60
+
61
+ describe "define_service_mock" do
62
+ before do
63
+ @network = Dupe::Network.new
64
+ end
65
+
66
+ it "should require a valid REST verb" do
67
+ proc { @network.define_service_mock }.should raise_error
68
+ proc { @network.define_service_mock :invalid_rest_verb, // }.should raise_error(Dupe::Network::UnknownRestVerbError)
69
+ proc { @network.define_service_mock :get, // }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
70
+ proc { @network.define_service_mock :post, // }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
71
+ proc { @network.define_service_mock :put, // }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
72
+ end
73
+
74
+ it "should require a valid Regexp url pattern" do
75
+ proc { @network.define_service_mock :get, 'not a regular expression' }.should raise_error(ArgumentError)
76
+ proc { @network.define_service_mock :post, 'not a regular expression' }.should raise_error(ArgumentError)
77
+ proc { @network.define_service_mock :get, // }.should_not raise_error
78
+ proc { @network.define_service_mock :post, // }.should_not raise_error
79
+ end
80
+
81
+ it "should create and return a new get service mock when given valid parameters" do
82
+ verb = :get
83
+ pattern = //
84
+ response = proc { 'test' }
85
+ @network.mocks[:get].should be_empty
86
+ mock = @network.define_service_mock verb, pattern, response
87
+ @network.mocks[:get].should_not be_empty
88
+ @network.mocks[:get].first.class.should == Dupe::Network::GetMock
89
+ @network.mocks[:get].length.should == 1
90
+ @network.mocks[:get].first.should == mock
91
+ end
92
+
93
+ it "should create and return a new post service mock when given valid parameters" do
94
+ verb = :post
95
+ pattern = //
96
+ response = proc { 'test' }
97
+ @network.mocks[:post].should be_empty
98
+ mock = @network.define_service_mock verb, pattern, response
99
+ @network.mocks[:post].should_not be_empty
100
+ @network.mocks[:post].first.class.should == Dupe::Network::PostMock
101
+ @network.mocks[:post].length.should == 1
102
+ @network.mocks[:post].first.should == mock
103
+ end
104
+
105
+ it "should create and return a new put service mock when given valid parameters" do
106
+ verb = :put
107
+ pattern = //
108
+ response = proc { 'test' }
109
+ @network.mocks[:put].should be_empty
110
+ mock = @network.define_service_mock verb, pattern, response
111
+ @network.mocks[:put].should_not be_empty
112
+ @network.mocks[:put].first.class.should == Dupe::Network::PutMock
113
+ @network.mocks[:put].length.should == 1
114
+ @network.mocks[:put].first.should == mock
115
+ end
116
+
117
+ it "should create and return a new delete service mock when given valid parameters" do
118
+ verb = :delete
119
+ pattern = //
120
+ response = proc { 'test' }
121
+ @network.mocks[:delete].should be_empty
122
+ mock = @network.define_service_mock verb, pattern, response
123
+ @network.mocks[:delete].should_not be_empty
124
+ @network.mocks[:delete].first.class.should == Dupe::Network::DeleteMock
125
+ @network.mocks[:delete].length.should == 1
126
+ @network.mocks[:delete].first.should == mock
127
+ end
128
+ end
129
+
130
+ end
@@ -0,0 +1,70 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Dupe::Database::Record do
4
+ describe "new" do
5
+ it "should create an object that is a kind of Hash" do
6
+ Dupe::Database::Record.new.should be_kind_of(Hash)
7
+ end
8
+ end
9
+
10
+ describe "id" do
11
+ it "should allow us to set the record id (and not the object id)" do
12
+ d = Dupe::Database::Record.new
13
+ d.id.should == nil
14
+ d[:id].should == nil
15
+ d.id = 1
16
+ d.id.should == 1
17
+ d[:id].should == 1
18
+ end
19
+ end
20
+
21
+ describe "method_missing" do
22
+ it "should allow us to access hash keys as if they were object attributes" do
23
+ d = Dupe::Database::Record.new
24
+ d[:some_key].should == nil
25
+ d.some_key.should == nil
26
+ d.some_key = 1
27
+ d.some_key.should == 1
28
+ d[:some_key].should == 1
29
+ d[:another_key] = 2
30
+ d.another_key.should == 2
31
+ d[:another_key].should == 2
32
+ end
33
+ end
34
+
35
+ describe "inspect" do
36
+ it "should show the class name" do
37
+ d = Dupe::Database::Record.new
38
+ d.inspect.should match(/^<#Dupe::Database::Record/)
39
+ end
40
+
41
+ it "should show the key/value pairs as attributes" do
42
+ d = Dupe::Database::Record.new
43
+ d.title = 'test'
44
+ d.inspect.should match(/title="test"/)
45
+ d.author = Dupe::Database::Record.new
46
+ d.inspect.should match(/author=<#Dupe::Database::Record/)
47
+ end
48
+
49
+ it "should show Fake::<model_name> when the record has a model" do
50
+ b = Dupe.create :book
51
+ b.inspect.should match(/^<#Duped::Book/)
52
+ b.author = Dupe.create :author
53
+ b.inspect.should match(/^<#Duped::Book/)
54
+ b.inspect.should match(/author=<#Duped::Author/)
55
+ end
56
+ end
57
+
58
+ describe "__model__" do
59
+ it "should have a __model__ instance variable" do
60
+ proc {Dupe::Database::Record.new.__model__}.should_not raise_error
61
+ end
62
+
63
+ it "should all you to set the __model_name__ instance variable" do
64
+ r = Dupe::Database::Record.new
65
+ proc {r.__model__ = :book}.should_not raise_error
66
+ r.__model__.should == :book
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Dupe::Network::RestValidation do
4
+ before do
5
+ class TestRestValidation
6
+ include Dupe::Network::RestValidation
7
+ end
8
+ end
9
+
10
+ describe "validate_request_type" do
11
+ it "should raise an exception if the request type isn't :get, :post, :put, or :delete" do
12
+ proc {
13
+ TestRestValidation.new.validate_request_type(:unknown_request_type)
14
+ }.should raise_error(Dupe::Network::UnknownRestVerbError)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,109 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Dupe::Model::Schema do
4
+ describe "new" do
5
+ it "should initialize attribute_templates to an empty hash" do
6
+ Dupe::Model::Schema.new.attribute_templates.should == {}
7
+ end
8
+
9
+ it "should initialize after_create_callbacks to an empty array" do
10
+ Dupe::Model::Schema.new.after_create_callbacks.should == []
11
+ end
12
+ end
13
+
14
+ describe "dynamic attribute_template creation methods" do
15
+ before do
16
+ @schema = Dupe::Model::Schema.new
17
+ end
18
+
19
+ describe "called with no parameters" do
20
+ it "should create a new attribute template with no transformer and no default value" do
21
+ @schema.title
22
+ @schema.attribute_templates[:title].should be_kind_of(Dupe::Model::Schema::AttributeTemplate)
23
+ @schema.attribute_templates[:title].name.should == :title
24
+ @schema.attribute_templates[:title].default.should be_nil
25
+ @schema.attribute_templates[:title].transformer.should be_nil
26
+ end
27
+ end
28
+
29
+ describe "called with a single parameter, but no block" do
30
+ it "should create a new attribute template with a default value but no transformer" do
31
+ @schema.title 'Untitled'
32
+ @schema.attribute_templates[:title].should be_kind_of(Dupe::Model::Schema::AttributeTemplate)
33
+ @schema.attribute_templates[:title].name.should == :title
34
+ @schema.attribute_templates[:title].default.should == 'Untitled'
35
+ @schema.attribute_templates[:title].transformer.should be_nil
36
+ end
37
+ end
38
+
39
+ describe "called with a block that accepts a parameter" do
40
+ it "should create a new attribute template without a default value, but with a tranformer" do
41
+ @schema.title {|dont_care| 'test'}
42
+ @schema.attribute_templates[:title].should be_kind_of(Dupe::Model::Schema::AttributeTemplate)
43
+ @schema.attribute_templates[:title].name.should == :title
44
+ @schema.attribute_templates[:title].default.should be_nil
45
+ @schema.attribute_templates[:title].transformer.should be_kind_of(Proc)
46
+ end
47
+ end
48
+
49
+ describe "called with a block that doesn't accept a parameter" do
50
+ it "should create a new attribute template without a transformer, and with the block as the default value" do
51
+ @schema.title { 'knock' * 3 }
52
+ @schema.attribute_templates[:title].should be_kind_of(Dupe::Model::Schema::AttributeTemplate)
53
+ @schema.attribute_templates[:title].name.should == :title
54
+ @schema.attribute_templates[:title].default.should be_kind_of(Proc)
55
+ @schema.attribute_templates[:title].default.call.should == "knockknockknock"
56
+ @schema.attribute_templates[:title].transformer.should be_nil
57
+ end
58
+ end
59
+
60
+ describe "called with a block and a parameter" do
61
+ it "should create a new attribute template with a default value AND with a tranformer" do
62
+ @schema.title('Untitled') {|dont_care| 'test'}
63
+ @schema.attribute_templates[:title].should be_kind_of(Dupe::Model::Schema::AttributeTemplate)
64
+ @schema.attribute_templates[:title].name.should == :title
65
+ @schema.attribute_templates[:title].default.should == 'Untitled'
66
+ @schema.attribute_templates[:title].transformer.should be_kind_of(Proc)
67
+ end
68
+ end
69
+ end
70
+
71
+ describe "#after_create" do
72
+ before do
73
+ @schema = Dupe::Model::Schema.new
74
+ end
75
+
76
+ it "should require a block that accepts a single parameter" do
77
+ proc { @schema.after_create }.should raise_error(ArgumentError)
78
+ proc { @schema.after_create { "parameterless block" } }.should raise_error(ArgumentError)
79
+ proc { @schema.after_create {|s| s.title = 'test' } }.should_not raise_error
80
+ end
81
+
82
+ it "should add the callback to the list of after_create_callbacks" do
83
+ @schema.after_create_callbacks.should be_empty
84
+ @schema.after_create {|s| s.title = 'test'}
85
+ @schema.after_create_callbacks.length.should == 1
86
+ @schema.after_create_callbacks.first.should be_kind_of(Proc)
87
+ end
88
+
89
+ end
90
+
91
+ describe "#uniquify" do
92
+ before do
93
+ @schema = Dupe::Model::Schema.new
94
+ end
95
+
96
+ it "should only accept a list of symbols" do
97
+ proc { @schema.uniquify }.should raise_error(ArgumentError, "You must pass at least one attribute to uniquify.")
98
+ proc { @schema.uniquify :hash => 'value' }.should raise_error(ArgumentError, "You may only pass symbols to uniquify.")
99
+ proc { @schema.uniquify :one, :two}.should_not raise_error
100
+ end
101
+
102
+ it "should create after_create_callbacks for each symbol passed to it" do
103
+ @schema.after_create_callbacks.should be_empty
104
+ @schema.uniquify :title, :label
105
+ @schema.after_create_callbacks.length.should == 2
106
+ @schema.after_create_callbacks.first.should be_kind_of(Proc)
107
+ end
108
+ end
109
+ end