seat-belt 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +29 -0
  3. data/.travis.yml +6 -0
  4. data/Changelog.md +55 -0
  5. data/Gemfile +15 -0
  6. data/Guardfile +13 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +705 -0
  9. data/Rakefile +1 -0
  10. data/ext/.gitkeep +0 -0
  11. data/lib/seatbelt.rb +37 -0
  12. data/lib/seatbelt/collections/collection.rb +56 -0
  13. data/lib/seatbelt/core.rb +15 -0
  14. data/lib/seatbelt/core/callee.rb +35 -0
  15. data/lib/seatbelt/core/eigenmethod.rb +150 -0
  16. data/lib/seatbelt/core/eigenmethod_proxy.rb +45 -0
  17. data/lib/seatbelt/core/ext/core_ext.rb +0 -0
  18. data/lib/seatbelt/core/gate.rb +198 -0
  19. data/lib/seatbelt/core/ghost_tunnel.rb +81 -0
  20. data/lib/seatbelt/core/implementation.rb +158 -0
  21. data/lib/seatbelt/core/interface.rb +51 -0
  22. data/lib/seatbelt/core/iterators/method_config.rb +50 -0
  23. data/lib/seatbelt/core/lookup_table.rb +101 -0
  24. data/lib/seatbelt/core/pool.rb +90 -0
  25. data/lib/seatbelt/core/property.rb +161 -0
  26. data/lib/seatbelt/core/proxy.rb +135 -0
  27. data/lib/seatbelt/core/synthesizeable.rb +50 -0
  28. data/lib/seatbelt/core/terminal.rb +59 -0
  29. data/lib/seatbelt/dependencies.rb +5 -0
  30. data/lib/seatbelt/document.rb +175 -0
  31. data/lib/seatbelt/errors.rb +1 -0
  32. data/lib/seatbelt/errors/errors.rb +150 -0
  33. data/lib/seatbelt/gate_config.rb +59 -0
  34. data/lib/seatbelt/ghost.rb +140 -0
  35. data/lib/seatbelt/models.rb +9 -0
  36. data/lib/seatbelt/seatbelt.rb +10 -0
  37. data/lib/seatbelt/synthesizer.rb +3 -0
  38. data/lib/seatbelt/synthesizers/document.rb +16 -0
  39. data/lib/seatbelt/synthesizers/mongoid.rb +16 -0
  40. data/lib/seatbelt/synthesizers/synthesizer.rb +146 -0
  41. data/lib/seatbelt/tape.rb +2 -0
  42. data/lib/seatbelt/tape_deck.rb +71 -0
  43. data/lib/seatbelt/tapes/tape.rb +105 -0
  44. data/lib/seatbelt/tapes/util/delegate.rb +56 -0
  45. data/lib/seatbelt/translator.rb +66 -0
  46. data/lib/seatbelt/version.rb +3 -0
  47. data/seatbelt.gemspec +27 -0
  48. data/spec/lib/seatbelt/core/eigenmethod_spec.rb +102 -0
  49. data/spec/lib/seatbelt/core/gate_spec.rb +521 -0
  50. data/spec/lib/seatbelt/core/ghost_tunnel_spec.rb +21 -0
  51. data/spec/lib/seatbelt/core/lookup_table_spec.rb +234 -0
  52. data/spec/lib/seatbelt/core/pool_spec.rb +270 -0
  53. data/spec/lib/seatbelt/core/proxy_spec.rb +108 -0
  54. data/spec/lib/seatbelt/core/terminal_spec.rb +184 -0
  55. data/spec/lib/seatbelt/document_spec.rb +287 -0
  56. data/spec/lib/seatbelt/gate_config_spec.rb +98 -0
  57. data/spec/lib/seatbelt/ghost_spec.rb +568 -0
  58. data/spec/lib/seatbelt/synthesizers/document_spec.rb +47 -0
  59. data/spec/lib/seatbelt/synthesizers/mongoid_spec.rb +134 -0
  60. data/spec/lib/seatbelt/synthesizers/synthesizer_spec.rb +112 -0
  61. data/spec/lib/seatbelt/tape_deck_spec.rb +180 -0
  62. data/spec/lib/seatbelt/tapes/tape_spec.rb +115 -0
  63. data/spec/lib/seatbelt/translator_spec.rb +108 -0
  64. data/spec/spec_helper.rb +16 -0
  65. data/spec/support/implementations/seatbelt_environment.rb +19 -0
  66. data/spec/support/shared_examples/shared_api_class.rb +7 -0
  67. data/spec/support/shared_examples/shared_collection_child.rb +7 -0
  68. data/spec/support/worlds/eigenmethod_world.rb +7 -0
  69. metadata +205 -0
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ describe Seatbelt::Proxy do
4
+ before(:all) do
5
+ Seatbelt::Proxy.class_eval do
6
+ def klass
7
+ @klass = [] unless defined?(@klass)
8
+ @klass
9
+ end
10
+ private :klass
11
+ end
12
+ end
13
+ describe "instance methods" do
14
+
15
+ it "prodvides #call" do
16
+ expect(subject).to respond_to(:call)
17
+ end
18
+
19
+ it "provides #tunnel" do
20
+ expect(subject).to respond_to(:tunnel)
21
+ end
22
+
23
+ describe "#call" do
24
+
25
+ it "calls a method of its klass" do
26
+ proxy = Seatbelt::Proxy.new
27
+ expect(proxy.call(:empty?)).to be true
28
+ expect do
29
+ proxy.call(:push, 1)
30
+ end.to change{ proxy.send(:klass).size }.by(1)
31
+ expect do
32
+ proxy.push(1)
33
+ end.to change{ proxy.send(:klass).size }.by(1)
34
+ end
35
+
36
+ end
37
+
38
+ describe "#tunnel" do
39
+ let(:klass){ double(:region) }
40
+ let(:proxy){ Seatbelt::Proxy.new }
41
+ let(:implementation_object){ double("implementation") }
42
+ def define_doubles
43
+ implementation_object.stub(:my_implementation_attribute).
44
+ and_return("Black")
45
+ eigenmethod = double("Seatbelt::Eigenmethod")
46
+ eigenmethod.stub(:callee).and_return(implementation_object)
47
+ implementation_object.stub(:eigenmethods).and_return([eigenmethod])
48
+ klass.stub(:hasable).and_return(implementation_object)
49
+ end
50
+
51
+ before do
52
+ define_doubles
53
+ proxy.stub(:object).and_return(klass)
54
+ end
55
+
56
+ it "delegates a method chain call to the underlying callee object" do
57
+ expect(proxy.tunnel("hasable.my_implementation_attribute")).to eq "Black"
58
+ end
59
+
60
+ context "calling a not existing object" do
61
+
62
+ it "raises a Seatbelt::Errors::ObjectDoesNotExistError" do
63
+ expect do
64
+ proxy.tunnel("likeable.an_attribute")
65
+ end.to raise_error(Seatbelt::Errors::ObjectDoesNotExistError,
66
+ "The object you called does not exist.")
67
+ end
68
+ end
69
+
70
+ context "calling with an unchained argument" do
71
+
72
+ it "generates a warning" do
73
+ implementation_object.stub(:hasable).and_return([])
74
+ proxy.should_receive(:warn).with("You called a single object. Use #call instead.")
75
+ proxy.tunnel("hasable")
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+ describe "calling not allowable methods on proxy object" do
85
+
86
+ it "raises a NoMethodError" do
87
+ proxy = Seatbelt::Proxy.new
88
+ expect{ proxy.klass }.to raise_error(NoMethodError)
89
+ expect(proxy.object).to be_instance_of Array
90
+
91
+ Seatbelt::Proxy.class_eval do
92
+ def klass
93
+ @klass = Array unless defined?(@klass)
94
+ @klass
95
+ end
96
+ private :klass
97
+ end
98
+
99
+ proxy = Seatbelt::Proxy.new
100
+ expect{ proxy.klass }.to raise_error(NoMethodError)
101
+ expect(proxy.object).to be Array
102
+ expect do
103
+ proxy.try_convert([1])
104
+ end.to_not raise_error
105
+ end
106
+ end
107
+
108
+ end
@@ -0,0 +1,184 @@
1
+ require 'spec_helper'
2
+
3
+ describe Seatbelt::Terminal do
4
+
5
+ describe "class methods" do
6
+
7
+ it "provides #luggage=" do
8
+ expect(Seatbelt::Terminal).to respond_to(:luggage=)
9
+ end
10
+
11
+ it "provides #luggage" do
12
+ expect(Seatbelt::Terminal).to respond_to(:luggage)
13
+ end
14
+
15
+ it "provides #call" do
16
+ expect(Seatbelt::Terminal).to respond_to(:call)
17
+ end
18
+
19
+ describe "#call" do
20
+
21
+ context "lugagge store is empty" do
22
+
23
+ it "calling a method raises Seatbelt::MethodNotImplementedError" do
24
+ stub_eigenmethods(Hash)
25
+ expect do
26
+ Seatbelt::Terminal.call(:my_method, Hash, 1,2)
27
+ end.to raise_error(Seatbelt::Errors::MethodNotImplementedError)
28
+ end
29
+
30
+ end
31
+
32
+ context "method to call is not implemented" do
33
+
34
+ module Seatbelt
35
+ class Array < ::Array
36
+ include Seatbelt::Gate
37
+ end
38
+ end
39
+
40
+ before do
41
+ proxy = Seatbelt::Eigenmethod.new
42
+ proxy.method = Array.instance_method(:push)
43
+ proxy.implemented_as = :add_item
44
+ proxy.scope = :class
45
+ proxy.namespace = "Seatbelt::Array"
46
+ proxy.receiver = Array
47
+
48
+ Seatbelt::Terminal.luggage << proxy
49
+
50
+ proxy = Seatbelt::Eigenmethod.new
51
+ proxy.method = Array.instance_method(:push)
52
+ proxy.implemented_as = :my_method
53
+ proxy.scope = :instance
54
+ proxy.namespace = "Seatbelt::Array"
55
+ proxy.receiver = Array
56
+
57
+ Seatbelt::Terminal.luggage << proxy
58
+ end
59
+
60
+ it "raises Seatbelt::MethodNotImplementedError" do
61
+ stub_eigenmethods(Seatbelt::Array)
62
+ expect do
63
+ Seatbelt::Terminal.call(:my_method, Seatbelt::Array, 1,2)
64
+ end.to raise_error(Seatbelt::Errors::MethodNotImplementedError)
65
+ end
66
+
67
+ context "the same method name but different namespace" do
68
+
69
+ it "raises Seatbelt::MethodNotImplementedError" do
70
+ hsh = {}
71
+ stub_eigenmethods(hsh)
72
+ expect do
73
+ Seatbelt::Terminal.call(:my_method, hsh, 1,2)
74
+ end.to raise_error(Seatbelt::Errors::MethodNotImplementedError)
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ context "method to call is implemented" do
82
+
83
+ before(:all) do
84
+ class ASample
85
+ include Seatbelt::Ghost
86
+ def abs_of_number(number)
87
+ number.abs
88
+ end
89
+
90
+ def self.klass_method
91
+ 1
92
+ end
93
+ end
94
+
95
+ module Seatbelt
96
+ class ImplementA
97
+ include Gate
98
+
99
+ def method_to_call(num)
100
+ 2+num+proxy.call(:abs_of_number, -2)
101
+ end
102
+ implement :method_to_call, :as => "ASample#my_method"
103
+
104
+ def acts_as_class_method(factor1, factor2)
105
+ #p proxy
106
+ factor1 * factor2 - proxy.call(:klass_method)
107
+ end
108
+ implement :acts_as_class_method, :as => "ASample.c_method"
109
+
110
+ def return_proxy
111
+ return proxy.object
112
+ end
113
+ implement :return_proxy, :as => "ASample#chain"
114
+
115
+ def self.klass_method
116
+ return 2 + proxy.call(:klass_method)
117
+ end
118
+ implement :klass_method, :as => "ASample.klass_method",
119
+ :type => :class
120
+ end
121
+ end
122
+ end
123
+
124
+ context "and it's an instance method" do
125
+ it "delegates to the implemented method" do
126
+ packages = Seatbelt::Terminal.for_scope_and_namespace(:instance,
127
+ "ASample")
128
+ asample = ASample.new
129
+ packages.each do |package|
130
+ Seatbelt::EigenmethodProxy.set(Seatbelt::Proxy.new,
131
+ { :eigenmethod => package,
132
+ :object => asample,
133
+ :receiver => package.receiver,
134
+ })
135
+ end
136
+ expect(Seatbelt::Terminal.call(:my_method, ASample.new ,1,2)).to eq 6
137
+
138
+ expect(Seatbelt::Terminal.call(:chain, ASample.new, 0)).to \
139
+ respond_to(:abs_of_number)
140
+
141
+ end
142
+
143
+ it "raises an ArgumentError if too few arguments passed through" do
144
+ expect do
145
+ Seatbelt::Terminal.call(:my_method, ASample.new)
146
+ end.to raise_error(ArgumentError)
147
+ end
148
+
149
+ end
150
+
151
+ context "and it's a class method" do
152
+ it "delegates to the implemented method" do
153
+ package = Seatbelt::Terminal.luggage.select do |pack|
154
+ pack.implemented_as.eql?(:c_method)
155
+ end.first
156
+ options = {
157
+ :eigenmethod => package,
158
+ :receiver => ASample.new,
159
+ :add_to => true,
160
+ }
161
+ #Seatbelt::EigenmethodProxy.set(Seatbelt::Proxy.new, options)
162
+ ASample.class_eval do
163
+ api_method :c_method, :scope => :class,
164
+ :args => [:sum1, :sum2]
165
+ end
166
+ ASample.c_method(2,3)
167
+ #expect(Seatbelt::Terminal.call(:c_method, ASample,2,3)).to eq 5
168
+ #expect(Seatbelt::Terminal.call(:klass_method, ASample)).to eq 3
169
+ end
170
+
171
+ it "raises an ArgumentError if too few arguments passed through" do
172
+ expect do
173
+ Seatbelt::Terminal.call(:c_method, ASample,3)
174
+ end.to raise_error(ArgumentError)
175
+
176
+ end
177
+ end
178
+
179
+ end
180
+ end
181
+
182
+ end
183
+
184
+ end
@@ -0,0 +1,287 @@
1
+ require 'spec_helper'
2
+
3
+ describe Seatbelt::Document do
4
+
5
+ describe "included in a class" do
6
+
7
+ before(:all) do
8
+ class SampleDocument
9
+ include Seatbelt::Document
10
+ end
11
+ end
12
+
13
+ describe "provides class methods" do
14
+
15
+ it "#attribute" do
16
+ expect(SampleDocument).to respond_to(:attribute)
17
+ end
18
+
19
+
20
+ it "#validates_presence_of" do
21
+ expect(SampleDocument).to respond_to(:validates_presence_of)
22
+ end
23
+
24
+ it "#provides #has_many" do
25
+ expect(SampleDocument).to respond_to(:has_many)
26
+ end
27
+
28
+ it "provides #has" do
29
+ expect(SampleDocument).to respond_to(:has)
30
+ end
31
+
32
+ end
33
+
34
+ describe "defining attributes" do
35
+ before(:all) do
36
+ SampleDocument.class_eval do
37
+ attribute :name, String
38
+ attribute :length, Integer, :default => 120
39
+ end
40
+ end
41
+
42
+ let(:document){ SampleDocument.new }
43
+
44
+ it "returns the value type" do
45
+ document.name = "API-doc.pdf"
46
+ expect(document.name).to be_instance_of String
47
+ expect(document.length).to be_instance_of Fixnum
48
+ expect(document.length).to be 120
49
+
50
+ end
51
+
52
+ it "returns the value after setting it" do
53
+ name = "API-doc.pdf"
54
+ document.name = name
55
+ expect(document.name).to eq name
56
+ end
57
+
58
+ describe "accessing attributes in implementation classes" do
59
+
60
+ before(:all) do
61
+ SampleDocument.class_eval do
62
+ include Seatbelt::Ghost
63
+ api_method :foo
64
+ api_method :foobar
65
+
66
+ api_method :bar, :scope => :class
67
+
68
+ def self.class_foo
69
+ "Summer"
70
+ end
71
+ end
72
+
73
+ class ImplementsSampleDocument
74
+ include Seatbelt::Gate
75
+
76
+ attr_accessor :single_num
77
+
78
+ def implementation_method
79
+ #proxy.call("name=", "Winter")
80
+ @single_num = 13
81
+ #p self
82
+ return [proxy.call(:name), self.object_id]
83
+ end
84
+ implement :implementation_method, :as => "SampleDocument#foo"
85
+
86
+ def implementation_method2
87
+ #p self
88
+ return [self.object_id, single_num]
89
+ end
90
+ implement :implementation_method2, :as => "SampleDocument#foobar"
91
+
92
+ def self.another_implementation_method
93
+ return proxy.call(:class_foo)
94
+ end
95
+ implement :another_implementation_method,
96
+ :as => "SampleDocument.bar",
97
+ :type => :class
98
+ end
99
+ end
100
+
101
+ it "evaluates the attributes value" do
102
+ document = SampleDocument.new(:name => "Winter")
103
+ #expect(document.foo).to eq "Winter"
104
+ name, object_id1 = document.foo
105
+ object_id2, single_num = document.foobar
106
+
107
+ expect(object_id1).to eq object_id2
108
+ expect(single_num).to eq 13
109
+
110
+ expect(SampleDocument.bar).to eq "Summer"
111
+ end
112
+ end
113
+ end
114
+
115
+ describe "validating of attributes" do
116
+ before(:all) do
117
+ SampleDocument.class_eval do
118
+ attribute :name, String
119
+
120
+ validates_presence_of :name
121
+ end
122
+ end
123
+
124
+ describe "#validates_presence_of" do
125
+
126
+ context "attribute is given" do
127
+
128
+ it "then the model is valid" do
129
+ document = SampleDocument.new(:name => "Peter")
130
+ expect(document).to be_valid
131
+ end
132
+
133
+ end
134
+
135
+ context "attribute is not given" do
136
+
137
+ it "then the model isn't valid" do
138
+ document = SampleDocument.new
139
+ expect(document).to_not be_valid
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+ end
146
+
147
+ describe "defining associations" do
148
+
149
+ describe "#has_many" do
150
+
151
+ before(:all) do
152
+ SampleDocument.class_eval do
153
+ has_many :hotels, Seatbelt::Models::Hotel
154
+ end
155
+ end
156
+
157
+ let(:document){ SampleDocument.new }
158
+
159
+ it "returns an instance of array" do
160
+ expect(document.hotels).to respond_to(:count)
161
+ end
162
+
163
+ context "adding a model that isn't of required type" do
164
+
165
+ it "raises a Seatbelt::Errors::TypeMissmatchError" do
166
+ message = "An instance of Seatbelt::Models::Hotel awaited but "
167
+ message += "get an instance of Seatbelt::Models::Offer."
168
+ expect do
169
+ document.hotels << Seatbelt::Models::Offer.new
170
+ end.to raise_error Seatbelt::Errors::TypeMissmatchError, message
171
+ end
172
+ end
173
+
174
+ context "adding a model that is of required type" do
175
+
176
+ it "increases the association collection size" do
177
+ expect do
178
+ document.hotels << Seatbelt::Models::Hotel.new(:name => "Atlanta")
179
+ end.to change{ document.hotels.size }.by(1)
180
+ end
181
+
182
+ context "by only adding an attribute hash" do
183
+
184
+ it "increases the association collection size" do
185
+ expect do
186
+ document.hotels << {:name => "Westin"}
187
+ end.to change{ document.hotels.size }.by(1)
188
+ end
189
+
190
+ end
191
+
192
+ end
193
+
194
+ context "add multiple #has_many associations" do
195
+
196
+ before(:all) do
197
+ SampleDocument.class_eval do
198
+ has_many :offers, Seatbelt::Models::Offer
199
+ end
200
+ end
201
+
202
+ it "passes validation for every #has_many definition" do
203
+ document = SampleDocument.new
204
+ expect do
205
+ document.hotels << Seatbelt::Models::Hotel.new
206
+ end.to change { document.hotels.size }.by(1)
207
+ expect do
208
+ document.offers << Seatbelt::Models::Offer.new
209
+ end.to change { document.offers.size }.by(1)
210
+ end
211
+
212
+ it "raises an TypeMissmatchError if model isn't required type" do
213
+ document = SampleDocument.new
214
+ message = "An instance of Seatbelt::Models::Hotel awaited but "
215
+ message += "get an instance of Seatbelt::Models::Offer."
216
+ expect do
217
+ document.hotels << Seatbelt::Models::Offer.new
218
+ end.to raise_error Seatbelt::Errors::TypeMissmatchError, message
219
+ message = "An instance of Seatbelt::Models::Offer awaited but "
220
+ message += "get an instance of Seatbelt::Models::Hotel."
221
+ expect do
222
+ document.offers << Seatbelt::Models::Hotel.new
223
+ end.to raise_error Seatbelt::Errors::TypeMissmatchError, message
224
+ end
225
+
226
+ end
227
+ end
228
+
229
+ describe "#has" do
230
+
231
+ context "defining a reference without class" do
232
+ before(:all) do
233
+ SampleDocument.class_eval do
234
+ has :flight
235
+ end
236
+ end
237
+
238
+ let(:document){ SampleDocument.new }
239
+
240
+ it "takes Seatbelt::Models namespace" do
241
+ expect(document).to respond_to(:flight)
242
+ end
243
+
244
+ context "assigning the reference " do
245
+
246
+ context "has instance then the getter" do
247
+ it "returns the instance of guessed class" do
248
+ document.flight = Seatbelt::Models::Flight.new
249
+ expect(document.flight).to be_instance_of(Seatbelt::Models::Flight)
250
+ end
251
+ end
252
+
253
+ context "has attribute hash" do
254
+ it "returns the instance of guessed class" do
255
+ document.flight = {:trip_length => 12}
256
+ expect(document.flight).to be_instance_of(Seatbelt::Models::Flight)
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ context "defining a reference with class" do
263
+ before(:all) do
264
+ class TrashBin
265
+ include Seatbelt::Document
266
+
267
+ attribute :empty, Boolean
268
+ end
269
+ SampleDocument.class_eval do
270
+ has :trash_bin, TrashBin
271
+ end
272
+ end
273
+
274
+ let(:document){ SampleDocument.new }
275
+
276
+ it "takes the given class namespace" do
277
+ expect(document).to respond_to(:trash_bin)
278
+ document.trash_bin = TrashBin.new
279
+ expect(document.trash_bin).to be_instance_of(TrashBin)
280
+ document.trash_bin = {:emtpy => false}
281
+ expect(document.trash_bin).to be_instance_of(TrashBin)
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end
287
+ end