light_form 0.0.1 → 0.0.2
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 +4 -4
- data/lib/light_form.rb +2 -1
- data/lib/light_form/form.rb +4 -61
- data/lib/light_form/lash.rb +20 -0
- data/lib/light_form/property_methods.rb +190 -0
- data/lib/light_form/version.rb +1 -1
- data/light_form.gemspec +1 -0
- data/spec/lib/form_spec.rb +261 -11
- data/spec/lib/lash_spec.rb +161 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 378076de26719aab740e5c61065c2f6afd7575bc
|
4
|
+
data.tar.gz: 307deab9de929cd38f8709178cdcf759932d84e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32ac9549f36d5d68f0e238dd54181e79655effba82ec837fcc302b3c70e5a5d6a5737aca5190310fe0c3cf2b1fae46808f7941cb295f8f9f33c06066d6871743
|
7
|
+
data.tar.gz: 3a921ca9efefd2d99bfc15335c61bf8c59f22f671328350fa169cadbb02ff538980216917451b89b67bc6ea9520101f1fe259373c97b7150f188e2c672f14ebb
|
data/lib/light_form.rb
CHANGED
data/lib/light_form/form.rb
CHANGED
@@ -1,74 +1,17 @@
|
|
1
|
-
require 'set'
|
2
1
|
require 'active_model'
|
3
2
|
|
4
3
|
module LightForm
|
5
4
|
class Form
|
6
5
|
include ActiveModel::Model
|
6
|
+
include ActiveModel::Serializers::JSON
|
7
|
+
include PropertyMethods
|
7
8
|
|
8
9
|
def initialize(params = {})
|
9
10
|
super(_prepare_params(params))
|
10
11
|
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
@config ||= {}
|
15
|
-
end
|
16
|
-
|
17
|
-
def properties(*prop_names)
|
18
|
-
add_property = method(:_add_property)
|
19
|
-
prop_names.each(&add_property)
|
20
|
-
end
|
21
|
-
|
22
|
-
def property(prop_name, options = {}, &_block)
|
23
|
-
_add_property(prop_name)
|
24
|
-
_add_property_transform(prop_name, options)
|
25
|
-
_add_property_validation(prop_name, options[:validates]) if options[:validates]
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def _properties_transform
|
31
|
-
config[:properties_transform] ||= {}
|
32
|
-
end
|
33
|
-
|
34
|
-
def _properties
|
35
|
-
config[:properties] ||= Set.new
|
36
|
-
end
|
37
|
-
|
38
|
-
def _add_property_transform(prop_name, options = {})
|
39
|
-
transformations = options.slice(:from, :transform_with, :default)
|
40
|
-
return if transformations.empty?
|
41
|
-
_properties_transform[prop_name] = transformations
|
42
|
-
end
|
43
|
-
|
44
|
-
def _add_property(prop_name)
|
45
|
-
send(:attr_accessor, prop_name) if _properties.add?(prop_name)
|
46
|
-
end
|
47
|
-
|
48
|
-
def _add_property_validation(prop_name, validation)
|
49
|
-
validates(prop_name, validation)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def _prepare_params(value)
|
56
|
-
params = value.clone
|
57
|
-
_prepare_params_keys_and_values!(params)
|
58
|
-
properties = self.class.config[:properties] || []
|
59
|
-
params.extract!(*properties)
|
60
|
-
end
|
61
|
-
|
62
|
-
def _prepare_params_keys_and_values!(params)
|
63
|
-
properties_from = self.class.config[:properties_transform] || {}
|
64
|
-
properties_from.each do |key_to, hash|
|
65
|
-
params[key_to] = params.delete(hash[:from]) if hash[:from]
|
66
|
-
next params[key_to] = hash[:default] if params[key_to].empty? && hash[:default]
|
67
|
-
next unless hash[:transform_with]
|
68
|
-
transformation = hash[:transform_with]
|
69
|
-
trans_proc = transformation.is_a?(Symbol) ? method(transformation) : transformation
|
70
|
-
params[key_to] = trans_proc.call(params[key_to])
|
71
|
-
end
|
13
|
+
def to_h(*args)
|
14
|
+
as_json(*args)
|
72
15
|
end
|
73
16
|
end
|
74
17
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
module LightForm
|
4
|
+
class Lash < Hash
|
5
|
+
include ActiveModel::Model
|
6
|
+
include PropertyMethods
|
7
|
+
|
8
|
+
def initialize(params = {})
|
9
|
+
_prepare_params(params).each_pair { |k, v| self[k.to_sym] = v }
|
10
|
+
end
|
11
|
+
|
12
|
+
def self._add_property(prop_name)
|
13
|
+
config[:errors_overriden] = true if prop_name == :errors
|
14
|
+
return unless _properties.add?(prop_name)
|
15
|
+
define_method(prop_name) { |&block| self.[](prop_name, &block) }
|
16
|
+
property_assignment = "#{prop_name}=".to_sym
|
17
|
+
define_method(property_assignment) { |value| self.[]=(prop_name, value) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'active_support'
|
3
|
+
|
4
|
+
module LightForm
|
5
|
+
TransformationError = Class.new(StandardError)
|
6
|
+
MissingCollectionError = Class.new(StandardError)
|
7
|
+
MissingParamError = Class.new(StandardError)
|
8
|
+
|
9
|
+
module PropertyMethods
|
10
|
+
def self.included(base)
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def config
|
16
|
+
@config ||= {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def properties(*prop_names)
|
20
|
+
add_property = method(:_add_property)
|
21
|
+
prop_names.each(&add_property)
|
22
|
+
end
|
23
|
+
|
24
|
+
def property(prop_name, options = {}, &block)
|
25
|
+
_add_property(prop_name)
|
26
|
+
_add_property_transform(prop_name, options)
|
27
|
+
_add_property_validation(prop_name, options[:validates]) if options[:validates]
|
28
|
+
_add_property_source(prop_name, &block) if block
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def _add_property_source(prop_name, &block)
|
34
|
+
klass = Class.new(Lash)
|
35
|
+
klass.class_eval("def self.name; \"#{ActiveSupport::Inflector.classify(prop_name)}\"; end")
|
36
|
+
klass.class_eval(&block)
|
37
|
+
_properties_sources[prop_name] = { class: klass }
|
38
|
+
end
|
39
|
+
|
40
|
+
def _properties_sources
|
41
|
+
config[:properties_sources] ||= {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def _properties_transform
|
45
|
+
config[:properties_transform] ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def _properties
|
49
|
+
config[:properties] ||= Set.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def _add_property_transform(prop_name, options = {})
|
53
|
+
transformations = options.slice(:from, :transform_with, :with, :default, :model, :collection, :uniq, :required)
|
54
|
+
return if transformations.empty?
|
55
|
+
_properties_transform[prop_name] = transformations
|
56
|
+
end
|
57
|
+
|
58
|
+
def _add_property(prop_name)
|
59
|
+
config[:errors_overriden] = true if prop_name == :errors
|
60
|
+
send(:attr_accessor, prop_name) if _properties.add?(prop_name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def _add_property_validation(prop_name, validation)
|
64
|
+
validates(prop_name, validation)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def _errors
|
69
|
+
@_errors
|
70
|
+
end
|
71
|
+
|
72
|
+
def errors_overriden?
|
73
|
+
self.class.config[:errors_overriden] == true
|
74
|
+
end
|
75
|
+
|
76
|
+
def valid?
|
77
|
+
return (_check_validation && super) unless errors_overriden?
|
78
|
+
@_errors = @errors
|
79
|
+
@errors = ActiveModel::Errors.new(self)
|
80
|
+
stored_method = method(:errors)
|
81
|
+
errors_method = -> { @errors }
|
82
|
+
define_singleton_method(:errors) { errors_method.call }
|
83
|
+
result, store, @_errors, @errors = (_check_validation && super), @_errors, @errors, store
|
84
|
+
define_singleton_method(:errors) { stored_method.call }
|
85
|
+
result
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def _validation_errors(obj)
|
91
|
+
obj.errors if obj.respond_to?(:valid?) && !obj.valid?
|
92
|
+
end
|
93
|
+
|
94
|
+
def _check_validation
|
95
|
+
@errors = ActiveModel::Errors.new(self)
|
96
|
+
properties = _properties.delete(_properties_sources.keys)
|
97
|
+
properties.each do |prop|
|
98
|
+
public_send(prop).tap do |subject|
|
99
|
+
items = subject.is_a?(Array) ? subject.map(&method(:_validation_errors)).compact : _validation_errors(subject)
|
100
|
+
@errors.add(prop, items) if items && !items.empty?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
_properties_sources.each do |prop, v|
|
104
|
+
next unless v[:params]
|
105
|
+
subject = v[:params].clone
|
106
|
+
items = subject.is_a?(Array) ? subject.map(&method(:_validation_errors)) : _validation_errors(v[:params])
|
107
|
+
@errors.add(prop, items) if items && !items.empty?
|
108
|
+
end
|
109
|
+
@errors.empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
def _properties_sources
|
113
|
+
@_properties_sources ||= self.class.config[:properties_sources] || {}
|
114
|
+
end
|
115
|
+
|
116
|
+
def _properties
|
117
|
+
@_properties ||= self.class.config[:properties] || []
|
118
|
+
end
|
119
|
+
|
120
|
+
def _prepare_params(value)
|
121
|
+
return if value.nil?
|
122
|
+
params = value.clone.symbolize_keys
|
123
|
+
return params if _properties.empty?
|
124
|
+
_prepare_sources(params)
|
125
|
+
_prepare_params_keys_and_values!(params)
|
126
|
+
params.extract!(*_properties)
|
127
|
+
end
|
128
|
+
|
129
|
+
def _prepare_params_keys_and_values!(params)
|
130
|
+
properties_from = self.class.config[:properties_transform] || {}
|
131
|
+
properties_from.clone.each do |key_to, hash|
|
132
|
+
_update_key!(params, key_to, hash)
|
133
|
+
fail(MissingParamError, key_to.to_s) if hash[:required] && !params.key?(key_to)
|
134
|
+
set_default = (params[key_to].nil? || params[key_to].empty?) && hash[:default]
|
135
|
+
_update_value_as_default!(params, key_to, hash) if set_default
|
136
|
+
_transform_value!(params, key_to, hash) unless set_default
|
137
|
+
_modelable_value!(params, key_to, hash)
|
138
|
+
_collectionaize_value!(params, key_to, hash)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def _update_key!(params, key, hash)
|
143
|
+
params[key] = params.delete(hash[:from]) if hash[:from]
|
144
|
+
end
|
145
|
+
|
146
|
+
def _update_value_as_default!(params, key, hash)
|
147
|
+
params[key] = hash[:default]
|
148
|
+
end
|
149
|
+
|
150
|
+
def _transform_value!(params, key, hash)
|
151
|
+
transformation = hash[:with] || hash[:transform_with]
|
152
|
+
return unless transformation
|
153
|
+
trans_proc = transformation.is_a?(Symbol) ? method(transformation) : transformation
|
154
|
+
params[key] = trans_proc.call(params[key])
|
155
|
+
rescue => e
|
156
|
+
raise TransformationError, "key #{key}: #{e.message}"
|
157
|
+
end
|
158
|
+
|
159
|
+
def _modelable_value!(params, key, hash)
|
160
|
+
return unless hash[:model]
|
161
|
+
_save_source_params(key, params[key])
|
162
|
+
params[key] = hash[:model].new(params[key])
|
163
|
+
end
|
164
|
+
|
165
|
+
def _save_source_params(key, params)
|
166
|
+
_properties_sources[key][:params] = params.clone if _properties_sources[key]
|
167
|
+
end
|
168
|
+
|
169
|
+
def _collectionaize_value!(params, key, hash)
|
170
|
+
return unless hash[:collection]
|
171
|
+
array = params[key]
|
172
|
+
fail(MissingCollectionError, "on key: #{key}") unless array.is_a? Array
|
173
|
+
array.uniq! if hash[:uniq]
|
174
|
+
return params[key] = array if hash[:collection] == true
|
175
|
+
_save_source_params(key, array.compact)
|
176
|
+
params[key] = array.compact.map { |source| hash[:collection].new(source) }
|
177
|
+
end
|
178
|
+
|
179
|
+
def _prepare_sources(params)
|
180
|
+
_properties_sources.each do |k, v|
|
181
|
+
next unless params[k]
|
182
|
+
params[k] = params[k].is_a?(Array) ? params[k].map { |s| v[:class].new(s) } : v[:class].new(params[k])
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def attributes
|
187
|
+
OpenStruct.new(keys: self.class.config[:properties])
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
data/lib/light_form/version.rb
CHANGED
data/light_form.gemspec
CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
+
spec.add_runtime_dependency 'activesupport', '>= 3.0.2'
|
21
22
|
spec.add_runtime_dependency 'activemodel', '>= 3.0.2'
|
22
23
|
spec.add_development_dependency 'bundler', '>= 1.6'
|
23
24
|
spec.add_development_dependency 'rake', '~> 10.4'
|
data/spec/lib/form_spec.rb
CHANGED
@@ -1,4 +1,20 @@
|
|
1
1
|
describe LightForm::Form do
|
2
|
+
class ChildModel
|
3
|
+
include ActiveModel::Model
|
4
|
+
attr_accessor :name, :age
|
5
|
+
def equality_state
|
6
|
+
[:name, :age].map { |attr| public_send("#{attr}") }
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(o)
|
10
|
+
eql?(o)
|
11
|
+
end
|
12
|
+
|
13
|
+
def eql?(o)
|
14
|
+
o.class == self.class && o.equality_state == equality_state
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
2
18
|
context '.properties' do
|
3
19
|
subject do
|
4
20
|
class_factory do
|
@@ -68,6 +84,19 @@ describe LightForm::Form do
|
|
68
84
|
end
|
69
85
|
end
|
70
86
|
|
87
|
+
context 'add with :default and :with option' do
|
88
|
+
let(:time) { Time.parse('2015-03-29 14:41:28 +0200') }
|
89
|
+
subject do
|
90
|
+
object_factory(attributes: { time: '' }) do
|
91
|
+
property :time, default: Time.parse('2015-03-29 14:41:28 +0200'), with: -> (v) { Time.parse(v) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'assign property value from key :default when value is empty and skip :transform_with' do
|
96
|
+
expect(subject.time).to eq(time)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
71
100
|
context 'add with :transform_with option assign property value after transformation' do
|
72
101
|
it 'by proc' do
|
73
102
|
test_obj = object_factory(attributes: { number: '12' }) do
|
@@ -90,6 +119,28 @@ describe LightForm::Form do
|
|
90
119
|
end
|
91
120
|
end
|
92
121
|
|
122
|
+
context 'add with :with option assign property value after transformation' do
|
123
|
+
it 'by proc' do
|
124
|
+
test_obj = object_factory(attributes: { number: '12' }) do
|
125
|
+
property :number, with: -> (v) { v.to_i }
|
126
|
+
end
|
127
|
+
|
128
|
+
expect(test_obj.number).to eq(12)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'by method' do
|
132
|
+
test_obj = object_factory(attributes: { number: '12' }) do
|
133
|
+
property :number, with: :convert_to_number
|
134
|
+
|
135
|
+
def convert_to_number(value)
|
136
|
+
value.to_i
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
expect(test_obj.number).to eq(12)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
93
144
|
context 'add with :validates option' do
|
94
145
|
subject do
|
95
146
|
object_factory(real_class_name: 'FakeForm', attributes: { name: '' }) do
|
@@ -102,17 +153,216 @@ describe LightForm::Form do
|
|
102
153
|
expect(subject.errors.as_json).to eq(name: ["can't be blank"])
|
103
154
|
end
|
104
155
|
end
|
156
|
+
|
157
|
+
context 'add with :model option' do
|
158
|
+
subject do
|
159
|
+
object_factory(attributes: { child: { name: 'Tom', age: 2 } }) do
|
160
|
+
property :child, model: ChildModel
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'create model for attribute' do
|
165
|
+
expect(subject.child).to be_a(ChildModel)
|
166
|
+
expect(subject.child.name).to eq('Tom')
|
167
|
+
expect(subject.child.age).to eq(2)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'add with :collection option' do
|
172
|
+
it 'raise error when is not a Array' do
|
173
|
+
expect {
|
174
|
+
object_factory(attributes: { children: { name: 'Tom', age: 2 } }) do
|
175
|
+
property :children, collection: true
|
176
|
+
end
|
177
|
+
}.to raise_error
|
178
|
+
end
|
179
|
+
|
180
|
+
context 'with :model' do
|
181
|
+
subject do
|
182
|
+
attributes = {
|
183
|
+
children: [
|
184
|
+
{ name: 'Tom', age: 2 },
|
185
|
+
{ name: 'Emi', age: 4 }
|
186
|
+
]
|
187
|
+
}
|
188
|
+
|
189
|
+
object_factory(attributes: attributes) do
|
190
|
+
property :children, collection: ChildModel
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'returns array of models' do
|
195
|
+
expect(subject.children.map(&:class)).to eq([ChildModel, ChildModel])
|
196
|
+
expect(subject.children.count).to eq(2)
|
197
|
+
expect(subject.children.first.name).to eq('Tom')
|
198
|
+
expect(subject.children.last.name).to eq('Emi')
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context 'with :model and :uniq' do
|
203
|
+
subject do
|
204
|
+
attributes = {
|
205
|
+
children: [
|
206
|
+
{ name: 'Tom', age: 2 },
|
207
|
+
{ name: 'Tom', age: 2 }
|
208
|
+
]
|
209
|
+
}
|
210
|
+
|
211
|
+
object_factory(attributes: attributes) do
|
212
|
+
property :children, collection: ChildModel, uniq: true
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'returns array of models' do
|
217
|
+
expect(subject.children.map(&:class)).to eq([ChildModel])
|
218
|
+
expect(subject.children.count).to eq(1)
|
219
|
+
expect(subject.children.first.name).to eq('Tom')
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'add with :required option' do
|
225
|
+
it 'raise MissingParamError when required param missing' do
|
226
|
+
expect {
|
227
|
+
object_factory { property :abc, required: true }
|
228
|
+
}.to raise_error(LightForm::MissingParamError, 'abc')
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context 'add nested' do
|
234
|
+
let(:attributes) do
|
235
|
+
{
|
236
|
+
ab: {
|
237
|
+
cd: {
|
238
|
+
child: { age: '1', name: 'pawel', skip: 'av' }
|
239
|
+
},
|
240
|
+
ef: [
|
241
|
+
{ age: '31', name: 'Pawel', skip: 'wrong' },
|
242
|
+
{ age: '32', name: 'Sylwia', skip: 'bad' }
|
243
|
+
],
|
244
|
+
gh: [
|
245
|
+
{ age: '31', name: 'Pawel', skip: 'wrong' },
|
246
|
+
{ age: '32', name: 'Sylwia', skip: 'bad' },
|
247
|
+
{ age: '32', name: 'Sylwia', skip: 'bad' }
|
248
|
+
]
|
249
|
+
}
|
250
|
+
}
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'hold proper structure' do
|
254
|
+
test_obj = object_factory(attributes: attributes) do
|
255
|
+
property :ab do
|
256
|
+
property :cd do
|
257
|
+
property :child, model: ChildModel do
|
258
|
+
properties :name, :age
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
property :ef, collection: true do
|
263
|
+
property :name
|
264
|
+
property :age, with: -> (v) { v.to_i }
|
265
|
+
end
|
266
|
+
|
267
|
+
property :gh, collection: ChildModel, uniq: true do
|
268
|
+
properties :name, :age
|
269
|
+
end
|
270
|
+
|
271
|
+
property :ij, collection: true, with: -> (v) { (v.nil? || v.empty?) ? [] : v }
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
expect(test_obj.ab).not_to eq(nil)
|
276
|
+
expect(test_obj.ab.cd.child).to be_a(ChildModel)
|
277
|
+
expect(test_obj.ab.cd.child.age).to eq('1')
|
278
|
+
expect(test_obj.ab.cd.child.name).to eq('pawel')
|
279
|
+
expect { test_obj.ab.cd.child.skip }.to raise_error
|
280
|
+
expect(test_obj.ab.ef[0].name).to eq('Pawel')
|
281
|
+
expect(test_obj.ab.ef[0].age).to eq(31)
|
282
|
+
expect(test_obj.ab.gh[0]).to be_a(ChildModel)
|
283
|
+
expect(test_obj.ab.gh.count).to eq(2)
|
284
|
+
expect(test_obj.ab.ij).to eq([])
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'validation works' do
|
288
|
+
test_obj = object_factory(
|
289
|
+
attributes: {
|
290
|
+
test: {
|
291
|
+
child: { age: '1', name: '', skip: 'av' },
|
292
|
+
children_hash: [
|
293
|
+
{ age: '31', name: '', skip: 'wrong' },
|
294
|
+
{ age: '32', name: 'Sylwia', skip: 'bad' }
|
295
|
+
],
|
296
|
+
children: [
|
297
|
+
{ age: '31', name: 'Pawel', skip: 'wrong' },
|
298
|
+
{ age: '32', name: '', skip: 'bad' },
|
299
|
+
{ age: '32', name: '', skip: 'bad' }
|
300
|
+
]
|
301
|
+
}
|
302
|
+
}
|
303
|
+
) do
|
304
|
+
property :test do
|
305
|
+
property :child, model: ChildModel do
|
306
|
+
property :name, validates: { presence: true }
|
307
|
+
property :age, validates: { numericality: true }
|
308
|
+
end
|
309
|
+
|
310
|
+
property :children_hash, collection: true do
|
311
|
+
property :name, validates: { presence: true }
|
312
|
+
property :age, with: -> (v) { v.to_i }
|
313
|
+
end
|
314
|
+
|
315
|
+
property :children, collection: ChildModel, uniq: true do
|
316
|
+
property :name, validates: { presence: true }
|
317
|
+
property :age, validates: { numericality: true }
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
expect(test_obj.valid?).to eq(false)
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'to_h' do
|
326
|
+
test_obj = object_factory(attributes: attributes) do
|
327
|
+
property :ab do
|
328
|
+
property :cd do
|
329
|
+
property :child, model: ChildModel do
|
330
|
+
properties :name, :age
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
property :ef, collection: true do
|
335
|
+
property :name
|
336
|
+
property :age, with: -> (v) { v.to_i }
|
337
|
+
end
|
338
|
+
|
339
|
+
property :gh, collection: ChildModel, uniq: true do
|
340
|
+
properties :name, :age
|
341
|
+
end
|
342
|
+
|
343
|
+
property :ij, collection: true, with: -> (v) { (v.nil? || v.empty?) ? [] : v }
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
expect(test_obj.to_h).to eq(
|
348
|
+
{
|
349
|
+
ab: {
|
350
|
+
cd: {
|
351
|
+
child: ChildModel.new(age: '1', name: 'pawel')
|
352
|
+
},
|
353
|
+
ef: [
|
354
|
+
{ age: 31, name: 'Pawel' },
|
355
|
+
{ age: 32, name: 'Sylwia' }
|
356
|
+
],
|
357
|
+
gh: [
|
358
|
+
ChildModel.new(age: '31', name: 'Pawel'),
|
359
|
+
ChildModel.new(age: '32', name: 'Sylwia')
|
360
|
+
],
|
361
|
+
ij: []
|
362
|
+
}
|
363
|
+
}
|
364
|
+
)
|
365
|
+
end
|
105
366
|
end
|
106
|
-
# context 'add nested' do
|
107
|
-
# it 'first_level' do
|
108
|
-
# test_obj = object_factory(attributes: { ab: { cd: 'cd'} }) do
|
109
|
-
# property :ab do
|
110
|
-
# property :cd
|
111
|
-
# end
|
112
|
-
# end
|
113
|
-
|
114
|
-
# expect(test_obj.ab).to eq(cd: 'cd')
|
115
|
-
# end
|
116
|
-
# end
|
117
367
|
end
|
118
368
|
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
describe LightForm::Lash do
|
2
|
+
context '.properties' do
|
3
|
+
subject do
|
4
|
+
class_factory do
|
5
|
+
properties :ab, :cd
|
6
|
+
properties :cd, :ef
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'add attributes' do
|
11
|
+
instance = subject.new(ab: 'ab', cd: 'cd', ef: 'ef', skip: 'this')
|
12
|
+
expect(instance.ab).to eq('ab')
|
13
|
+
expect(instance.cd).to eq('cd')
|
14
|
+
expect(instance.ef).to eq('ef')
|
15
|
+
expect { instance.skip }.to raise_error(NoMethodError)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'property' do
|
20
|
+
context 'add' do
|
21
|
+
it 'new property' do
|
22
|
+
test_obj = object_factory(attributes: { ab: 'ab', cd: 'cd', skip: 'this' }) do
|
23
|
+
property :ab
|
24
|
+
property :cd
|
25
|
+
end
|
26
|
+
|
27
|
+
expect(test_obj.ab).to eq('ab')
|
28
|
+
expect(test_obj.cd).to eq('cd')
|
29
|
+
expect { test_obj.skip }.to raise_error(NoMethodError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with options' do
|
34
|
+
context 'add with :from option' do
|
35
|
+
subject do
|
36
|
+
object_factory(attributes: { aB: 'ab' }) do
|
37
|
+
property :ab, from: :aB
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'assign property value from key provided by :from option' do
|
42
|
+
expect(subject.ab).to eq('ab')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'add with :default option' do
|
47
|
+
subject do
|
48
|
+
object_factory(attributes: { ab: '' }) do
|
49
|
+
property :ab, default: 'Default'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'assign property value from key provided by :default option when value is empty' do
|
54
|
+
expect(subject.ab).to eq('Default')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'add with :default and :transform_with option' do
|
59
|
+
let(:time) { Time.parse('2015-03-29 14:41:28 +0200') }
|
60
|
+
subject do
|
61
|
+
object_factory(attributes: { time: '' }) do
|
62
|
+
property :time, default: Time.parse('2015-03-29 14:41:28 +0200'), transform_with: -> (v) { Time.parse(v) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'assign property value from key :default when value is empty and skip :transform_with' do
|
67
|
+
expect(subject.time).to eq(time)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'add with :default and :with option' do
|
72
|
+
let(:time) { Time.parse('2015-03-29 14:41:28 +0200') }
|
73
|
+
subject do
|
74
|
+
object_factory(attributes: { time: '' }) do
|
75
|
+
property :time, default: Time.parse('2015-03-29 14:41:28 +0200'), with: -> (v) { Time.parse(v) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'assign property value from key :default when value is empty and skip :transform_with' do
|
80
|
+
expect(subject.time).to eq(time)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'add with :transform_with option assign property value after transformation' do
|
85
|
+
it 'by proc' do
|
86
|
+
test_obj = object_factory(attributes: { number: '12' }) do
|
87
|
+
property :number, transform_with: -> (v) { v.to_i }
|
88
|
+
end
|
89
|
+
|
90
|
+
expect(test_obj.number).to eq(12)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'by method' do
|
94
|
+
test_obj = object_factory(attributes: { number: '12' }) do
|
95
|
+
property :number, transform_with: :convert_to_number
|
96
|
+
|
97
|
+
def convert_to_number(value)
|
98
|
+
value.to_i
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
expect(test_obj.number).to eq(12)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'add with :with option assign property value after transformation' do
|
107
|
+
it 'by proc' do
|
108
|
+
test_obj = object_factory(attributes: { number: '12' }) do
|
109
|
+
property :number, with: -> (v) { v.to_i }
|
110
|
+
end
|
111
|
+
|
112
|
+
expect(test_obj.number).to eq(12)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'by method' do
|
116
|
+
test_obj = object_factory(attributes: { number: '12' }) do
|
117
|
+
property :number, with: :convert_to_number
|
118
|
+
|
119
|
+
def convert_to_number(value)
|
120
|
+
value.to_i
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
expect(test_obj.number).to eq(12)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'add with :validates option' do
|
129
|
+
subject do
|
130
|
+
object_factory(real_class_name: 'FakeHash', attributes: { name: '' }) do
|
131
|
+
property :name, validates: { presence: true }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'attribute has validation' do
|
136
|
+
expect(subject.valid?).to eq(false)
|
137
|
+
expect(subject.errors.as_json).to eq(name: ["can't be blank"])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'is a hash' do
|
143
|
+
test_obj = object_factory(real_class_name: 'LashTest', attributes: { name: '' }) do
|
144
|
+
property :name, validates: { presence: true }
|
145
|
+
end
|
146
|
+
|
147
|
+
expect(test_obj).to eq(name: '')
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'is a hash' do
|
151
|
+
test_obj = object_factory(real_class_name: 'LashTest1', attributes: { errors: '' }) do
|
152
|
+
property :errors, validates: { presence: true }
|
153
|
+
end
|
154
|
+
expect(test_obj.valid?).to eq(false)
|
155
|
+
expect(test_obj).to eq(errors: '')
|
156
|
+
expect(test_obj.errors).to eq('')
|
157
|
+
expect(test_obj.errors_overriden?).to eq(true)
|
158
|
+
expect(test_obj._errors.as_json).to eq(errors: ["can't be blank"])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: light_form
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pawel Niemczyk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.0.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.0.2
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: activemodel
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -154,9 +168,12 @@ files:
|
|
154
168
|
- Rakefile
|
155
169
|
- lib/light_form.rb
|
156
170
|
- lib/light_form/form.rb
|
171
|
+
- lib/light_form/lash.rb
|
172
|
+
- lib/light_form/property_methods.rb
|
157
173
|
- lib/light_form/version.rb
|
158
174
|
- light_form.gemspec
|
159
175
|
- spec/lib/form_spec.rb
|
176
|
+
- spec/lib/lash_spec.rb
|
160
177
|
- spec/spec_helper.rb
|
161
178
|
- spec/support/factory_helper.rb
|
162
179
|
homepage: https://github.com/pniemczyk/light_form
|
@@ -185,5 +202,6 @@ specification_version: 4
|
|
185
202
|
summary: Light Form
|
186
203
|
test_files:
|
187
204
|
- spec/lib/form_spec.rb
|
205
|
+
- spec/lib/lash_spec.rb
|
188
206
|
- spec/spec_helper.rb
|
189
207
|
- spec/support/factory_helper.rb
|