locomotivecms_models 0.0.1.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +4 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +19 -0
  7. data/Gemfile.lock +53 -0
  8. data/README.md +29 -0
  9. data/Rakefile +12 -0
  10. data/example.rb +76 -0
  11. data/lib/locomotive/adapters/memory/command.rb +35 -0
  12. data/lib/locomotive/adapters/memory/condition.rb +98 -0
  13. data/lib/locomotive/adapters/memory/dataset.rb +75 -0
  14. data/lib/locomotive/adapters/memory/query.rb +106 -0
  15. data/lib/locomotive/adapters/memory/wrapper.rb +23 -0
  16. data/lib/locomotive/adapters/memory_adapter.rb +72 -0
  17. data/lib/locomotive/core_ext.rb +2 -0
  18. data/lib/locomotive/core_ext/hash.rb +44 -0
  19. data/lib/locomotive/core_ext/string.rb +13 -0
  20. data/lib/locomotive/decorators.rb +1 -0
  21. data/lib/locomotive/decorators/i18n_decorator.rb +45 -0
  22. data/lib/locomotive/entity.rb +28 -0
  23. data/lib/locomotive/fields/i18n_field.rb +62 -0
  24. data/lib/locomotive/mapper.rb +47 -0
  25. data/lib/locomotive/mapping.rb +16 -0
  26. data/lib/locomotive/mapping/coercer.rb +64 -0
  27. data/lib/locomotive/mapping/collection.rb +68 -0
  28. data/lib/locomotive/mapping/dereferencer.rb +40 -0
  29. data/lib/locomotive/mapping/referencer.rb +66 -0
  30. data/lib/locomotive/mapping/virtual_proxy.rb +30 -0
  31. data/lib/locomotive/models.rb +57 -0
  32. data/lib/locomotive/models/configuration.rb +13 -0
  33. data/lib/locomotive/models/version.rb +10 -0
  34. data/lib/locomotive/repository.rb +51 -0
  35. data/locomotive_models.gemspec +28 -0
  36. data/non_persisted_example.rb +70 -0
  37. data/presenter_example.rb +49 -0
  38. data/relation_example.rb +119 -0
  39. data/spec/fixtures/example_entities.rb +23 -0
  40. data/spec/fixtures/example_mapper.rb +40 -0
  41. data/spec/fixtures/example_repositories.rb +19 -0
  42. data/spec/integration/criteria_spec.rb +67 -0
  43. data/spec/integration/persistence_entity_spec.rb +75 -0
  44. data/spec/integration/relations_spec.rb +114 -0
  45. data/spec/spec_helper.rb +22 -0
  46. data/spec/support/adapters/memory.rb +39 -0
  47. data/spec/unit/adapters/memory/condition_spec.rb +120 -0
  48. data/spec/unit/adapters/memory/dataset_spec.rb +71 -0
  49. data/spec/unit/adapters/memory/query_spec.rb +69 -0
  50. data/spec/unit/decorators/i18n_decorator_spec.rb +133 -0
  51. data/spec/unit/entity_spec.rb +30 -0
  52. data/spec/unit/fields/i18n_field_spec.rb +60 -0
  53. data/spec/unit/mapper_spec.rb +60 -0
  54. data/spec/unit/mapping/coercer_spec.rb +30 -0
  55. data/spec/unit/mapping/collection_spec.rb +32 -0
  56. data/spec/unit/models_spec.rb +14 -0
  57. metadata +190 -0
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Locomotive::Adapters::Memory::Dataset do
4
+
5
+ let(:john) do
6
+ {
7
+ firstname: 'John',
8
+ lastname: 'Doe',
9
+ email: 'john@example.com',
10
+ age: 24
11
+ }
12
+ end
13
+
14
+ let(:jane) do
15
+ {
16
+ firstname: 'Jane',
17
+ lastname: 'Doe',
18
+ email: 'jane@example.com',
19
+ age: 20
20
+ }
21
+ end
22
+
23
+ let(:alex) do
24
+ {
25
+ firstname: 'Alex',
26
+ lastname: 'Turam',
27
+ email: 'alex@example.com',
28
+ age: 26
29
+ }
30
+ end
31
+
32
+ subject { Locomotive::Adapters::Memory::Dataset.new(:foo) } #(loader) }
33
+
34
+ before do
35
+ [john.to_hash, jane.to_hash, alex.to_hash].each do |record|
36
+ subject.insert record
37
+ end
38
+ end
39
+
40
+ describe '#all' do
41
+ its(:all) { should eq [john.to_hash, jane.to_hash, alex.to_hash] }
42
+ end
43
+
44
+ describe '#find' do
45
+ specify do
46
+ expect(subject.find(john[:id])).to eq(john.to_hash)
47
+ end
48
+ end
49
+
50
+ describe '#update' do
51
+ before do
52
+ subject.update(jane.to_hash.merge(lastname: 'birkin'))
53
+ end
54
+
55
+ specify do
56
+ expect(subject.find(jane[:id]).fetch(:lastname)).to eq('birkin')
57
+ end
58
+ end
59
+
60
+ describe '#exists?' do
61
+ let(:dataset) { Locomotive::Adapters::Memory::Dataset.new(:dummy) }
62
+ before do
63
+ dataset.instance_variable_set('@records', { 1 => 'Record 1', 2 => 'Record 2' })
64
+ end
65
+
66
+ it { dataset.exists?(2).should be_true }
67
+ it { dataset.exists?(3).should be_false }
68
+ it { dataset.exists?(nil).should be_false }
69
+
70
+ end
71
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ module Locomotive
4
+ module Adapters
5
+ module Memory
6
+
7
+ describe Query do
8
+ let(:entry_1) {{ name: 'foo', id: 1 }}
9
+ let(:entry_2) {{ name: 'bar', id: 2 }}
10
+ let(:entry_3) {{ name: 'zone', id: 3 }}
11
+ let(:records) do
12
+ { 1 => entry_1, 2 => entry_2, 3 => entry_3 }
13
+ end
14
+ let(:collection) { :foo }
15
+ let(:dataset) do
16
+ Dataset.new(collection).tap do |dataset|
17
+ dataset.stub records: records
18
+ end
19
+ end
20
+ let(:locale) { :en }
21
+
22
+ describe '#limited' do
23
+ specify do
24
+ expect(
25
+ Query.new(dataset, locale) do
26
+ limit(1)
27
+ end.all
28
+ ).to eq([entry_1])
29
+ end
30
+ end
31
+
32
+ describe '#order_by' do
33
+
34
+ context 'asc' do
35
+ specify do
36
+ expect(
37
+ Query.new(dataset, locale) do
38
+ order_by('name asc')
39
+ end.all.map(&:id)
40
+ ).to eq([2, 1, 3])
41
+ end
42
+ end
43
+
44
+ context 'desc' do
45
+ specify do
46
+ expect(
47
+ Query.new(dataset, locale) do
48
+ order_by('name desc')
49
+ end.all.map(&:id)
50
+ ).to eq([3, 1, 2])
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#where' do
56
+ specify do
57
+ expect(
58
+ Query.new(dataset, locale) do
59
+ where('name.eq' => 'foo').
60
+ where('id.lt' => 2)
61
+ end.all.map(&:id)
62
+ ).to eq([1])
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+ require 'locomotive/decorators'
3
+
4
+ describe Locomotive::Decorators::I18nDecorator do
5
+
6
+ class FakeEntity
7
+ include Locomotive::Entity
8
+ attributes :name, :description
9
+ end
10
+
11
+ describe 'initialization' do
12
+ context 'with instance' do
13
+ subject do
14
+ Locomotive::Decorators::I18nDecorator.new(FakeEntity.new)
15
+ end
16
+
17
+ it 'accepts an entity as parameter' do
18
+ subject.should be_kind_of Locomotive::Decorators::I18nDecorator
19
+ end
20
+
21
+ it 'responds to all entity attributes' do
22
+ subject.should respond_to :name
23
+ subject.should respond_to :description
24
+ subject.should respond_to :name=
25
+ subject.should respond_to :description=
26
+ end
27
+ end
28
+
29
+ context 'with resultset' do
30
+ let(:entities) {
31
+ [ FakeEntity.new, FakeEntity.new ]
32
+ }
33
+ it 'decorates each entity' do
34
+ Locomotive::Decorators::I18nDecorator.decorate(entities).each do |entity|
35
+ entity.should be_kind_of(Locomotive::Decorators::I18nDecorator)
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ describe 'fields on single instance' do
42
+
43
+ subject { decorator.name }
44
+ let(:decorator) { Locomotive::Decorators::I18nDecorator.new(entity) }
45
+ let(:entity) { FakeEntity.new(name: name) }
46
+
47
+ context 'when field is not an i18n field' do
48
+ let(:name) { 'blah' }
49
+ it { should eql 'blah' }
50
+ end
51
+
52
+ context 'when field is an i18n field' do
53
+ let(:name) { { en: 'blah', fr: 'bla' } }
54
+ context 'no current locale' do
55
+ it 'displays object for inspection' do
56
+ subject.should eql '#<I18nField: @i18n_values=>{:en=>blah,:fr=>bla}>'
57
+ end
58
+ end
59
+
60
+ context 'with current locale' do
61
+ before { decorator.current_locale = :fr }
62
+ it { should eql 'bla' }
63
+ end
64
+
65
+ context 'passing current locale in constructor' do
66
+ let(:decorator) { Locomotive::Decorators::I18nDecorator.new(entity, :fr) }
67
+ it { should eql 'bla' }
68
+ end
69
+ end
70
+ end
71
+
72
+ describe 'fields on resultset' do
73
+ subject { decorated_set.first.name }
74
+ let(:entities) {
75
+ [ FakeEntity.new(name: { en: 'blah', es: '', fr: 'bla'}),
76
+ FakeEntity.new
77
+ ]
78
+ }
79
+ context 'when no locale is passed in constructor' do
80
+ let(:decorated_set) do
81
+ Locomotive::Decorators::I18nDecorator.decorate(entities)
82
+ end
83
+ it 'displays object for inspection' do
84
+ subject.should eq '#<I18nField: @i18n_values=>{:en=>blah,:es=>,:fr=>bla}>'
85
+ end
86
+ end
87
+
88
+ context 'when locale is passed in the constructor' do
89
+ let(:decorated_set) do
90
+ Locomotive::Decorators::I18nDecorator.decorate(entities, :fr)
91
+ end
92
+ it { should eq 'bla' }
93
+ end
94
+ end
95
+
96
+ context 'Fallbacks' do
97
+ subject { decorator.name }
98
+ let(:decorator) { Locomotive::Decorators::I18nDecorator.new(entity, locale) }
99
+ let(:entity) { FakeEntity.new(name: name) }
100
+ let(:name) { { en: 'blah', fr: 'bla', es: '' } }
101
+
102
+ context 'when passed locale does not exist' do
103
+ let(:locale) { :wk }
104
+ context 'default fallback' do
105
+ it { should be_nil }
106
+ end
107
+ context 'custom fallback' do
108
+ before do
109
+ decorator.on_no_locale = Proc.new { |field, locale| "Translation #{locale} is not supported" }
110
+ end
111
+ it { should eq 'Translation wk is not supported' }
112
+ end
113
+ end
114
+
115
+ context 'when entity has empty locale value' do
116
+ let(:locale) { :es }
117
+
118
+ context 'default fallback' do
119
+ it 'returns the first locale in the field' do
120
+ subject.should eq 'blah'
121
+ end
122
+ end
123
+
124
+ context 'custom fallback' do
125
+ before do
126
+ decorator.on_empty_locale = Proc.new { |field, locale| "#{field[:en]} [untranslated #{locale}]" }
127
+ end
128
+ it { should eq 'blah [untranslated es]' }
129
+ end
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Locomotive::Entity do
4
+
5
+ class FakeEntity
6
+ include Locomotive::Entity
7
+ attributes :name, :description
8
+ end
9
+
10
+ context 'simplest instanciation' do
11
+ subject { FakeEntity.new }
12
+ it { should be_a FakeEntity }
13
+ it { should respond_to :name }
14
+ it { should respond_to :name= }
15
+ end
16
+
17
+ context 'with empty hash' do
18
+ subject { FakeEntity.new({}) }
19
+ it { should be_a FakeEntity }
20
+ its(:name) { should be_nil }
21
+ end
22
+
23
+ context 'with hash and data in it' do
24
+ subject { FakeEntity.new({name: 'John'}) }
25
+ it { should be_a FakeEntity }
26
+ its(:name) { should eq 'John' }
27
+ its(:description) { should be_nil }
28
+ end
29
+
30
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Locomotive::Fields::I18nField do
4
+
5
+ subject { Locomotive::Fields::I18nField.new(values) }
6
+ let(:values) { { wk: '', en: 'My value', es: 'Mi valor' } }
7
+ describe '#to_s' do
8
+ context 'no params' do
9
+ it 'displays object inspection' do
10
+ subject.to_s.should eql '#<I18nField: @i18n_values=>{:wk=>,:en=>My value,:es=>Mi valor}>'
11
+ end
12
+ end
13
+ context 'locale as param' do
14
+ it { subject.to_s(:en).should eq 'My value' }
15
+ end
16
+ context 'non existing locale as param' do
17
+ it 'raises an error' do
18
+ expect{ subject.to_s(:kz) }.to raise_error(Locomotive::Fields::I18nField::NoLocaleError)
19
+ end
20
+ end
21
+ context 'empty translation' do
22
+ it 'raises an error' do
23
+ expect{ subject.to_s(:wk) }.to raise_error(Locomotive::Fields::I18nField::EmptyLocaleError)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe 'alternative access' do
29
+ it { subject.en.should eq 'My value' }
30
+ it { subject[:en].should eq 'My value' }
31
+ end
32
+
33
+ describe 'adding values' do
34
+ subject { Locomotive::Fields::I18nField.new(values) }
35
+ let(:values) { { en: 'My value' } }
36
+ before { subject << new_values }
37
+
38
+ context 'hash with string keys' do
39
+ let(:new_values) { {'fr' => 'Valeur'} }
40
+ it { subject.fr.should eq 'Valeur' }
41
+ end
42
+
43
+ context 'hash with symbol keys' do
44
+ let(:new_values) { {fr: 'Valeur'} }
45
+ it { subject.fr.should eq 'Valeur' }
46
+ end
47
+ end
48
+
49
+ describe 'iterating over values' do
50
+ subject { Locomotive::Fields::I18nField.new(values) }
51
+ let(:values) { { en: 'My value', fr: 'Valeur' } }
52
+ it 'allow to iterate over strings' do
53
+ test_val = []
54
+ subject.each do |locale, value|
55
+ test_val << subject[locale]
56
+ end
57
+ test_val.join.should eq 'My valueValeur'
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ # require 'fixtures/example_entities'
3
+
4
+ module Locomotive
5
+
6
+ describe Mapper do
7
+
8
+ class Dummy
9
+ include Entity
10
+ attributes :foo
11
+ end
12
+
13
+ class DummiesRepository
14
+ include Repository
15
+ end
16
+
17
+ let(:adapter) do
18
+ Locomotive::Adapters::MemoryAdapter
19
+ end
20
+
21
+ let!(:mapper) do
22
+ Mapper.new(adapter) do
23
+ collection :dummies do
24
+ entity Dummy
25
+ repository DummiesRepository
26
+
27
+ attribute :foo
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '' do
33
+ before do mapper end
34
+ specify do
35
+ expect(Models.mapper).to be_a Mapper
36
+ end
37
+ specify do
38
+ expect(Models.mapper.collection(:dummies)).to be_a Mapping::Collection
39
+ end
40
+ specify do
41
+ expect(Models.mapper.collection(:dummies).repository).to be_a Locomotive::DummiesRepository
42
+ end
43
+
44
+ context '' do
45
+ let(:dummy_entity) do
46
+ Dummy.new({ foo: 'bar' })
47
+ end
48
+ let(:repository) do
49
+ Models.mapper.collection(:dummies).repository
50
+ end
51
+ before do
52
+ repository.create dummy_entity
53
+ end
54
+ specify do
55
+ expect(repository.find(dummy_entity.id).foo).to eq('bar')
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module Locomotive
4
+ describe Mapping::Coercer do
5
+ include_context 'memory'
6
+
7
+ let(:coercer) { Mapping::Coercer.new(mapper.collection(:products)) }
8
+
9
+ describe '#to_record' do
10
+ let(:entity) { Example::Product.new entity_hash }
11
+
12
+ context 'with locale' do
13
+ subject { coercer.to_record(entity)[:title] }
14
+
15
+ context 'when i18n field is localized' do
16
+ let(:entity_hash) {{ title: { en: 'magic vacuum', fr: 'aspirateur magique' }}}
17
+
18
+ it { should eq en: 'magic vacuum' , fr: 'aspirateur magique' }
19
+ end
20
+
21
+ end
22
+
23
+ context 'nil value' do
24
+ subject { coercer.to_record(entity)[:title] }
25
+ let(:entity_hash) { { title: nil } }
26
+ it { should eq({}) }
27
+ end
28
+ end
29
+ end
30
+ end