modelish 0.3.0 → 1.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +3 -1
- data/.ruby-version +1 -1
- data/.travis.yml +14 -0
- data/Appraisals +11 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +5 -1
- data/README.md +5 -0
- data/gemfiles/hashie_1.gemfile +7 -0
- data/gemfiles/hashie_2.gemfile +7 -0
- data/gemfiles/hashie_3.gemfile +7 -0
- data/lib/modelish/base.rb +108 -72
- data/lib/modelish/configuration.rb +9 -8
- data/lib/modelish/property_translations.rb +23 -11
- data/lib/modelish/property_types.rb +67 -50
- data/lib/modelish/validations.rb +70 -51
- data/lib/modelish/version.rb +3 -1
- data/lib/modelish.rb +3 -1
- data/modelish.gemspec +20 -15
- data/spec/modelish/base_spec.rb +203 -182
- data/spec/modelish/configuration_spec.rb +26 -19
- data/spec/modelish/property_translations_spec.rb +47 -39
- data/spec/modelish/property_types_spec.rb +53 -45
- data/spec/modelish/validations_spec.rb +296 -256
- data/spec/modelish_spec.rb +5 -3
- data/spec/spec_helper.rb +97 -2
- data/spec/support/property_examples.rb +15 -12
- data/spec/support/typed_property_examples.rb +39 -35
- data/spec/support/unknown_property_examples.rb +6 -4
- data/spec/support/validation_examples.rb +29 -23
- metadata +136 -103
- data/.ruby-gemset +0 -1
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
|
-
describe Modelish::Configuration do
|
5
|
+
RSpec.describe Modelish::Configuration do
|
4
6
|
let(:test_module) do
|
5
7
|
module TestModule
|
6
8
|
extend Modelish::Configuration
|
@@ -11,10 +13,10 @@ describe Modelish::Configuration do
|
|
11
13
|
|
12
14
|
after { test_module.reset }
|
13
15
|
|
14
|
-
it {
|
16
|
+
it { is_expected.to respond_to(:configure) }
|
15
17
|
|
16
18
|
context 'with default configuration' do
|
17
|
-
its(:ignore_unknown_properties) {
|
19
|
+
its(:ignore_unknown_properties) { is_expected.to eq(false) }
|
18
20
|
end
|
19
21
|
|
20
22
|
describe '.configure' do
|
@@ -29,12 +31,12 @@ describe Modelish::Configuration do
|
|
29
31
|
|
30
32
|
context 'when ignore_unknown_properties is true' do
|
31
33
|
let(:ignore_unknown_props) { true }
|
32
|
-
its(:ignore_unknown_properties) {
|
34
|
+
its(:ignore_unknown_properties) { is_expected.to eq(true) }
|
33
35
|
end
|
34
36
|
|
35
37
|
context 'when ignore_unknown_properties is false' do
|
36
38
|
let(:ignore_unknown_props) { false }
|
37
|
-
its(:ignore_unknown_properties) {
|
39
|
+
its(:ignore_unknown_properties) { is_expected.to eq(false) }
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
@@ -44,8 +46,9 @@ describe Modelish::Configuration do
|
|
44
46
|
|
45
47
|
before { test_module.configure { |c| c.ignore_unknown_properties = true } }
|
46
48
|
|
47
|
-
it '
|
48
|
-
expect { subject }.to change { test_module.ignore_unknown_properties }
|
49
|
+
it 'resets the value of ignore_unknown_properties' do
|
50
|
+
expect { subject }.to change { test_module.ignore_unknown_properties }
|
51
|
+
.from(true).to(false)
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
@@ -57,24 +60,26 @@ describe Modelish::Configuration do
|
|
57
60
|
context 'when ignore_unknown_properties is true' do
|
58
61
|
let(:ignore_unknown_props) { true }
|
59
62
|
|
60
|
-
it '
|
63
|
+
it 'does not change the setting' do
|
61
64
|
expect { subject }.to_not change { test_module.ignore_unknown_properties }
|
62
65
|
end
|
63
|
-
end
|
66
|
+
end
|
64
67
|
|
65
68
|
context 'when ignore_unknown_properties is false' do
|
66
69
|
let(:ignore_unknown_props) { false }
|
67
70
|
|
68
|
-
it '
|
69
|
-
expect { subject }.to change { test_module.ignore_unknown_properties }
|
71
|
+
it 'changes the setting' do
|
72
|
+
expect { subject }.to change { test_module.ignore_unknown_properties }
|
73
|
+
.from(false).to(true)
|
70
74
|
end
|
71
75
|
end
|
72
76
|
|
73
77
|
context 'when ignore_unknown_properties is nil' do
|
74
78
|
let(:ignore_unknown_props) { nil }
|
75
79
|
|
76
|
-
it '
|
77
|
-
expect { subject }.to change { test_module.ignore_unknown_properties }
|
80
|
+
it 'changes the setting' do
|
81
|
+
expect { subject }.to change { test_module.ignore_unknown_properties }
|
82
|
+
.from(nil).to(true)
|
78
83
|
end
|
79
84
|
end
|
80
85
|
end
|
@@ -87,15 +92,16 @@ describe Modelish::Configuration do
|
|
87
92
|
context 'when ignore_unknown_properties is true' do
|
88
93
|
let(:ignore_unknown_props) { true }
|
89
94
|
|
90
|
-
it '
|
91
|
-
expect { subject }.to change { test_module.ignore_unknown_properties }
|
95
|
+
it 'changes the setting' do
|
96
|
+
expect { subject }.to change { test_module.ignore_unknown_properties }
|
97
|
+
.from(true).to(false)
|
92
98
|
end
|
93
|
-
end
|
99
|
+
end
|
94
100
|
|
95
101
|
context 'when ignore_unknown_properties is false' do
|
96
102
|
let(:ignore_unknown_props) { false }
|
97
103
|
|
98
|
-
it '
|
104
|
+
it 'does not change the setting' do
|
99
105
|
expect { subject }.to_not change { test_module.ignore_unknown_properties }
|
100
106
|
end
|
101
107
|
end
|
@@ -103,8 +109,9 @@ describe Modelish::Configuration do
|
|
103
109
|
context 'when ignore_unknown_properties is nil' do
|
104
110
|
let(:ignore_unknown_props) { nil }
|
105
111
|
|
106
|
-
it '
|
107
|
-
expect { subject }.to change { test_module.ignore_unknown_properties }
|
112
|
+
it 'changes the setting' do
|
113
|
+
expect { subject }.to change { test_module.ignore_unknown_properties }
|
114
|
+
.from(nil).to(false)
|
108
115
|
end
|
109
116
|
end
|
110
117
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
|
-
describe Modelish::PropertyTranslations do
|
4
|
-
let(:model_class) do
|
5
|
-
Class.new do
|
5
|
+
RSpec.describe Modelish::PropertyTranslations do
|
6
|
+
let(:model_class) do
|
7
|
+
Class.new do
|
6
8
|
include Modelish::PropertyTranslations
|
7
9
|
|
8
10
|
attr_accessor :foo_prop, :bar_prop
|
@@ -16,50 +18,53 @@ describe Modelish::PropertyTranslations do
|
|
16
18
|
describe '.translations' do
|
17
19
|
subject { model_class.translations }
|
18
20
|
|
19
|
-
it {
|
20
|
-
it {
|
21
|
+
it { is_expected.to be_a Hash }
|
22
|
+
it { is_expected.to be_empty }
|
21
23
|
end
|
22
24
|
|
23
25
|
describe '.add_property_translation' do
|
24
|
-
subject
|
25
|
-
|
26
|
+
subject(:add_translation) do
|
27
|
+
model_class.add_property_translation(from_name, to_name)
|
28
|
+
end
|
26
29
|
|
27
|
-
context 'when there are no existing translations
|
30
|
+
context 'when there are no existing translations' do
|
28
31
|
context 'with symbolic property names' do
|
29
32
|
let(:from_name) { :my_input_prop }
|
30
33
|
let(:to_name) { :foo_prop }
|
31
34
|
|
32
|
-
it '
|
33
|
-
expect { subject }.to change { model_class.translations[from_name] }
|
35
|
+
it 'maps the input property to the output property' do
|
36
|
+
expect { subject }.to change { model_class.translations[from_name] }
|
37
|
+
.to([to_name])
|
34
38
|
end
|
35
39
|
|
36
|
-
describe
|
40
|
+
describe 'the generated model' do
|
37
41
|
before { add_translation }
|
38
42
|
|
39
43
|
subject { model }
|
40
44
|
|
41
|
-
it {
|
42
|
-
it {
|
43
|
-
|
44
|
-
it { should respond_to("#{to_name}=") }
|
45
|
+
it { is_expected.to respond_to(to_name) }
|
46
|
+
it { is_expected.to_not respond_to(from_name) }
|
47
|
+
it { is_expected.to respond_to("#{to_name}=") }
|
45
48
|
|
46
49
|
let(:value) { 'blah' }
|
47
50
|
|
48
|
-
describe
|
51
|
+
describe 'non-translated mutator' do
|
49
52
|
subject { model.send("#{to_name}=", value) }
|
50
53
|
|
51
|
-
it '
|
52
|
-
expect { subject }.to change { model.send(to_name) }
|
54
|
+
it 'changes the property value' do
|
55
|
+
expect { subject }.to change { model.send(to_name) }
|
56
|
+
.from(nil).to(value)
|
53
57
|
end
|
54
58
|
end
|
55
59
|
|
56
|
-
it {
|
60
|
+
it { is_expected.to respond_to("#{from_name}=") }
|
57
61
|
|
58
|
-
describe
|
62
|
+
describe 'translated mutator' do
|
59
63
|
subject { model.send("#{from_name}=", value) }
|
60
64
|
|
61
|
-
it '
|
62
|
-
expect { subject }.to change { model.send(to_name) }
|
65
|
+
it 'changes the property value' do
|
66
|
+
expect { subject }.to change { model.send(to_name) }
|
67
|
+
.from(nil).to(value)
|
63
68
|
end
|
64
69
|
end
|
65
70
|
end
|
@@ -69,8 +74,11 @@ describe Modelish::PropertyTranslations do
|
|
69
74
|
let(:from_name) { 'my_input_prop' }
|
70
75
|
let(:to_name) { 'foo_prop' }
|
71
76
|
|
72
|
-
it '
|
73
|
-
|
77
|
+
it 'maps the symbolic input property to the symbolic output property' do
|
78
|
+
from_key = from_name.to_sym
|
79
|
+
to_key = to_name.to_sym
|
80
|
+
expect { subject }.to change { model_class.translations[from_key] }
|
81
|
+
.to([to_key])
|
74
82
|
end
|
75
83
|
end
|
76
84
|
end
|
@@ -81,60 +89,60 @@ describe Modelish::PropertyTranslations do
|
|
81
89
|
let(:from_name) { :input_prop }
|
82
90
|
let(:to_name) { :foo_prop }
|
83
91
|
|
84
|
-
it '
|
92
|
+
it 'adds output property to the mapping for the input property' do
|
85
93
|
subject
|
86
|
-
model_class.translations[from_name].
|
87
|
-
|
94
|
+
expect(model_class.translations[from_name]).to include(to_name,
|
95
|
+
other_to_name)
|
88
96
|
end
|
89
97
|
|
90
98
|
describe 'generated model' do
|
91
99
|
before { add_translation }
|
92
100
|
subject { model }
|
93
101
|
|
94
|
-
it {
|
95
|
-
it {
|
96
|
-
it {
|
102
|
+
it { is_expected.to respond_to(other_to_name) }
|
103
|
+
it { is_expected.to respond_to(to_name) }
|
104
|
+
it { is_expected.to_not respond_to(from_name) }
|
97
105
|
|
98
|
-
it {
|
106
|
+
it { is_expected.to respond_to("#{other_to_name}=") }
|
99
107
|
|
100
108
|
let(:value) { 'blah' }
|
101
109
|
|
102
110
|
describe 'non-translated mutator for the existing property' do
|
103
111
|
subject { model.send("#{other_to_name}=", value) }
|
104
112
|
|
105
|
-
it '
|
113
|
+
it 'changes the value for the existing property' do
|
106
114
|
expect { subject }.to change { model.send(other_to_name) }.to(value)
|
107
115
|
end
|
108
116
|
|
109
|
-
it '
|
117
|
+
it 'does not change the value for the new property' do
|
110
118
|
expect { subject }.to_not change { model.send(to_name) }
|
111
119
|
end
|
112
120
|
end
|
113
121
|
|
114
|
-
it {
|
122
|
+
it { is_expected.to respond_to("#{to_name}=") }
|
115
123
|
|
116
124
|
describe 'non-translated mutator for the new property' do
|
117
125
|
subject { model.send("#{to_name}=", value) }
|
118
126
|
|
119
|
-
it '
|
127
|
+
it 'changes the value for the new property' do
|
120
128
|
expect { subject }.to change { model.send(to_name) }.to(value)
|
121
129
|
end
|
122
130
|
|
123
|
-
it '
|
131
|
+
it 'does not change the value for the existing property' do
|
124
132
|
expect { subject }.to_not change { model.send(other_to_name) }
|
125
133
|
end
|
126
134
|
end
|
127
135
|
|
128
|
-
it {
|
136
|
+
it { is_expected.to respond_to("#{from_name}=") }
|
129
137
|
|
130
138
|
describe 'translated mutator' do
|
131
139
|
subject { model.send("#{from_name}=", value) }
|
132
140
|
|
133
|
-
it '
|
141
|
+
it 'changes the value for the existing property' do
|
134
142
|
expect { subject }.to change { model.send(other_to_name) }.to(value)
|
135
143
|
end
|
136
144
|
|
137
|
-
it '
|
145
|
+
it 'changes the value for the new property' do
|
138
146
|
expect { subject }.to change { model.send(to_name) }.to(value)
|
139
147
|
end
|
140
148
|
end
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
|
-
describe Modelish::PropertyTypes do
|
5
|
+
RSpec.describe Modelish::PropertyTypes do
|
4
6
|
let(:model_class) { Class.new { include Modelish::PropertyTypes } }
|
5
7
|
let(:model) { model_class.new }
|
6
8
|
|
@@ -8,91 +10,93 @@ describe Modelish::PropertyTypes do
|
|
8
10
|
|
9
11
|
subject { model_class }
|
10
12
|
|
11
|
-
context
|
12
|
-
it {
|
13
|
-
it {
|
13
|
+
context 'when included' do
|
14
|
+
it { is_expected.to respond_to(:add_property_type) }
|
15
|
+
it { is_expected.to respond_to(:property_types) }
|
14
16
|
|
15
|
-
describe
|
17
|
+
describe 'property_types' do
|
16
18
|
subject { model_class.property_types }
|
17
19
|
|
18
|
-
it {
|
19
|
-
it {
|
20
|
+
it { is_expected.to be_a Hash }
|
21
|
+
it { is_expected.to be_empty }
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
|
-
describe
|
25
|
+
describe '#add_property_type' do
|
24
26
|
before { model_class.add_property_type(property_name, property_type) }
|
25
27
|
|
26
28
|
let(:default_value) { nil }
|
27
29
|
|
28
|
-
context
|
30
|
+
context 'when property_type is Integer' do
|
29
31
|
let(:property_type) { Integer }
|
30
32
|
|
31
|
-
|
33
|
+
it_behaves_like 'a typed property', :my_property, Integer do
|
32
34
|
let(:valid_string) { '42' }
|
33
35
|
let(:valid_typed_value) { 42 }
|
34
36
|
let(:invalid_value) { 'forty-two' }
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
38
|
-
context
|
40
|
+
context 'when property_type is Float' do
|
39
41
|
let(:property_type) { Float }
|
40
42
|
|
41
|
-
|
43
|
+
it_behaves_like 'a typed property', :my_property, Float do
|
42
44
|
let(:valid_string) { '42.5' }
|
43
45
|
let(:valid_typed_value) { 42.5 }
|
44
46
|
let(:invalid_value) { 'forty-two point five' }
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
48
|
-
context
|
50
|
+
context 'when property_type is Date' do
|
49
51
|
let(:property_type) { Date }
|
50
52
|
|
51
|
-
|
53
|
+
it_behaves_like 'a typed property', :my_property, Date do
|
52
54
|
let(:valid_string) { '2011-03-10' }
|
53
|
-
let(:valid_typed_value) { Date.civil(2011,
|
55
|
+
let(:valid_typed_value) { Date.civil(2011, 3, 10) }
|
54
56
|
let(:invalid_value) { 'foo' }
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
|
-
context
|
60
|
+
context 'when property_type is DateTime' do
|
59
61
|
let(:property_type) { DateTime }
|
60
62
|
|
61
|
-
|
63
|
+
it_behaves_like 'a typed property', :my_property, DateTime do
|
62
64
|
let(:valid_string) { '2011-02-24T14:09:43-07:00' }
|
63
|
-
let(:valid_typed_value)
|
65
|
+
let(:valid_typed_value) do
|
66
|
+
DateTime.civil(2011, 2, 24, 14, 9, 43, Rational(-7, 24))
|
67
|
+
end
|
64
68
|
let(:invalid_value) { 'foo' }
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
68
|
-
context
|
72
|
+
context 'when property_type is String' do
|
69
73
|
let(:property_type) { String }
|
70
74
|
|
71
|
-
|
75
|
+
it_behaves_like 'a typed property', :my_property, String do
|
72
76
|
let(:valid_string) { "\tmy_string \n" }
|
73
|
-
let(:valid_typed_value) {
|
74
|
-
end
|
77
|
+
let(:valid_typed_value) { 'my_string' }
|
78
|
+
end
|
75
79
|
end
|
76
80
|
|
77
|
-
context
|
81
|
+
context 'when property_type is Symbol' do
|
78
82
|
let(:property_type) { Symbol }
|
79
83
|
|
80
|
-
|
84
|
+
it_behaves_like 'a typed property', :my_property, Symbol do
|
81
85
|
let(:valid_string) { 'MyCrazy String' }
|
82
86
|
let(:valid_typed_value) { :my_crazy_string }
|
83
87
|
end
|
84
88
|
end
|
85
89
|
|
86
|
-
context
|
90
|
+
context 'when property_type is Array' do
|
87
91
|
let(:property_type) { Array }
|
88
92
|
|
89
|
-
|
93
|
+
it_behaves_like 'a typed property', :my_property, Array do
|
90
94
|
let(:valid_string) { 'my valid string' }
|
91
95
|
let(:valid_typed_value) { ['my valid string'] }
|
92
96
|
end
|
93
97
|
end
|
94
98
|
|
95
|
-
context
|
99
|
+
context 'when type is an arbitrary class' do
|
96
100
|
class CustomType
|
97
101
|
attr_accessor :foo
|
98
102
|
|
@@ -102,68 +106,72 @@ describe Modelish::PropertyTypes do
|
|
102
106
|
elsif raw_val.is_a?(String)
|
103
107
|
self.foo = raw_val
|
104
108
|
else
|
105
|
-
raise TypeError
|
109
|
+
raise TypeError
|
106
110
|
end
|
107
111
|
end
|
108
112
|
|
109
113
|
def ==(other)
|
110
|
-
other.respond_to?(:foo) && other.foo ==
|
114
|
+
other.respond_to?(:foo) && other.foo == foo
|
111
115
|
end
|
112
116
|
end
|
113
117
|
|
114
118
|
let(:property_type) { CustomType }
|
115
119
|
|
116
|
-
|
120
|
+
it_behaves_like 'a typed property', :my_property, CustomType do
|
117
121
|
let(:valid_string) { 'bar' }
|
118
122
|
let(:valid_typed_value) { CustomType.new(valid_string) }
|
119
123
|
let(:invalid_value) { Object.new }
|
124
|
+
let(:error_type) { TypeError }
|
120
125
|
end
|
121
126
|
end
|
122
127
|
|
123
|
-
context
|
128
|
+
context 'when type is a closure' do
|
124
129
|
let(:property_type) do
|
125
130
|
lambda do |val|
|
126
131
|
value = val.respond_to?(:split) ? val.split(',') : val
|
127
|
-
value.
|
132
|
+
value.map(&:to_i)
|
128
133
|
end
|
129
134
|
end
|
130
135
|
|
131
|
-
|
136
|
+
it_behaves_like 'a typed property', :my_property do
|
132
137
|
let(:valid_string) { '1,2,3' }
|
133
|
-
let(:valid_typed_value) { [1,2,3] }
|
138
|
+
let(:valid_typed_value) { [1, 2, 3] }
|
134
139
|
let(:invalid_value) { Object.new }
|
140
|
+
let(:error_type) { NoMethodError }
|
135
141
|
end
|
136
142
|
end
|
137
143
|
|
138
|
-
context
|
139
|
-
before
|
144
|
+
context 'when a property is defined more than once' do
|
145
|
+
before do
|
146
|
+
model_class.add_property_type(property_name, other_property_type)
|
147
|
+
end
|
140
148
|
|
141
|
-
let(:property_type) { Integer }
|
149
|
+
let(:property_type) { Integer }
|
142
150
|
|
143
|
-
context
|
151
|
+
context 'with the same property_type' do
|
144
152
|
let(:other_property_type) { property_type }
|
145
153
|
|
146
|
-
it
|
154
|
+
it 'does not raise an error when the property is accessed' do
|
147
155
|
expect { model.send(property_name) }.to_not raise_error
|
148
156
|
end
|
149
157
|
|
150
|
-
|
158
|
+
it_behaves_like 'a typed property', :my_property, Integer do
|
151
159
|
let(:valid_string) { '112358' }
|
152
|
-
let(:valid_typed_value) {
|
160
|
+
let(:valid_typed_value) { 112_358 }
|
153
161
|
let(:invalid_value) { 'blah' }
|
154
162
|
end
|
155
163
|
end
|
156
164
|
|
157
|
-
context
|
165
|
+
context 'with different property_types' do
|
158
166
|
let(:other_property_type) { Float }
|
159
167
|
|
160
|
-
it
|
168
|
+
it 'does not raise an error when the property is accessed' do
|
161
169
|
expect { model.send(property_name) }.to_not raise_error
|
162
170
|
end
|
163
171
|
|
164
|
-
|
172
|
+
it_behaves_like 'a typed property', :my_property, Float do
|
165
173
|
let(:valid_string) { '112358.13' }
|
166
|
-
let(:valid_typed_value) {
|
174
|
+
let(:valid_typed_value) { 112_358.13 }
|
167
175
|
let(:invalid_value) { 'blah' }
|
168
176
|
end
|
169
177
|
end
|