rmodel 0.4.0.dev → 1.0.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 (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