rmodel 0.4.0.dev → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +4 -0
  4. data/.travis.yml +4 -3
  5. data/README.md +119 -153
  6. data/Rakefile +1 -2
  7. data/examples/mongo_embedded.rb +27 -21
  8. data/examples/scopes.rb +28 -0
  9. data/examples/sql_repository.rb +14 -26
  10. data/examples/timestamps.rb +7 -10
  11. data/examples/webapp/models/task.rb +9 -0
  12. data/examples/webapp/repositories/task_repository.rb +14 -0
  13. data/examples/webapp/server.rb +25 -0
  14. data/examples/webapp/support/mappers.rb +11 -0
  15. data/examples/webapp/support/repositories.rb +12 -0
  16. data/examples/webapp/support/sources.rb +13 -0
  17. data/examples/webapp/views/index.erb +20 -0
  18. data/lib/rmodel.rb +11 -8
  19. data/lib/rmodel/array_mapper.rb +23 -0
  20. data/lib/rmodel/base_mapper.rb +56 -0
  21. data/lib/rmodel/dummy_mapper.rb +15 -0
  22. data/lib/rmodel/mongo/mapper.rb +11 -0
  23. data/lib/rmodel/mongo/source.rb +50 -0
  24. data/lib/rmodel/repository.rb +36 -0
  25. data/lib/rmodel/repository_ext/scopable.rb +44 -0
  26. data/lib/rmodel/repository_ext/sugarable.rb +35 -0
  27. data/lib/rmodel/repository_ext/timestampable.rb +29 -0
  28. data/lib/rmodel/scope.rb +31 -0
  29. data/lib/rmodel/sequel/mapper.rb +6 -0
  30. data/lib/rmodel/sequel/source.rb +43 -0
  31. data/lib/rmodel/uni_hash.rb +16 -0
  32. data/lib/rmodel/version.rb +1 -1
  33. data/rmodel.gemspec +9 -3
  34. data/spec/rmodel/array_mapper_spec.rb +43 -0
  35. data/spec/rmodel/mongo/mapper_spec.rb +154 -0
  36. data/spec/rmodel/mongo/repository_spec.rb +16 -37
  37. data/spec/rmodel/mongo/source_spec.rb +113 -0
  38. data/spec/rmodel/sequel/mapper_spec.rb +57 -0
  39. data/spec/rmodel/sequel/repository_spec.rb +27 -38
  40. data/spec/rmodel/sequel/source_spec.rb +121 -0
  41. data/spec/shared/base_mapper.rb +39 -0
  42. data/spec/shared/clean_moped.rb +6 -2
  43. data/spec/shared/clean_sequel.rb +1 -1
  44. data/spec/shared/{repository_ext → repository}/crud.rb +20 -14
  45. data/spec/shared/repository/initialization.rb +39 -0
  46. data/spec/shared/repository/scopable.rb +137 -0
  47. data/spec/shared/repository/sugarable.rb +67 -0
  48. data/spec/shared/repository/timestampable.rb +137 -0
  49. data/spec/spec_helper.rb +17 -18
  50. metadata +120 -54
  51. data/examples/advanced_creation_of_repository.rb +0 -15
  52. data/examples/user.rb +0 -48
  53. data/lib/rmodel/base/repository.rb +0 -12
  54. data/lib/rmodel/base/repository_ext/sugarable.rb +0 -17
  55. data/lib/rmodel/base/repository_ext/timestampable.rb +0 -19
  56. data/lib/rmodel/mongo/repository.rb +0 -62
  57. data/lib/rmodel/mongo/repository_ext/query.rb +0 -34
  58. data/lib/rmodel/mongo/repository_ext/queryable.rb +0 -34
  59. data/lib/rmodel/mongo/setup.rb +0 -17
  60. data/lib/rmodel/mongo/simple_factory.rb +0 -78
  61. data/lib/rmodel/sequel/repository.rb +0 -61
  62. data/lib/rmodel/sequel/repository_ext/query.rb +0 -34
  63. data/lib/rmodel/sequel/repository_ext/queryable.rb +0 -30
  64. data/lib/rmodel/sequel/setup.rb +0 -12
  65. data/lib/rmodel/sequel/simple_factory.rb +0 -28
  66. data/lib/rmodel/setup.rb +0 -34
  67. data/spec/rmodel/mongo/repository_ext/queryable_spec.rb +0 -103
  68. data/spec/rmodel/mongo/repository_initialize_spec.rb +0 -174
  69. data/spec/rmodel/mongo/simple_factory_spec.rb +0 -195
  70. data/spec/rmodel/sequel/repository_ext/queryable_spec.rb +0 -114
  71. data/spec/rmodel/sequel/repository_initialize_spec.rb +0 -118
  72. data/spec/rmodel/sequel/simple_factory_spec.rb +0 -55
  73. data/spec/rmodel/setup_spec.rb +0 -54
  74. data/spec/shared/repository_ext/sugarable.rb +0 -44
  75. data/spec/shared/repository_ext/timestampable.rb +0 -67
