yadm 0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +273 -0
- data/Rakefile +10 -0
- data/examples/basic.rb +43 -0
- data/examples/migration.rb +65 -0
- data/lib/yadm.rb +39 -0
- data/lib/yadm/adapters.rb +32 -0
- data/lib/yadm/adapters/common_sql.rb +120 -0
- data/lib/yadm/adapters/memory.rb +175 -0
- data/lib/yadm/adapters/mysql.rb +17 -0
- data/lib/yadm/adapters/postgresql.rb +17 -0
- data/lib/yadm/adapters/sqlite.rb +17 -0
- data/lib/yadm/criteria.rb +32 -0
- data/lib/yadm/criteria/argument.rb +22 -0
- data/lib/yadm/criteria/attribute.rb +15 -0
- data/lib/yadm/criteria/condition.rb +31 -0
- data/lib/yadm/criteria/expression.rb +19 -0
- data/lib/yadm/criteria/limit.rb +25 -0
- data/lib/yadm/criteria/order.rb +48 -0
- data/lib/yadm/criteria_parser.rb +62 -0
- data/lib/yadm/criteria_parser/expression_parser.rb +77 -0
- data/lib/yadm/entity.rb +53 -0
- data/lib/yadm/identity_map.rb +51 -0
- data/lib/yadm/mapper.rb +16 -0
- data/lib/yadm/mapping.rb +71 -0
- data/lib/yadm/mapping/attribute.rb +31 -0
- data/lib/yadm/query.rb +28 -0
- data/lib/yadm/repository.rb +103 -0
- data/lib/yadm/version.rb +3 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/criteria_helpers.rb +33 -0
- data/spec/support/sequel_helpers.rb +25 -0
- data/spec/support/shared_examples_for_a_sequel_adapter.rb +173 -0
- data/spec/yadm/adapters/common_sql_spec.rb +89 -0
- data/spec/yadm/adapters/memory_spec.rb +230 -0
- data/spec/yadm/adapters/mysql_spec.rb +9 -0
- data/spec/yadm/adapters/postgresql_spec.rb +9 -0
- data/spec/yadm/adapters/sqlite_spec.rb +5 -0
- data/spec/yadm/adapters_spec.rb +32 -0
- data/spec/yadm/criteria/condition_spec.rb +50 -0
- data/spec/yadm/criteria/limit_spec.rb +45 -0
- data/spec/yadm/criteria/order_spec.rb +50 -0
- data/spec/yadm/criteria_parser/expression_parser_spec.rb +47 -0
- data/spec/yadm/criteria_parser_spec.rb +55 -0
- data/spec/yadm/criteria_spec.rb +40 -0
- data/spec/yadm/entity_spec.rb +76 -0
- data/spec/yadm/identity_map_spec.rb +128 -0
- data/spec/yadm/mapper_spec.rb +23 -0
- data/spec/yadm/mapping/attribute_spec.rb +35 -0
- data/spec/yadm/mapping_spec.rb +122 -0
- data/spec/yadm/query_spec.rb +45 -0
- data/spec/yadm/repository_spec.rb +175 -0
- data/spec/yadm_spec.rb +45 -0
- data/yadm.gemspec +33 -0
- metadata +254 -0
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'yadm/adapters/memory'
|
2
|
+
|
3
|
+
RSpec.describe YADM::Adapters::Memory do
|
4
|
+
let(:person_attributes) do
|
5
|
+
{ name: 'John', email: 'john@example.com' }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#get' do
|
9
|
+
before(:each) do
|
10
|
+
subject.add(:people, person_attributes)
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'with a valid id' do
|
14
|
+
it 'returns the record with the specified id' do
|
15
|
+
expect(subject.get(:people, 1)).to eq(person_attributes.merge(id: 1))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with an invalid id' do
|
20
|
+
it 'raises' do
|
21
|
+
expect {
|
22
|
+
subject.get(:people, 11)
|
23
|
+
}.to raise_error(KeyError)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#add' do
|
29
|
+
it 'adds a new record' do
|
30
|
+
expect {
|
31
|
+
subject.add(:people, person_attributes)
|
32
|
+
}.to change { subject.count(:people) }.by(1)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns id of the created record' do
|
36
|
+
expect(subject.add(:people, person_attributes)).to eq(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'adds the id attribute to the created record' do
|
40
|
+
id = subject.add(:people, person_attributes)
|
41
|
+
expect(subject.get(:people, id)[:id]).to eq(id)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'increments the id with each record' do
|
45
|
+
5.times { subject.add(:people, person_attributes) }
|
46
|
+
|
47
|
+
expect(subject.count(:people)).to eq(5)
|
48
|
+
expect(subject.get(:people, 5)).to eq(person_attributes.merge(id: 5))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#change' do
|
53
|
+
before(:each) do
|
54
|
+
subject.add(:people, person_attributes)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'changes an existing record' do
|
58
|
+
expect {
|
59
|
+
subject.change(:people, 1, name: 'Jack')
|
60
|
+
}.to change { subject.get(:people, 1)[:name] }.to('Jack')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#remove' do
|
65
|
+
before(:each) do
|
66
|
+
subject.add(:people, person_attributes)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'removes the record with the specified id' do
|
70
|
+
expect {
|
71
|
+
subject.remove(:people, 1)
|
72
|
+
}.to change { subject.count(:people) }.by(-1)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#migrate' do
|
77
|
+
it 'does nothing' do
|
78
|
+
expect {
|
79
|
+
subject.migrate(:block)
|
80
|
+
}.not_to raise_error
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe YADM::Adapters::Memory::Collection do
|
85
|
+
describe '#send_query' do
|
86
|
+
let(:criteria) do
|
87
|
+
YADM::Criteria.new(
|
88
|
+
condition: :some_condition,
|
89
|
+
order: :some_order,
|
90
|
+
limit: :some_limit
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
let(:query) { double('Query', criteria: criteria, arguments: {}) }
|
95
|
+
|
96
|
+
let(:records) { %i(first second third fourth) }
|
97
|
+
let(:filtered_records) { %i(first third fourth) }
|
98
|
+
let(:ordered_records) { %i(third first fourth) }
|
99
|
+
let(:limited_records) { %i(third first) }
|
100
|
+
|
101
|
+
before(:each) do
|
102
|
+
allow(subject).to receive(:all).and_return(records)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'returns the transformed records collection' do
|
106
|
+
expect(subject).to receive(:filter).with(records, :some_condition, {}).and_return(filtered_records)
|
107
|
+
expect(subject).to receive(:order).with(filtered_records, :some_order, {}).and_return(ordered_records)
|
108
|
+
expect(subject).to receive(:limit).with(ordered_records, :some_limit, {}).and_return(limited_records)
|
109
|
+
|
110
|
+
expect(subject.send_query(query)).to eq(limited_records)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#filter' do
|
115
|
+
before(:each) do
|
116
|
+
subject.add(title: 'First post', comments_count: 14)
|
117
|
+
subject.add(title: 'Second post', comments_count: 7)
|
118
|
+
subject.add(title: 'Third post', comments_count: 17)
|
119
|
+
end
|
120
|
+
|
121
|
+
let(:condition) do
|
122
|
+
attribute = build_attribute(:comments_count)
|
123
|
+
expression = build_expression(attribute, :>, 10)
|
124
|
+
|
125
|
+
build_condition(expression)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'returns all records matching the condition' do
|
129
|
+
result = subject.filter(subject.all, condition, {})
|
130
|
+
|
131
|
+
expect(result.count).to eq(2)
|
132
|
+
expect(result.first[:title]).to eq('First post')
|
133
|
+
expect(result.last[:title]).to eq('Third post')
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'with arguments' do
|
137
|
+
let(:condition) do
|
138
|
+
attribute = build_attribute(:comments_count)
|
139
|
+
argument = build_argument(:first, 0)
|
140
|
+
expression = build_expression(attribute, :>, argument)
|
141
|
+
|
142
|
+
build_condition(expression)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'returns all records matching the condition' do
|
146
|
+
result = subject.filter(subject.all, condition, first: [15])
|
147
|
+
|
148
|
+
expect(result.count).to eq(1)
|
149
|
+
expect(result.first[:title]).to eq('Third post')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '#order' do
|
155
|
+
before(:each) do
|
156
|
+
now = Time.now
|
157
|
+
|
158
|
+
subject.add(name: 'Past', created_at: now - 5)
|
159
|
+
subject.add(name: 'Future', created_at: now + 5)
|
160
|
+
subject.add(name: 'Present', created_at: now)
|
161
|
+
subject.add(name: 'Pre-past', created_at: now - 5)
|
162
|
+
end
|
163
|
+
|
164
|
+
let(:order) do
|
165
|
+
created_at = build_attribute(:created_at)
|
166
|
+
timestamp_clause = build_order_clause(:asc, created_at)
|
167
|
+
|
168
|
+
id = build_attribute(:id)
|
169
|
+
id_clause = build_order_clause(:desc, id)
|
170
|
+
|
171
|
+
build_order([timestamp_clause, id_clause])
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'returns records in the specified order' do
|
175
|
+
result = subject.order(subject.all, order, {})
|
176
|
+
names = result.map { |record| record[:name] }
|
177
|
+
|
178
|
+
expect(names).to eq(%w(Pre-past Past Present Future))
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'with arguments' do
|
182
|
+
let(:order) do
|
183
|
+
name = build_attribute(:name)
|
184
|
+
argument = build_argument(:first, 0)
|
185
|
+
expression = build_expression(name, :[], argument)
|
186
|
+
|
187
|
+
clause = build_order_clause(:asc, expression)
|
188
|
+
build_order([clause])
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'returns records in the specified order' do
|
192
|
+
result = subject.order(subject.all, order, first: [1])
|
193
|
+
names = result.map { |record| record[:name] }
|
194
|
+
|
195
|
+
expect(names).to eq(%w(Past Present Pre-past Future))
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe '#limit' do
|
201
|
+
before(:each) do
|
202
|
+
subject.add(name: 'First')
|
203
|
+
subject.add(name: 'Second')
|
204
|
+
subject.add(name: 'Third')
|
205
|
+
end
|
206
|
+
|
207
|
+
let(:limit) { build_limit(2) }
|
208
|
+
|
209
|
+
it 'returns first N records' do
|
210
|
+
result = subject.limit(subject.all, limit, {})
|
211
|
+
|
212
|
+
expect(result.count).to eq(2)
|
213
|
+
expect(result.first[:name]).to eq('First')
|
214
|
+
expect(result.last[:name]).to eq('Second')
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'with arguments' do
|
218
|
+
let(:limit) do
|
219
|
+
argument = build_argument(:first, 0)
|
220
|
+
build_limit(argument)
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'returns first N records' do
|
224
|
+
result = subject.limit(subject.all, limit, first: [1])
|
225
|
+
expect(result.count).to eq(1)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
RSpec.describe YADM::Adapters do
|
2
|
+
describe '.register' do
|
3
|
+
let(:adapter) { double('Adapter') }
|
4
|
+
|
5
|
+
it 'stores a new adapter in the registry' do
|
6
|
+
subject.register(:some_adapter, adapter)
|
7
|
+
expect(subject.fetch(:some_adapter)).to eq(adapter)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.fetch' do
|
12
|
+
context 'with a registered adapter name' do
|
13
|
+
let(:adapter) { double('Adapter') }
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
subject.register(:some_adapter, adapter)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns the corresponding adapter' do
|
20
|
+
expect(subject.fetch(:some_adapter)).to eq(adapter)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with an unknown adapter_name' do
|
25
|
+
it 'raises a NotImplementedError' do
|
26
|
+
expect {
|
27
|
+
subject.fetch(:unknown)
|
28
|
+
}.to raise_error(NotImplementedError, "Adapter `:unknown` isn't registered.")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
RSpec.describe YADM::Criteria::Condition do
|
2
|
+
describe '.merge' do
|
3
|
+
let(:first_condition) do
|
4
|
+
attribute = build_attribute(:age)
|
5
|
+
expression = build_expression(attribute, :>, 20)
|
6
|
+
|
7
|
+
described_class.new(expression)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:second_condition) do
|
11
|
+
attribute = build_attribute(:age)
|
12
|
+
expression = build_expression(attribute, :<, 35)
|
13
|
+
|
14
|
+
described_class.new(expression)
|
15
|
+
end
|
16
|
+
|
17
|
+
subject { described_class.merge(first_condition, second_condition) }
|
18
|
+
|
19
|
+
it 'concatenates the conditions with a logical AND' do
|
20
|
+
expect(subject.expression.receiver).to eq(first_condition.expression)
|
21
|
+
expect(subject.expression.method_name).to eq(:&)
|
22
|
+
expect(subject.expression.arguments).to eq([second_condition.expression])
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with first argument being nil' do
|
26
|
+
let(:first_condition) { nil }
|
27
|
+
|
28
|
+
it 'returns the second argument' do
|
29
|
+
expect(subject).to eq(second_condition)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with second argument being nil' do
|
34
|
+
let(:second_condition) { nil }
|
35
|
+
|
36
|
+
it 'returns the first argument' do
|
37
|
+
expect(subject).to eq(first_condition)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with both arguments being nil' do
|
42
|
+
let(:first_condition) { nil }
|
43
|
+
let(:second_condition) { nil }
|
44
|
+
|
45
|
+
it 'returns nil' do
|
46
|
+
expect(subject).to be_nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
RSpec.describe YADM::Criteria::Limit do
|
2
|
+
describe '.merge' do
|
3
|
+
let(:first_limit) { described_class.new(10) }
|
4
|
+
let(:second_limit) { described_class.new(15) }
|
5
|
+
|
6
|
+
subject { described_class.merge(first_limit, second_limit) }
|
7
|
+
|
8
|
+
it 'respects the last value' do
|
9
|
+
expect(subject.limit).to eq(15)
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'with second argument having a nil limit' do
|
13
|
+
let(:second_limit) { described_class.new(nil) }
|
14
|
+
|
15
|
+
it 'returns nil' do
|
16
|
+
expect(subject).to be_nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with first argument being nil' do
|
21
|
+
let(:first_limit) { nil }
|
22
|
+
|
23
|
+
it 'returns the second argument' do
|
24
|
+
expect(subject).to eq(second_limit)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with second argument being nil' do
|
29
|
+
let(:second_limit) { nil }
|
30
|
+
|
31
|
+
it 'returns the first argument' do
|
32
|
+
expect(subject).to eq(first_limit)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with both arguments being nil' do
|
37
|
+
let(:first_limit) { nil }
|
38
|
+
let(:second_limit) { nil }
|
39
|
+
|
40
|
+
it 'returns nil' do
|
41
|
+
expect(subject).to be_nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
RSpec.describe YADM::Criteria::Order do
|
2
|
+
describe '.merge' do
|
3
|
+
let(:first_order) do
|
4
|
+
attribute = build_attribute(:last_name)
|
5
|
+
clause = build_order_clause(:asc, attribute)
|
6
|
+
|
7
|
+
described_class.new([clause])
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:second_order) do
|
11
|
+
attribute = build_attribute(:first_name)
|
12
|
+
clause = build_order_clause(:asc, attribute)
|
13
|
+
|
14
|
+
described_class.new([clause])
|
15
|
+
end
|
16
|
+
|
17
|
+
subject { described_class.merge(first_order, second_order) }
|
18
|
+
|
19
|
+
it 'sums up the clauses' do
|
20
|
+
expect(subject.clauses.count).to eq(2)
|
21
|
+
expect(subject.clauses.first).to eq(first_order.clauses.first)
|
22
|
+
expect(subject.clauses.last).to eq(second_order.clauses.first)
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with first argument being nil' do
|
26
|
+
let(:first_order) { nil }
|
27
|
+
|
28
|
+
it 'returns the second argument' do
|
29
|
+
expect(subject).to eq(second_order)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with second argument being nil' do
|
34
|
+
let(:second_order) { nil }
|
35
|
+
|
36
|
+
it 'returns the first argument' do
|
37
|
+
expect(subject).to eq(first_order)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with both arguments being nil' do
|
42
|
+
let(:first_order) { nil }
|
43
|
+
let(:second_order) { nil }
|
44
|
+
|
45
|
+
it 'returns nil' do
|
46
|
+
expect(subject).to be_nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
RSpec.describe YADM::CriteriaParser::ExpressionParser do
|
2
|
+
subject { described_class.new(block) }
|
3
|
+
|
4
|
+
describe '#result' do
|
5
|
+
let(:result) { subject.result }
|
6
|
+
|
7
|
+
context 'with a simple block' do
|
8
|
+
let(:block) do
|
9
|
+
proc do
|
10
|
+
age > 12
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:expected_expression) do
|
15
|
+
build_expression(build_attribute(:age), :>, 12)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns a Criteria::Expression' do
|
19
|
+
expect(result).to eq(expected_expression)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'with a nested expression in the block' do
|
24
|
+
let(:block) do
|
25
|
+
proc do
|
26
|
+
(age >= 18) & (age <= 27) & (sex == 'male')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:expected_expression) do
|
31
|
+
build_expression(
|
32
|
+
build_expression(
|
33
|
+
build_expression(build_attribute(:age), :>=, 18),
|
34
|
+
:&,
|
35
|
+
build_expression(build_attribute(:age), :<=, 27)
|
36
|
+
),
|
37
|
+
:&,
|
38
|
+
build_expression(build_attribute(:sex), :==, 'male')
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns a nested Criteria::Expression' do
|
43
|
+
expect(result).to eq(expected_expression)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|