seat-belt 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +29 -0
- data/.travis.yml +6 -0
- data/Changelog.md +55 -0
- data/Gemfile +15 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +705 -0
- data/Rakefile +1 -0
- data/ext/.gitkeep +0 -0
- data/lib/seatbelt.rb +37 -0
- data/lib/seatbelt/collections/collection.rb +56 -0
- data/lib/seatbelt/core.rb +15 -0
- data/lib/seatbelt/core/callee.rb +35 -0
- data/lib/seatbelt/core/eigenmethod.rb +150 -0
- data/lib/seatbelt/core/eigenmethod_proxy.rb +45 -0
- data/lib/seatbelt/core/ext/core_ext.rb +0 -0
- data/lib/seatbelt/core/gate.rb +198 -0
- data/lib/seatbelt/core/ghost_tunnel.rb +81 -0
- data/lib/seatbelt/core/implementation.rb +158 -0
- data/lib/seatbelt/core/interface.rb +51 -0
- data/lib/seatbelt/core/iterators/method_config.rb +50 -0
- data/lib/seatbelt/core/lookup_table.rb +101 -0
- data/lib/seatbelt/core/pool.rb +90 -0
- data/lib/seatbelt/core/property.rb +161 -0
- data/lib/seatbelt/core/proxy.rb +135 -0
- data/lib/seatbelt/core/synthesizeable.rb +50 -0
- data/lib/seatbelt/core/terminal.rb +59 -0
- data/lib/seatbelt/dependencies.rb +5 -0
- data/lib/seatbelt/document.rb +175 -0
- data/lib/seatbelt/errors.rb +1 -0
- data/lib/seatbelt/errors/errors.rb +150 -0
- data/lib/seatbelt/gate_config.rb +59 -0
- data/lib/seatbelt/ghost.rb +140 -0
- data/lib/seatbelt/models.rb +9 -0
- data/lib/seatbelt/seatbelt.rb +10 -0
- data/lib/seatbelt/synthesizer.rb +3 -0
- data/lib/seatbelt/synthesizers/document.rb +16 -0
- data/lib/seatbelt/synthesizers/mongoid.rb +16 -0
- data/lib/seatbelt/synthesizers/synthesizer.rb +146 -0
- data/lib/seatbelt/tape.rb +2 -0
- data/lib/seatbelt/tape_deck.rb +71 -0
- data/lib/seatbelt/tapes/tape.rb +105 -0
- data/lib/seatbelt/tapes/util/delegate.rb +56 -0
- data/lib/seatbelt/translator.rb +66 -0
- data/lib/seatbelt/version.rb +3 -0
- data/seatbelt.gemspec +27 -0
- data/spec/lib/seatbelt/core/eigenmethod_spec.rb +102 -0
- data/spec/lib/seatbelt/core/gate_spec.rb +521 -0
- data/spec/lib/seatbelt/core/ghost_tunnel_spec.rb +21 -0
- data/spec/lib/seatbelt/core/lookup_table_spec.rb +234 -0
- data/spec/lib/seatbelt/core/pool_spec.rb +270 -0
- data/spec/lib/seatbelt/core/proxy_spec.rb +108 -0
- data/spec/lib/seatbelt/core/terminal_spec.rb +184 -0
- data/spec/lib/seatbelt/document_spec.rb +287 -0
- data/spec/lib/seatbelt/gate_config_spec.rb +98 -0
- data/spec/lib/seatbelt/ghost_spec.rb +568 -0
- data/spec/lib/seatbelt/synthesizers/document_spec.rb +47 -0
- data/spec/lib/seatbelt/synthesizers/mongoid_spec.rb +134 -0
- data/spec/lib/seatbelt/synthesizers/synthesizer_spec.rb +112 -0
- data/spec/lib/seatbelt/tape_deck_spec.rb +180 -0
- data/spec/lib/seatbelt/tapes/tape_spec.rb +115 -0
- data/spec/lib/seatbelt/translator_spec.rb +108 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/implementations/seatbelt_environment.rb +19 -0
- data/spec/support/shared_examples/shared_api_class.rb +7 -0
- data/spec/support/shared_examples/shared_collection_child.rb +7 -0
- data/spec/support/worlds/eigenmethod_world.rb +7 -0
- metadata +205 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Seatbelt::Synthesizers::Document do
|
4
|
+
describe "#synthesize" do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
class ApiDocumentSynthesize
|
8
|
+
include Seatbelt::Document
|
9
|
+
include Seatbelt::Ghost
|
10
|
+
|
11
|
+
attribute :name, String
|
12
|
+
attribute :age, Integer
|
13
|
+
|
14
|
+
api_method :lolspeak_my_name
|
15
|
+
end
|
16
|
+
|
17
|
+
class ImplementationApiDocumentSynthesize
|
18
|
+
include Seatbelt::Document
|
19
|
+
include Seatbelt::Gate
|
20
|
+
|
21
|
+
#field :name, :type => String
|
22
|
+
attribute :name, String
|
23
|
+
|
24
|
+
synthesize :from => "ApiDocumentSynthesize"
|
25
|
+
|
26
|
+
def implement_lolspeak_my_name
|
27
|
+
return self
|
28
|
+
end
|
29
|
+
implement :implement_lolspeak_my_name,
|
30
|
+
:as => "ApiDocumentSynthesize#lolspeak_my_name"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "synchrones the implementation object with the proxy object" do
|
35
|
+
api = ApiDocumentSynthesize.new(:name => "Hendrik")
|
36
|
+
|
37
|
+
impl_name = api.lolspeak_my_name
|
38
|
+
|
39
|
+
expect(impl_name.name).to eq api.name
|
40
|
+
api.name="Jan"
|
41
|
+
expect(impl_name.name).to eq api.name
|
42
|
+
impl_name.name = "Gerno"
|
43
|
+
expect(api.name).to eq impl_name.name
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Seatbelt::Synthesizers::Mongoid do
|
4
|
+
describe "#synthesize" do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
class ApiMongoidSynthesize
|
8
|
+
include Seatbelt::Document
|
9
|
+
include Seatbelt::Ghost
|
10
|
+
|
11
|
+
attribute :name, String
|
12
|
+
attribute :age, Integer
|
13
|
+
|
14
|
+
api_method :lolspeak_my_name
|
15
|
+
end
|
16
|
+
|
17
|
+
class ApiMongoidSynthesize2
|
18
|
+
include Seatbelt::Document
|
19
|
+
include Seatbelt::Ghost
|
20
|
+
|
21
|
+
attribute :name, String
|
22
|
+
attribute :lastname, String
|
23
|
+
|
24
|
+
api_method :lolspeak_lastname
|
25
|
+
api_method :change_my_name
|
26
|
+
end
|
27
|
+
|
28
|
+
class ImplementationApiMongoidSynthesize
|
29
|
+
include ::Mongoid::Document
|
30
|
+
include Seatbelt::Gate
|
31
|
+
|
32
|
+
field :name, :type => String
|
33
|
+
field :age, :type => String
|
34
|
+
field :lastname, :type => String
|
35
|
+
|
36
|
+
synthesize :from => "ApiMongoidSynthesize",
|
37
|
+
:adapter => "Seatbelt::Synthesizers::Mongoid"
|
38
|
+
|
39
|
+
def implement_lolspeak_my_name
|
40
|
+
return self
|
41
|
+
end
|
42
|
+
implement :implement_lolspeak_my_name,
|
43
|
+
:as => "ApiMongoidSynthesize#lolspeak_my_name"
|
44
|
+
|
45
|
+
def implement_lolspeak_lastname
|
46
|
+
return self
|
47
|
+
end
|
48
|
+
implement :implement_lolspeak_lastname,
|
49
|
+
:as => "ApiMongoidSynthesize2#lolspeak_lastname"
|
50
|
+
|
51
|
+
def implement_change_my_name(name_value)
|
52
|
+
@proxy.name = name_value
|
53
|
+
return self
|
54
|
+
end
|
55
|
+
implement :implement_change_my_name,
|
56
|
+
:as => "ApiMongoidSynthesize2#change_my_name"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "synchrones the implementation object with the proxy object" do
|
61
|
+
api = ApiMongoidSynthesize.new(:name => "Hendrik")
|
62
|
+
|
63
|
+
impl_name = api.lolspeak_my_name
|
64
|
+
|
65
|
+
expect(impl_name.name).to eq api.name
|
66
|
+
api.name="Jan"
|
67
|
+
expect(impl_name.name).to eq api.name
|
68
|
+
impl_name.name = "Gerno"
|
69
|
+
expect(api.name).to eq impl_name.name
|
70
|
+
|
71
|
+
api2 = ApiMongoidSynthesize.new(:name => "Jon")
|
72
|
+
|
73
|
+
impl2_name = api2.lolspeak_my_name
|
74
|
+
expect(impl2_name.name).to eq api2.name
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "different attributes than API class" do
|
78
|
+
before(:all) do
|
79
|
+
class ApiMongoid
|
80
|
+
include Seatbelt::Document
|
81
|
+
include Seatbelt::Ghost
|
82
|
+
|
83
|
+
attribute :name, String
|
84
|
+
attribute :age, Integer
|
85
|
+
|
86
|
+
api_method :my_name
|
87
|
+
end
|
88
|
+
class ImplementationApiMongoidSynthesizeDifferentAttrs
|
89
|
+
include ::Mongoid::Document
|
90
|
+
include Seatbelt::Gate
|
91
|
+
|
92
|
+
field :prx_identifier, :type => String
|
93
|
+
field :n_age, :type => Integer
|
94
|
+
|
95
|
+
synthesize :from => "ApiMongoid",
|
96
|
+
:adapter => "Seatbelt::Synthesizers::Mongoid"
|
97
|
+
|
98
|
+
synthesize_map :name => :prx_identifier,:age => :n_age
|
99
|
+
|
100
|
+
def implement_speak_my_name
|
101
|
+
return self
|
102
|
+
end
|
103
|
+
implement :implement_speak_my_name,
|
104
|
+
:as => "ApiMongoid#my_name"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
it "synchrones the implementation object with the proxy object" do
|
108
|
+
api = ApiMongoid.new(:name => "John")
|
109
|
+
|
110
|
+
impl_name = api.my_name
|
111
|
+
expect(impl_name.prx_identifier).to eq api.name
|
112
|
+
|
113
|
+
api.name="Jan"
|
114
|
+
expect(impl_name.prx_identifier).to eq api.name
|
115
|
+
impl_name.prx_identifier = "Gerno"
|
116
|
+
expect(api.name).to eq impl_name.prx_identifier
|
117
|
+
|
118
|
+
api.age = 29
|
119
|
+
expect(impl_name.n_age).to eq api.age
|
120
|
+
|
121
|
+
impl_name.n_age = 44
|
122
|
+
expect(api.age).to eq impl_name.n_age
|
123
|
+
|
124
|
+
api2 = ApiMongoid.new(:name => "George",:age => 65)
|
125
|
+
|
126
|
+
impl2_name = api2.my_name
|
127
|
+
expect(impl2_name.prx_identifier).to eq api2.name
|
128
|
+
expect(impl2_name.n_age).to eq api2.age
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Seatbelt::Synthesizer do
|
4
|
+
before(:all) do
|
5
|
+
class ApiFromSynthesize
|
6
|
+
include Seatbelt::Document
|
7
|
+
include Seatbelt::Ghost
|
8
|
+
|
9
|
+
attribute :age, Integer
|
10
|
+
attribute :name, String
|
11
|
+
end
|
12
|
+
|
13
|
+
class ImplementationSynthesize
|
14
|
+
include Mongoid::Document
|
15
|
+
|
16
|
+
field :age, :type => Integer
|
17
|
+
end
|
18
|
+
|
19
|
+
class SynthesizeWrapper
|
20
|
+
include Seatbelt::Synthesizer
|
21
|
+
|
22
|
+
def synthesizable_attributes
|
23
|
+
[:age]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#instance_methods" do
|
29
|
+
let(:implementation) { ImplementationSynthesize.new }
|
30
|
+
let(:instance) do
|
31
|
+
SynthesizeWrapper.new(ApiFromSynthesize, implementation)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "provides #synthesize" do
|
35
|
+
expect(instance).to respond_to(:synthesize)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "expects a #synthesizable_attributes method" do
|
39
|
+
class InvalidSynthesizer
|
40
|
+
include Seatbelt::Synthesizer
|
41
|
+
end
|
42
|
+
|
43
|
+
synthesizer = InvalidSynthesizer.new(ApiFromSynthesize, implementation)
|
44
|
+
|
45
|
+
expect do
|
46
|
+
synthesizer.synthesize
|
47
|
+
end.to \
|
48
|
+
raise_error(Seatbelt::Errors::SynthesizeableAttributesNotImplementedError,
|
49
|
+
"Your synthesizer has to implement #synthesizable_attributes.")
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#synthesize" do
|
54
|
+
|
55
|
+
it "sets the attributes of the proxy object and vice versa" do
|
56
|
+
implementation.stub(:proxy).and_return(ApiFromSynthesize.new)
|
57
|
+
implementation.class.stub(:get_synthesize_map).and_return({})
|
58
|
+
instance.synthesize
|
59
|
+
|
60
|
+
implementation.proxy.age = 12
|
61
|
+
expect(implementation.age).to eq implementation.proxy.age
|
62
|
+
|
63
|
+
implementation.age = 44
|
64
|
+
expect(implementation.proxy.age).to eq implementation.age
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "and a #synthesize_map" do
|
68
|
+
before(:all) do
|
69
|
+
|
70
|
+
class ImplementationSynthesizeWithDifferentAttributes
|
71
|
+
include Mongoid::Document
|
72
|
+
|
73
|
+
field :lc_age, :type => Integer
|
74
|
+
field :prx_identifier, :type => String
|
75
|
+
|
76
|
+
def self.synthesize_map
|
77
|
+
{
|
78
|
+
:age => :lc_age,
|
79
|
+
:name => :prx_identifier
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
SynthesizeWrapper.class_eval do
|
84
|
+
def synthesizable_attributes
|
85
|
+
[:lc_age, :prx_identifier]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
it "sets the attributes of the proxy object and vice versa" do
|
93
|
+
implementation2 = ImplementationSynthesizeWithDifferentAttributes.new
|
94
|
+
instance = SynthesizeWrapper.new(ApiFromSynthesize, implementation2)
|
95
|
+
implementation2.stub(:proxy).and_return(ApiFromSynthesize.new)
|
96
|
+
|
97
|
+
instance.synthesize
|
98
|
+
implementation2.proxy.age = 12
|
99
|
+
implementation2.proxy.name = "Kafka"
|
100
|
+
expect(implementation2.lc_age).to eq implementation2.proxy.age
|
101
|
+
expect(implementation2.prx_identifier).to eq implementation2.proxy.name
|
102
|
+
|
103
|
+
implementation2.lc_age = 44
|
104
|
+
expect(implementation2.proxy.age).to eq implementation2.lc_age
|
105
|
+
implementation2.prx_identifier = "Roberts"
|
106
|
+
expect(implementation2.proxy.name).to eq implementation2.prx_identifier
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Seatbelt::TapeDeck do
|
4
|
+
|
5
|
+
describe "#class_methods" do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
class Sample
|
9
|
+
include Seatbelt::TapeDeck
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "provides #tapes" do
|
14
|
+
expect(Sample).to respond_to(:tapes)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "provides #use_tape" do
|
18
|
+
expect(Sample).to respond_to(:use_tape)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "provides #use_tapes" do
|
22
|
+
expect(Sample).to respond_to(:use_tapes)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "provides #add_tape" do
|
26
|
+
expect(Sample).to respond_to(:add_tape)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "provides #respond" do
|
30
|
+
expect(Sample).to respond_to(:respond)
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#tapes" do
|
34
|
+
|
35
|
+
let(:mock_tape){ double("ATape") }
|
36
|
+
|
37
|
+
it "is a store to hold tapes for the class" do
|
38
|
+
expect do
|
39
|
+
Sample.tapes << mock_tape
|
40
|
+
end.to change { Sample.tapes.size }.by(1)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#use_tape" do
|
46
|
+
|
47
|
+
let(:sample_tape){ double("SampleTape") }
|
48
|
+
|
49
|
+
it "adds a tape to the tape store for this class" do
|
50
|
+
sample_tape.stub(:tape_deck=)
|
51
|
+
sample_tape.stub(:tape_deck).and_return(nil)
|
52
|
+
expect do
|
53
|
+
Sample.use_tape sample_tape
|
54
|
+
end.to change { Sample.tapes.size }.by(1)
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
it "a tape is not useable in multiple tapedecks" do
|
59
|
+
sample_tape.stub(:tape_deck).and_return(Sample)
|
60
|
+
class Sample2
|
61
|
+
include Seatbelt::TapeDeck
|
62
|
+
end
|
63
|
+
expect do
|
64
|
+
Sample2.use_tape sample_tape
|
65
|
+
end.to raise_error(Seatbelt::Errors::MultipleTapeUsageDetectedError)
|
66
|
+
end
|
67
|
+
|
68
|
+
context "the tape is already in the tape store for this class" do
|
69
|
+
|
70
|
+
before(:all) do
|
71
|
+
class SampleTape < Seatbelt::Tape
|
72
|
+
end
|
73
|
+
Sample.instance_eval do
|
74
|
+
self.instance_variable_set(:@tapes, [])
|
75
|
+
end
|
76
|
+
Sample.use_tape SampleTape
|
77
|
+
end
|
78
|
+
|
79
|
+
it "then it didn't add the tape again" do
|
80
|
+
expect do
|
81
|
+
Sample.use_tape SampleTape
|
82
|
+
end.to change{ Sample.tapes.size }.by(0)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#use_tapes" do
|
89
|
+
|
90
|
+
before(:all) do
|
91
|
+
class RegionTape < Seatbelt::Tape;end
|
92
|
+
class HotelTape < Seatbelt::Tape;end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "adds a bunch of tapes to the tape store for this class" do
|
96
|
+
expect do
|
97
|
+
Sample.use_tapes RegionTape, HotelTape
|
98
|
+
end.to change{ Sample.tapes.size }.by(2)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#respond" do
|
104
|
+
|
105
|
+
before(:all) do
|
106
|
+
Sample.instance_eval do
|
107
|
+
self.instance_variable_set(:@tapes, [])
|
108
|
+
end
|
109
|
+
|
110
|
+
class TestTape < Seatbelt::Tape
|
111
|
+
end
|
112
|
+
|
113
|
+
Sample.class_eval do
|
114
|
+
use_tape TestTape
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "finds an answer on the tapes for the class that matches a query" do
|
119
|
+
TestTape.class_eval do
|
120
|
+
translate /Call me (\w+) !/ do |sentence, name|
|
121
|
+
name
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
query = "Call me Harry !"
|
126
|
+
expect(Sample.respond(query)).to eq "Harry"
|
127
|
+
end
|
128
|
+
|
129
|
+
it "passes the original query as first param to the block" do
|
130
|
+
TestTape.class_eval do
|
131
|
+
translate /Super summer party at (\d+)/ do |sentence, data|
|
132
|
+
sentence
|
133
|
+
end
|
134
|
+
end
|
135
|
+
query2 = "Super summer party at 12.08.2014"
|
136
|
+
expect(Sample.respond(query2)).to eq query2
|
137
|
+
end
|
138
|
+
|
139
|
+
it "passes the matched data as second param list to the block" do
|
140
|
+
TestTape.class_eval do
|
141
|
+
translate /Add (\d+) to (\d+) and divide it by (\d+)/ do |sentence,
|
142
|
+
sum1,
|
143
|
+
sum2,
|
144
|
+
divider|
|
145
|
+
result = sum1.to_i + sum2.to_i
|
146
|
+
result/divider.to_i
|
147
|
+
end
|
148
|
+
end
|
149
|
+
query = "Add 5 to 11 and divide it by 2"
|
150
|
+
result = Sample.respond(query)
|
151
|
+
expect(result).to be_instance_of Fixnum
|
152
|
+
expect(result).to eq 8
|
153
|
+
end
|
154
|
+
|
155
|
+
it "raises an error if no answer is found for the query" do
|
156
|
+
expect do
|
157
|
+
Sample.respond(%Q{Show me 2 hotels nearby London})
|
158
|
+
end.to raise_error(Seatbelt::Errors::NoTapeFoundForQueryError)
|
159
|
+
end
|
160
|
+
|
161
|
+
context "translate block takes just one argument" do
|
162
|
+
|
163
|
+
it "then the argument is not the query sentence" do
|
164
|
+
TestTape.class_eval do
|
165
|
+
translate /Finde für mich (\d+) Hotels nahe meiner aktuellen Position/ do |count|
|
166
|
+
count.to_i
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
query = "Finde für mich 5 Hotels nahe meiner aktuellen Position"
|
171
|
+
result = Sample.answer(query)
|
172
|
+
expect(result).to_not eq query
|
173
|
+
expect(result).to eq 5
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|