yadm 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|