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,55 @@
|
|
1
|
+
RSpec.describe YADM::CriteriaParser do
|
2
|
+
describe '.parse' do
|
3
|
+
context 'without arguments' do
|
4
|
+
let(:block) do
|
5
|
+
proc do
|
6
|
+
with { age > 12 }.descending_by { age }.ascending_by { id }.first(5)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:expected_criteria) do
|
11
|
+
build_criteria(
|
12
|
+
condition: build_condition(
|
13
|
+
build_expression(build_attribute(:age), :>, 12)
|
14
|
+
),
|
15
|
+
order: build_order(
|
16
|
+
[
|
17
|
+
build_order_clause(:desc, build_attribute(:age)),
|
18
|
+
build_order_clause(:asc, build_attribute(:id))
|
19
|
+
]
|
20
|
+
),
|
21
|
+
limit: build_limit(5)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns the parsed YADM::Criteria' do
|
26
|
+
expect(described_class.parse(block, :first)).to eq(expected_criteria)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with arguments' do
|
31
|
+
let(:block) do
|
32
|
+
proc do |min_age, limit|
|
33
|
+
with { age > min_age }.first(limit)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:expected_criteria) do
|
38
|
+
build_criteria(
|
39
|
+
condition: build_condition(
|
40
|
+
build_expression(
|
41
|
+
build_attribute(:age),
|
42
|
+
:>,
|
43
|
+
build_argument(:first, 0)
|
44
|
+
)
|
45
|
+
),
|
46
|
+
limit: build_limit(build_argument(:first, 1))
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns the parsed YADM::Criteria' do
|
51
|
+
expect(described_class.parse(block, :first)).to eq(expected_criteria)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
RSpec.describe YADM::Criteria do
|
2
|
+
describe '#merge' do
|
3
|
+
subject do
|
4
|
+
described_class.new(
|
5
|
+
condition: :first_condition,
|
6
|
+
order: :first_order,
|
7
|
+
limit: :first_limit
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:other_criteria) do
|
12
|
+
described_class.new(
|
13
|
+
condition: :second_condition,
|
14
|
+
order: :second_order,
|
15
|
+
limit: :second_limit
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
allow(YADM::Criteria::Condition).to receive(:merge).and_return(:third_condition)
|
21
|
+
allow(YADM::Criteria::Order).to receive(:merge).and_return(:third_order)
|
22
|
+
allow(YADM::Criteria::Limit).to receive(:merge).and_return(:third_limit)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'merges conditions' do
|
26
|
+
expect(YADM::Criteria::Condition).to receive(:merge).with(:first_condition, :second_condition)
|
27
|
+
expect(subject.merge(other_criteria).condition).to eq(:third_condition)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'merges orders' do
|
31
|
+
expect(YADM::Criteria::Order).to receive(:merge).with(:first_order, :second_order)
|
32
|
+
expect(subject.merge(other_criteria).order).to eq(:third_order)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'merges limits' do
|
36
|
+
expect(YADM::Criteria::Limit).to receive(:merge).with(:first_limit, :second_limit)
|
37
|
+
expect(subject.merge(other_criteria).limit).to eq(:third_limit)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
RSpec.describe YADM::Entity do
|
2
|
+
let(:entity_class) do
|
3
|
+
Class.new do
|
4
|
+
include YADM::Entity
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.attributes' do
|
9
|
+
before(:each) do
|
10
|
+
entity_class.class_eval do
|
11
|
+
attributes :name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:entity) { entity_class.new(id: 1, name: 'John') }
|
16
|
+
|
17
|
+
it 'defines a constructor that takes specified attributes' do
|
18
|
+
expect(entity.id).to eq(1)
|
19
|
+
expect(entity.name).to eq('John')
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'defines accessors' do
|
23
|
+
entity.name = 'Jack'
|
24
|
+
expect(entity.name).to eq('Jack')
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with `readonly` option' do
|
28
|
+
before(:each) do
|
29
|
+
entity_class.class_eval do
|
30
|
+
attribute :password, readonly: true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:entity) { entity_class.new(id: 1, name: 'John', password: 'secret') }
|
35
|
+
|
36
|
+
it 'defines just a reader' do
|
37
|
+
expect(entity.password).to eq('secret')
|
38
|
+
|
39
|
+
expect {
|
40
|
+
entity.password = 'hello'
|
41
|
+
}.to raise_error(NoMethodError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'with string parameters' do
|
46
|
+
before(:each) do
|
47
|
+
entity_class.class_eval do
|
48
|
+
attribute 'email'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:entity) do
|
53
|
+
entity_class.new(id: 1, name: 'John', email: 'john@example.com')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'behaves the same as with symbol parameters' do
|
57
|
+
expect(entity.email).to eq('john@example.com')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with string keys in contructor' do
|
62
|
+
let(:entity) { entity_class.new('id' => 1, 'name' => 'John') }
|
63
|
+
|
64
|
+
it 'behaves the same as with symbol keys' do
|
65
|
+
expect(entity.id).to eq(1)
|
66
|
+
expect(entity.name).to eq('John')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'without arguments' do
|
71
|
+
it 'returns all attribute names' do
|
72
|
+
expect(entity_class.attributes).to eq([:id, :name].to_set)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'yadm/adapters/memory'
|
2
|
+
|
3
|
+
RSpec.describe YADM::IdentityMap do
|
4
|
+
let(:data_source) { YADM::Adapters::Memory.new }
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
data_source.add(:people, name: 'John')
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { YADM::IdentityMap.new(data_source) }
|
11
|
+
|
12
|
+
describe '#get' do
|
13
|
+
context 'without a record in map' do
|
14
|
+
it 'gets the record from the data source' do
|
15
|
+
expect(subject.get(:people, 1)).to eq(id: 1, name: 'John')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with a record in map' do
|
20
|
+
before(:each) do
|
21
|
+
subject.get(:people, 1)
|
22
|
+
data_source.change(:people, 1, name: 'Jack')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'gets the record from the map' do
|
26
|
+
expect(subject.get(:people, 1)).to eq(id: 1, name: 'John')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#add' do
|
32
|
+
it 'saves the record in the data source' do
|
33
|
+
subject.add(:people, name: 'Jack')
|
34
|
+
|
35
|
+
expect(data_source.get(:people, 2)[:name]).to eq('Jack')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'saves the record in the map' do
|
39
|
+
subject.add(:people, name: 'Jack')
|
40
|
+
|
41
|
+
expect(data_source).not_to receive(:get)
|
42
|
+
expect(subject.get(:people, 2)[:id]).to eq(2)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns the id of the added record' do
|
46
|
+
expect(subject.add(:people, name: 'Jack')).to eq(2)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#change' do
|
51
|
+
context 'without a record in map' do
|
52
|
+
before(:each) do
|
53
|
+
subject.change(:people, 1, name: 'David')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'changes the record in the data source' do
|
57
|
+
expect(data_source.get(:people, 1)[:name]).to eq('David')
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'adds the record to the map' do
|
61
|
+
expect(data_source).not_to receive(:get)
|
62
|
+
expect(subject.get(:people, 1)[:id]).to eq(1)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'with a record in map' do
|
67
|
+
before(:each) do
|
68
|
+
subject.get(:people, 1)
|
69
|
+
subject.change(:people, 1, name: 'David')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'updates the record in the map' do
|
73
|
+
expect(data_source).not_to receive(:get)
|
74
|
+
expect(subject.get(:people, 1)[:name]).to eq('David')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#remove' do
|
80
|
+
it 'removes the record from the data source' do
|
81
|
+
subject.remove(:people, 1)
|
82
|
+
expect(data_source.count(:people)).to be_zero
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with a record in map' do
|
86
|
+
before(:each) do
|
87
|
+
subject.get(:people, 1)
|
88
|
+
subject.remove(:people, 1)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'removes the record from the map' do
|
92
|
+
expect(data_source).to receive(:get).with(:people, 1).and_call_original
|
93
|
+
|
94
|
+
expect {
|
95
|
+
subject.get(:people, 1)
|
96
|
+
}.to raise_error(KeyError)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#count' do
|
102
|
+
it 'passes the method call to the data source' do
|
103
|
+
expect(subject.count(:people)).to eq(1)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#send_query' do
|
108
|
+
let(:query) { double('Query') }
|
109
|
+
let(:data) { double('Data') }
|
110
|
+
|
111
|
+
before(:each) do
|
112
|
+
allow(data_source).to receive(:send_query).with(:people, query).and_return(data)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'passes the method call to the data source' do
|
116
|
+
expect(subject.send_query(:people, query)).to eq(data)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe '#migrate' do
|
121
|
+
let(:block) { double('Block') }
|
122
|
+
|
123
|
+
it 'passes the method call to the data source' do
|
124
|
+
expect(data_source).to receive(:migrate).with(block)
|
125
|
+
subject.migrate(block)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
RSpec.describe YADM::Mapper do
|
2
|
+
let(:identity_map) { YADM::IdentityMap.new(nil) }
|
3
|
+
|
4
|
+
before(:each) do
|
5
|
+
YADM.data_sources[:source] = identity_map
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.repository' do
|
9
|
+
let(:repository) do
|
10
|
+
Class.new do
|
11
|
+
include YADM::Repository
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'adds a new mapping for the given repository' do
|
16
|
+
subject.repository(repository) do
|
17
|
+
data_source :source
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(subject.mapping_for(repository).data_source).to eq(identity_map)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
RSpec.describe YADM::Mapping::Attribute do
|
2
|
+
context 'with an Integer type' do
|
3
|
+
subject { described_class.new(Integer) }
|
4
|
+
|
5
|
+
describe '#coerce' do
|
6
|
+
it 'coerces the value to integer' do
|
7
|
+
expect(subject.coerce('123')).to eq(123)
|
8
|
+
expect(subject.coerce(123)).to eq(123)
|
9
|
+
expect(subject.coerce('123abc')).to eq(123)
|
10
|
+
expect(subject.coerce('abc')).to eq(0)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'leaves nil as is' do
|
14
|
+
expect(subject.coerce(nil)).to be_nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with a String type' do
|
20
|
+
subject { described_class.new(String) }
|
21
|
+
|
22
|
+
describe '#coerce' do
|
23
|
+
it 'coerces the value to String' do
|
24
|
+
expect(subject.coerce(123)).to eq('123')
|
25
|
+
expect(subject.coerce('123')).to eq('123')
|
26
|
+
expect(subject.coerce('123abc')).to eq('123abc')
|
27
|
+
expect(subject.coerce(:abc)).to eq('abc')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'leaves nil as is' do
|
31
|
+
expect(subject.coerce(nil)).to be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'yadm/adapters/memory'
|
2
|
+
|
3
|
+
RSpec.describe YADM::Mapping do
|
4
|
+
let(:data_source) { YADM::Adapters::Memory.new }
|
5
|
+
let(:identity_map) { YADM::IdentityMap.new(data_source) }
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
data_source.add(:people, name: 'John', age: '35')
|
9
|
+
YADM.data_sources[:source] = identity_map
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:people_mapping) do
|
13
|
+
YADM::Mapping.new do
|
14
|
+
data_source :source
|
15
|
+
collection :people
|
16
|
+
|
17
|
+
attribute :id, Integer
|
18
|
+
attribute :name, String
|
19
|
+
attribute :age, Integer
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#get' do
|
24
|
+
it 'returns a hash with converted attributes' do
|
25
|
+
hash = people_mapping.get(1)
|
26
|
+
|
27
|
+
expect(hash[:id]).to eq(1)
|
28
|
+
expect(hash[:name]).to eq('John')
|
29
|
+
expect(hash[:age]).to eq(35)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#add' do
|
34
|
+
it 'passes the method call to the data source' do
|
35
|
+
people_mapping.add(name: 'Jack', age: 27)
|
36
|
+
expect(people_mapping.get(2)[:name]).to eq('Jack')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#change' do
|
41
|
+
it 'passes the method call to the data source' do
|
42
|
+
people_mapping.change(1, name: 'Johnny')
|
43
|
+
expect(people_mapping.get(1)[:name]).to eq('Johnny')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#remove' do
|
48
|
+
it 'passes the method call to the data source' do
|
49
|
+
people_mapping.remove(1)
|
50
|
+
expect { people_mapping.get(1) }.to raise_error(KeyError)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#count' do
|
55
|
+
it 'passes the method call to the data source' do
|
56
|
+
expect(people_mapping.count).to eq(1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#send_query' do
|
61
|
+
let(:query) { double('Query') }
|
62
|
+
|
63
|
+
let(:data) do
|
64
|
+
[
|
65
|
+
{ id: 1, name: 'John', age: '31' },
|
66
|
+
{ id: 2, name: 'Jack', age: '42' }
|
67
|
+
]
|
68
|
+
end
|
69
|
+
|
70
|
+
before(:each) do
|
71
|
+
allow(identity_map).to receive(:send_query).with(:people, query).and_return(data)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'gets the data from the data source and coerces it' do
|
75
|
+
result = people_mapping.send_query(query)
|
76
|
+
|
77
|
+
expect(result.first[:name]).to eq('John')
|
78
|
+
expect(result.first[:age]).to eq(31)
|
79
|
+
expect(result.last[:name]).to eq('Jack')
|
80
|
+
expect(result.last[:age]).to eq(42)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe YADM::Mapping::DSL do
|
85
|
+
describe '#data_source' do
|
86
|
+
let(:mapping) do
|
87
|
+
YADM::Mapping.new do
|
88
|
+
data_source :source
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'specifies the data source identifier' do
|
93
|
+
expect(mapping.data_source).to eq(identity_map)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#collection' do
|
98
|
+
let(:mapping) do
|
99
|
+
YADM::Mapping.new do
|
100
|
+
collection :people
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'specifies the collection' do
|
105
|
+
expect(mapping.collection).to eq(:people)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#attribute' do
|
110
|
+
let(:mapping) do
|
111
|
+
YADM::Mapping.new do
|
112
|
+
attribute :id, Integer
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'adds a new attribute' do
|
117
|
+
expect(mapping.attributes).to have_key(:id)
|
118
|
+
expect(mapping.attributes[:id]).to eq(YADM::Mapping::Attribute.new(Integer))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|