@@ -1,103 +0,0 @@
1
- RSpec.describe Rmodel::Mongo::Repository do
2
- include_context 'clean mongo database'
3
-
4
- before do
5
- stub_const('Thing', Struct.new(:id, :a, :b))
6
- stub_const('ThingRepository', Class.new(Rmodel::Mongo::Repository))
7
- end
8
-
9
- let(:factory) { Rmodel::Mongo::SimpleFactory.new(Thing, :a, :b) }
10
- let(:repo) { ThingRepository.new(mongo_session, :things, factory) }
11
-
12
- before do
13
- repo.insert(Thing.new(nil, 2, 3))
14
- repo.insert(Thing.new(nil, 2, 4))
15
- repo.insert(Thing.new(nil, 5, 6))
16
- end
17
-
18
- describe '.scope' do
19
- context 'when a scope w/o arguments is defined' do
20
- before do
21
- ThingRepository.class_eval do
22
- scope :a_equals_2 do
23
- where(a: 2)
24
- end
25
- end
26
- end
27
-
28
- it 'works!' do
29
- expect(repo.query.a_equals_2.count).to eq 2
30
- end
31
-
32
- it 'returns an array of instances of the appropriate class' do
33
- expect(repo.query.a_equals_2.first).to be_an_instance_of Thing
34
- end
35
- end
36
-
37
- context 'when a scope w/ arguments is defined' do
38
- before do
39
- ThingRepository.class_eval do
40
- scope :a_equals do |n|
41
- where(a: n)
42
- end
43
- end
44
- end
45
-
46
- it 'works!' do
47
- expect(repo.query.a_equals(2).count).to eq 2
48
- end
49
- end
50
-
51
- context 'when two scopes are defined and chained' do
52
- before do
53
- ThingRepository.class_eval do
54
- scope :a_equals do |n|
55
- where(a: n)
56
- end
57
-
58
- scope :b_equals do |n|
59
- where(b: n)
60
- end
61
- end
62
- end
63
-
64
- it 'works!' do
65
- expect(repo.query.a_equals(2).b_equals(4).count).to eq 1
66
- end
67
- end
68
-
69
- context 'when an unknown scope is used' do
70
- it 'raises the NoMethodError' do
71
- expect {
72
- repo.query.something
73
- }.to raise_error NoMethodError
74
- end
75
- end
76
- end
77
-
78
- describe '.query' do
79
- describe '#remove' do
80
- context 'when no scope is given' do
81
- it 'removes all objects' do
82
- repo.query.remove
83
- expect(repo.query.count).to eq 0
84
- end
85
- end
86
-
87
- context 'when the scope filters 2 objects from 3' do
88
- before do
89
- ThingRepository.class_eval do
90
- scope :a_equals_2 do
91
- where(a: 2)
92
- end
93
- end
94
- end
95
-
96
- it 'removes 2 objects' do
97
- repo.query.a_equals_2.remove
98
- expect(repo.query.count).to eq 1
99
- end
100
- end
101
- end
102
- end
103
- end
@@ -1,174 +0,0 @@
1
- RSpec.describe Rmodel::Mongo::Repository do
2
- before do
3
- Mongo::Logger.logger.level = Logger::ERROR
4
- stub_const('User', Struct.new(:id, :name, :email))
5
- end
6
-
7
- describe '.client(name)' do
8
- subject { UserRepository.new }
9
-
10
- before { Rmodel::Setup.send :public, :client }
11
-
12
- context 'when it is called with an existent name' do
13
- before do
14
- Rmodel.setup do
15
- client :mongo, { hosts: [ 'localhost' ] }
16
- end
17
-
18
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository) {
19
- client :mongo
20
- simple_factory User, :name, :email
21
- attr_reader :client
22
- })
23
- end
24
- after { Rmodel::setup.clear }
25
-
26
- it 'sets the appropriate #client' do
27
- expect(subject.client).to be_an_instance_of Mongo::Client
28
- end
29
- end
30
-
31
- context 'when it is called with a non-existent name' do
32
- before do
33
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository) {
34
- client :mongo
35
- simple_factory User, :name, :email
36
- attr_reader :client
37
- })
38
- end
39
-
40
- it 'makes #client raise the ArgumentError' do
41
- expect { subject.client }.to raise_error ArgumentError
42
- end
43
- end
44
-
45
- context 'when it is not called' do
46
- before do
47
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository) {
48
- simple_factory User, :name, :email
49
- attr_reader :client
50
- })
51
- end
52
-
53
- context 'when the :default client is set' do
54
- before do
55
- Rmodel.setup do
56
- client :default, { hosts: [ 'localhost' ] }
57
- end
58
- end
59
- after { Rmodel::setup.clear }
60
-
61
- it 'sets #client to be default' do
62
- expect(subject.client).to be_an_instance_of Mongo::Client
63
- end
64
- end
65
-
66
- context 'when the :default client is not set' do
67
- it 'makes #client raise the ArgumentError' do
68
- expect { subject.client }.to raise_error ArgumentError
69
- end
70
- end
71
- end
72
- end
73
-
74
- describe '.collection(name)' do
75
- subject { UserRepository.new }
76
-
77
- before do
78
- Rmodel.setup do
79
- client :default, { hosts: [ 'localhost' ] }
80
- end
81
- end
82
- after { Rmodel::setup.clear }
83
-
84
- context 'when the :people collection is given' do
85
- before do
86
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository) {
87
- collection :people
88
- simple_factory User, :name, :email
89
- attr_reader :collection
90
- })
91
- end
92
-
93
- it 'uses the :people' do
94
- expect(subject.collection).to eq :people
95
- end
96
- end
97
-
98
- context 'when no collection is given' do
99
- before do
100
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository) {
101
- simple_factory User, :name, :email
102
- attr_reader :collection
103
- })
104
- end
105
-
106
- it 'gets the right name by convention' do
107
- expect(subject.collection).to eq :users
108
- end
109
- end
110
- end
111
-
112
- describe '.simple_factory(klass, attribute1, attribute2, ..., &block)' do
113
- subject { UserRepository.new }
114
-
115
- before do
116
- Rmodel.setup do
117
- client :default, { hosts: [ 'localhost' ] }
118
- end
119
- end
120
- after { Rmodel::setup.clear }
121
-
122
- context 'when it is called' do
123
- before do
124
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository) {
125
- simple_factory User, :name, :email
126
- attr_reader :factory
127
- })
128
- end
129
-
130
- it 'sets the appropriate #factory' do
131
- expect(subject.factory).to be_an_instance_of Rmodel::Mongo::SimpleFactory
132
- end
133
- end
134
-
135
- context 'when it is not called' do
136
- before do
137
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository))
138
- end
139
-
140
- it 'make #initialize raise an error' do
141
- expect {
142
- UserRepository.new
143
- }.to raise_error ArgumentError
144
- end
145
- end
146
-
147
- context 'when a block is given' do
148
- it 'evaluates the block within the context of the factory' do
149
- tmp = nil
150
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository) {
151
- simple_factory User, :name, :email do
152
- tmp = self
153
- end
154
- })
155
- expect(tmp).to be_an_instance_of Rmodel::Mongo::SimpleFactory
156
- end
157
- end
158
- end
159
-
160
- describe '#initialize(client, collection, factory)' do
161
- context 'when all constructor arguments are passed' do
162
- before do
163
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository))
164
- end
165
- let(:factory) { Rmodel::Mongo::SimpleFactory.new(User, :name, :email) }
166
-
167
- it 'works!' do
168
- expect {
169
- UserRepository.new(Object.new, :users, factory)
170
- }.not_to raise_error
171
- end
172
- end
173
- end
174
- end
@@ -1,195 +0,0 @@
1
- RSpec.describe Rmodel::Mongo::SimpleFactory do
2
- context 'when the Thing(id, name, price, parts, owner) class is defined' do
3
- before do
4
- stub_const('Thing', Struct.new(:id, :name, :price, :parts, :owner))
5
- stub_const('Part', Struct.new(:id, :name, :producer))
6
- stub_const('Producer', Struct.new(:id, :country))
7
- stub_const('Owner', Struct.new(:id, :full_name, :phones))
8
- stub_const('Phone', Struct.new(:id, :number))
9
- end
10
-
11
- subject do
12
- described_class.new(Thing, :name, :price) do
13
- embeds_many :parts, simple_factory(Part, :name) do
14
- embeds_one :producer, simple_factory(Producer, :country)
15
- end
16
- embeds_one :owner, simple_factory(Owner, :full_name) do
17
- embeds_many :phones, simple_factory(Phone, :number)
18
- end
19
- end
20
- end
21
-
22
- describe '#fromHash(hash)' do
23
- let(:result) { subject.fromHash(hash) }
24
-
25
- context 'when the hash with _id, name and price is given' do
26
- let(:hash) { { '_id' => 1, 'name' => 'chair', 'price' => 100 } }
27
-
28
- it 'returns an instance of Thing' do
29
- expect(result).to be_an_instance_of Thing
30
- end
31
-
32
- it 'sets the attributes correctly' do
33
- expect(result.name).to eq 'chair'
34
- expect(result.price).to eq 100
35
- end
36
-
37
- it 'sets the Thing#id correctly' do
38
- expect(result.id).to eq 1
39
- end
40
-
41
- it 'leaves <many embedded> nil' do
42
- expect(result.parts).to be_nil
43
- end
44
-
45
- it 'leaves <one embedded> nil' do
46
- expect(result.owner).to be_nil
47
- end
48
- end
49
-
50
- context 'when the hash contains many parts' do
51
- let(:hash) do
52
- {
53
- 'parts' => [
54
- { '_id' => 1, 'name' => 'back', 'producer' => { '_id' => 10, 'country' => 'UK' } },
55
- { '_id' => 2, 'name' => 'leg' }
56
- ]
57
- }
58
- end
59
-
60
- it 'maps subdocuments to <many embedded>' do
61
- expect(result.parts.length).to eq 2
62
-
63
- expect(result.parts[0]).to be_an_instance_of Part
64
- expect(result.parts[0].id).to eq 1
65
- expect(result.parts[0].name).to eq 'back'
66
-
67
- expect(result.parts[0].producer).to be_an_instance_of Producer
68
- expect(result.parts[0].producer.id).to eq 10
69
- expect(result.parts[0].producer.country).to eq 'UK'
70
-
71
- expect(result.parts[1]).to be_an_instance_of Part
72
- expect(result.parts[1].id).to eq 2
73
- expect(result.parts[1].name).to eq 'leg'
74
- expect(result.parts[1].producer).to be_nil
75
- end
76
- end
77
-
78
- context 'when the hash contains one owner' do
79
- let(:hash) do
80
- {
81
- 'owner' => {
82
- '_id' => 3,
83
- 'full_name' => 'John Doe',
84
- 'phones' => [
85
- { '_id' => 20, 'number' => '+1111111111' },
86
- { '_id' => 21, 'number' => '+2222222222' }
87
- ]
88
- }
89
- }
90
- end
91
-
92
- it 'maps subdocument to <one embedded>' do
93
- expect(result.owner).to be_an_instance_of Owner
94
- expect(result.owner.id).to eq 3
95
- expect(result.owner.full_name).to eq 'John Doe'
96
-
97
- expect(result.owner.phones.length).to eq 2
98
-
99
- expect(result.owner.phones[0].id).to eq 20
100
- expect(result.owner.phones[0].number).to eq '+1111111111'
101
-
102
- expect(result.owner.phones[1].id).to eq 21
103
- expect(result.owner.phones[1].number).to eq '+2222222222'
104
- end
105
- end
106
- end
107
-
108
- describe '#toHash(object, id_included)' do
109
- let(:thing) { Thing.new(1, 'chair', 100) }
110
-
111
- context 'when id_included is false' do
112
- let(:result) { subject.toHash(thing, false) }
113
-
114
- it 'returns an instance of Hash' do
115
- expect(result).to be_an_instance_of Hash
116
- end
117
-
118
- it 'sets the keys correctly' do
119
- expect(result['name']).to eq 'chair'
120
- expect(result['price']).to eq 100
121
- end
122
-
123
- it 'has no the "_id" key' do
124
- expect(result.has_key?('_id')).to be false
125
- end
126
- end
127
-
128
- context 'when id_included is true' do
129
- let(:result) { subject.toHash(thing, true) }
130
-
131
- it 'sets the "_id" key' do
132
- expect(result['_id']).to eq 1
133
- end
134
- end
135
-
136
- context 'when the object has <many embedded>' do
137
- let(:thing) do
138
- Thing.new(1, 'chair', 100, [
139
- Part.new(1, 'back', Producer.new(10, 'UK')),
140
- Part.new(2, 'leg')
141
- ])
142
- end
143
- let(:result) { subject.toHash(thing, true) }
144
-
145
- it 'maps <many embedded> to subdocuments' do
146
- expect(result['parts'].length).to eq 2
147
-
148
- expect(result['parts'][0]['_id']).to eq 1
149
- expect(result['parts'][0]['name']).to eq 'back'
150
-
151
- expect(result['parts'][0]['producer']['_id']).to eq 10
152
- expect(result['parts'][0]['producer']['country']).to eq 'UK'
153
-
154
- expect(result['parts'][1]['_id']).to eq 2
155
- expect(result['parts'][1]['name']).to eq 'leg'
156
- end
157
- end
158
-
159
- context 'when the object has <one embedded>' do
160
- let(:thing) do
161
- Thing.new(1, 'chair', 100, nil, Owner.new(3, 'John Doe', [
162
- Phone.new(20, '+1111111111'),
163
- Phone.new(21, '+2222222222')
164
- ]))
165
- end
166
- let(:result) { subject.toHash(thing, true) }
167
-
168
- it 'maps <one embedded> to the subdocument' do
169
- expect(result['owner']['_id']).to eq 3
170
- expect(result['owner']['full_name']).to eq 'John Doe'
171
-
172
- expect(result['owner']['phones'].length).to eq 2
173
-
174
- expect(result['owner']['phones'][0]['_id']).to eq 20
175
- expect(result['owner']['phones'][0]['number']).to eq '+1111111111'
176
-
177
- expect(result['owner']['phones'][1]['_id']).to eq 21
178
- expect(result['owner']['phones'][1]['number']).to eq '+2222222222'
179
- end
180
- end
181
- end
182
-
183
- describe '#initialize(..., &block)' do
184
- context 'when a block is given' do
185
- it 'passes self to the block' do
186
- tmp = nil
187
- factory = described_class.new(Thing, :name) do
188
- tmp = self
189
- end
190
- expect(tmp).to be factory
191
- end
192
- end
193
- end
194
- end
195
- end