mini_form 0.1.0
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 +17 -0
- data/.rspec +2 -0
- data/.rubocop.yml +24 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +261 -0
- data/Rakefile +8 -0
- data/lib/mini_form.rb +7 -0
- data/lib/mini_form/errors.rb +4 -0
- data/lib/mini_form/model.rb +120 -0
- data/lib/mini_form/nested_validator.rb +13 -0
- data/lib/mini_form/version.rb +3 -0
- data/mini_form.gemspec +30 -0
- data/spec/mini_form/nested_validator_spec.rb +41 -0
- data/spec/mini_form_spec.rb +238 -0
- data/spec/spec_helper.rb +12 -0
- metadata +177 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6d427b889425b8c2acd15827c88bc61430a70652
|
4
|
+
data.tar.gz: 582b6cf7164ab75b6f4df2f306eeb5580d9a0f3c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9e0f762fd07558b4c30386d9cd2a9a51b8855efb0279bb70b7f842d0e3748d7a413a0f449a31eb1d448e8b93c649930b3b90dbbb9ece8a4151ae013dd0f4d096
|
7
|
+
data.tar.gz: d23461cd996d47cbbdfe7efd5219aed986a72e83c5693e6d1e413927fa947786c200bd1e6bb1cd227573af8f4b1a8c9be7d89926111b4fc3bbd8fbd5efeec448
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require: rubocop-rspec
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
RunRailsCops: true
|
5
|
+
Exclude:
|
6
|
+
- example/db/**/*
|
7
|
+
- example/config/**/*
|
8
|
+
- search_object.gemspec
|
9
|
+
|
10
|
+
# Disables "Line is too long"
|
11
|
+
LineLength:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
# Disables "Missing top-level class documentation comment"
|
15
|
+
Documentation:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
# Disables "Use each_with_object instead of inject"
|
19
|
+
Style/EachWithObject:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
# Disables "Prefer reduce over inject."
|
23
|
+
Style/CollectionMethods:
|
24
|
+
Enabled: false
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013-2015 Radoslav Stankov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,261 @@
|
|
1
|
+
[](http://badge.fury.io/rb/mini_form)
|
2
|
+
[](https://codeclimate.com/github/RStankov/MiniForm)
|
3
|
+
[](http://travis-ci.org/RStankov/MiniForm)
|
4
|
+
[](https://coveralls.io/r/RStankov/MiniForm)
|
5
|
+
|
6
|
+
# MiniForm
|
7
|
+
|
8
|
+
Helpers for dealing with form objects and nested forms.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'mini_form'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install mini_form
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
class ProductForm
|
30
|
+
include MiniForm::Model
|
31
|
+
|
32
|
+
attributes :id, :name, :price, :description
|
33
|
+
|
34
|
+
validates :name, :price, :description, presence: true
|
35
|
+
|
36
|
+
# called after successful validations in update
|
37
|
+
def perform
|
38
|
+
@id = ExternalService.create(attributes)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class ProductsController < ApplicationController
|
45
|
+
def create
|
46
|
+
@product = ProductForm.new
|
47
|
+
|
48
|
+
if @product.update(product_params)
|
49
|
+
redirect_to product_path(product.id)
|
50
|
+
else
|
51
|
+
render :edit
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def product_params
|
58
|
+
params.require(:product).permit(:name, :price, :description)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
### Delegated attributes
|
64
|
+
|
65
|
+
Attributes can be delegated to a sub object.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class SignUpForm
|
69
|
+
include MiniForm::Model
|
70
|
+
|
71
|
+
attr_reader :account, :user
|
72
|
+
|
73
|
+
attributes :name, :email, delegate: :user
|
74
|
+
attributes :company_name, :plan, delegate: :account
|
75
|
+
|
76
|
+
validates :name, :email, :company_name, :plan, presence: true
|
77
|
+
|
78
|
+
def initialize
|
79
|
+
@account = Account.new
|
80
|
+
@user = User.new account: @account
|
81
|
+
end
|
82
|
+
|
83
|
+
def perform
|
84
|
+
user.save!
|
85
|
+
account.save!
|
86
|
+
end
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
form = SignUpForm.new
|
92
|
+
form.name = 'name' # => form.user.name = 'name'
|
93
|
+
form.name # => form.user.name
|
94
|
+
form.plan = 'free' # => form.account.plan = 'free'
|
95
|
+
form.plan # => form.account.plan
|
96
|
+
```
|
97
|
+
|
98
|
+
### Nested validator
|
99
|
+
|
100
|
+
`mini_form/nested` validator runs validations on the given model and copies errors to the form object.
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
class SignUpForm
|
104
|
+
include MiniForm::Model
|
105
|
+
|
106
|
+
attr_reader :account, :user
|
107
|
+
|
108
|
+
attributes :name, :email, delegate: :user
|
109
|
+
attributes :company_name, :plan, delegate: :account
|
110
|
+
|
111
|
+
validates :account, :user, 'mini_form/nested' => true
|
112
|
+
|
113
|
+
def initialize
|
114
|
+
@account = Account.new
|
115
|
+
@user = User.new account: @account
|
116
|
+
end
|
117
|
+
|
118
|
+
def perform
|
119
|
+
account.save!
|
120
|
+
user.save!
|
121
|
+
end
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
### Nested models
|
126
|
+
|
127
|
+
Combines delegated attributes and nested validation into a single call.
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class SignUpForm
|
131
|
+
include MiniForm::Model
|
132
|
+
|
133
|
+
modal :user, attributes: %i(name email)
|
134
|
+
model :account, attributes: %i(company_name plan)
|
135
|
+
|
136
|
+
def initialize
|
137
|
+
@account = Account.new
|
138
|
+
@user = User.new account: @account
|
139
|
+
end
|
140
|
+
|
141
|
+
def perform
|
142
|
+
account.save!
|
143
|
+
user.save!
|
144
|
+
end
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
### Auto saving nested models
|
149
|
+
|
150
|
+
In most of the time `perform` is just calling `save!`.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
class SignUpForm
|
154
|
+
include MiniForm::Model
|
155
|
+
|
156
|
+
modal :user, attributes: %i(name email), save: true
|
157
|
+
model :account, attributes: %i(company_name plan), save: true
|
158
|
+
|
159
|
+
def initialize
|
160
|
+
@account = Account.new
|
161
|
+
@user = User.new account: @account
|
162
|
+
end
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
### Before/after callbacks
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
class SignUpForm
|
170
|
+
include MiniForm::Model
|
171
|
+
|
172
|
+
# ... code
|
173
|
+
|
174
|
+
before_update :run_before_update
|
175
|
+
after_update :run_after_update
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def run_before_update
|
180
|
+
# ...
|
181
|
+
end
|
182
|
+
|
183
|
+
def run_after_update
|
184
|
+
# ...
|
185
|
+
end
|
186
|
+
|
187
|
+
# alternatively you can overwrite "before_update"
|
188
|
+
def before_update
|
189
|
+
end
|
190
|
+
|
191
|
+
# alternatively you can overwrite "after_update"
|
192
|
+
def after_update
|
193
|
+
end
|
194
|
+
end
|
195
|
+
```
|
196
|
+
|
197
|
+
### Methods
|
198
|
+
|
199
|
+
<table>
|
200
|
+
<tr>
|
201
|
+
<th>Method</th>
|
202
|
+
<th>Description</th>
|
203
|
+
</tr>
|
204
|
+
<tr>
|
205
|
+
<td>.model</td>
|
206
|
+
<td>Defines a sub object for the form</td>
|
207
|
+
</tr>
|
208
|
+
<tr>
|
209
|
+
<td>.attributes</td>
|
210
|
+
<td>Defines an attribute, it can delegate to sub object</td>
|
211
|
+
</tr>
|
212
|
+
<tr>
|
213
|
+
<td>#initialize</td>
|
214
|
+
<td>Meant to be overwritten. By defaults calls `attributes=`</td>
|
215
|
+
</tr>
|
216
|
+
<tr>
|
217
|
+
<td>#attributes=</td>
|
218
|
+
<td>Sets values of all attributes</td>
|
219
|
+
</tr>
|
220
|
+
<tr>
|
221
|
+
<td>#attributes</td>
|
222
|
+
<td>Returns all attributes of the form</td>
|
223
|
+
</tr>
|
224
|
+
<tr>
|
225
|
+
<td>#update</td>
|
226
|
+
<td>Sets attributes, calls validations, saves models and `perform`</td>
|
227
|
+
</tr>
|
228
|
+
<tr>
|
229
|
+
<td>#update!</td>
|
230
|
+
<td>Calls `update`. If validation fails, it raises an error</td>
|
231
|
+
</tr>
|
232
|
+
<tr>
|
233
|
+
<td>#perform</td>
|
234
|
+
<td>Meant to be overwritten. Doesn't do anything by default</td>
|
235
|
+
</tr>
|
236
|
+
<tr>
|
237
|
+
<td>#before_update</td>
|
238
|
+
<td>Meant to be overwritten.</td>
|
239
|
+
</tr>
|
240
|
+
<tr>
|
241
|
+
<td>#after_update</td>
|
242
|
+
<td>Meant to be overwritten.</td>
|
243
|
+
</tr>
|
244
|
+
<tr>
|
245
|
+
<td>#transaction</td>
|
246
|
+
<td>If ActiveRecord is available, wraps `perform` in transaction.</td>
|
247
|
+
</tr>
|
248
|
+
</table>
|
249
|
+
|
250
|
+
## Contributing
|
251
|
+
|
252
|
+
1. Fork it
|
253
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
254
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
255
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
256
|
+
5. Run the tests (`rake`)
|
257
|
+
6. Create new Pull Request
|
258
|
+
|
259
|
+
## License
|
260
|
+
|
261
|
+
**[MIT License](https://github.com/RStankov/MiniForm/blob/master/LICENSE.txt)**
|
data/Rakefile
ADDED
data/lib/mini_form.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'active_model'
|
3
|
+
|
4
|
+
module MiniForm
|
5
|
+
module Model
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
include ActiveModel::Validations
|
9
|
+
include ActiveModel::Conversion
|
10
|
+
|
11
|
+
extend ActiveModel::Naming
|
12
|
+
extend ActiveModel::Callbacks
|
13
|
+
|
14
|
+
extend ClassMethods
|
15
|
+
|
16
|
+
define_model_callbacks :update
|
17
|
+
|
18
|
+
before_update :before_update
|
19
|
+
after_update :after_update
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(attributes = {})
|
24
|
+
self.attributes = attributes
|
25
|
+
end
|
26
|
+
|
27
|
+
def persisted?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def attributes=(attributes)
|
32
|
+
attributes.slice(*self.class.attribute_names).each do |name, value|
|
33
|
+
public_send "#{name}=", value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def attributes
|
38
|
+
Hash[self.class.attribute_names.map { |name| [name, public_send(name)] }]
|
39
|
+
end
|
40
|
+
|
41
|
+
def update(attributes = {})
|
42
|
+
self.attributes = attributes unless attributes.empty?
|
43
|
+
|
44
|
+
return false unless valid?
|
45
|
+
|
46
|
+
run_callbacks :update do
|
47
|
+
transaction do
|
48
|
+
save_models
|
49
|
+
perform
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def update!(attributes = {})
|
57
|
+
fail InvalidForm unless update attributes
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def transaction(&block)
|
64
|
+
if defined? ActiveRecord
|
65
|
+
ActiveRecord::Base.transaction(&block)
|
66
|
+
else
|
67
|
+
yield
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# :api: private
|
72
|
+
def save_models
|
73
|
+
self.class.models_to_save.each { |model_name| public_send(model_name).save! }
|
74
|
+
end
|
75
|
+
|
76
|
+
def perform
|
77
|
+
# noop
|
78
|
+
end
|
79
|
+
|
80
|
+
def before_update
|
81
|
+
# noop
|
82
|
+
end
|
83
|
+
|
84
|
+
def after_update
|
85
|
+
# noop
|
86
|
+
end
|
87
|
+
|
88
|
+
module ClassMethods
|
89
|
+
# :api: private
|
90
|
+
def attribute_names
|
91
|
+
@attribute_names ||= []
|
92
|
+
end
|
93
|
+
|
94
|
+
# :api: private
|
95
|
+
def models_to_save
|
96
|
+
@models_to_save ||= []
|
97
|
+
end
|
98
|
+
|
99
|
+
def attributes(*attributes, delegate: nil, prefix: nil, allow_nil: nil)
|
100
|
+
attribute_names.push(*attributes)
|
101
|
+
|
102
|
+
if delegate.nil?
|
103
|
+
attr_accessor(*attributes)
|
104
|
+
else
|
105
|
+
self.delegate(*attributes, to: delegate, prefix: prefix, allow_nil: allow_nil)
|
106
|
+
self.delegate(*attributes.map { |attr| "#{attr}=" }, to: delegate, prefix: prefix, allow_nil: allow_nil)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def model(name, attributes: [], prefix: nil, allow_nil: nil, save: false)
|
111
|
+
attributes(name)
|
112
|
+
attributes(*attributes, delegate: name, prefix: prefix, allow_nil: allow_nil) unless attributes.empty?
|
113
|
+
|
114
|
+
validates name, 'mini_form/nested' => true
|
115
|
+
|
116
|
+
models_to_save << name if save
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
module MiniForm
|
4
|
+
class NestedValidator < ActiveModel::EachValidator
|
5
|
+
def validate_each(record, _, relation)
|
6
|
+
return if relation.valid?
|
7
|
+
|
8
|
+
relation.errors.each do |name, value|
|
9
|
+
record.errors.add name, value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/mini_form.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mini_form/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'mini_form'
|
8
|
+
spec.version = MiniForm::VERSION
|
9
|
+
spec.authors = ['Radoslav Stankov']
|
10
|
+
spec.email = ['rstankov@gmail.com']
|
11
|
+
spec.description = 'Sugar around ActiveModel::Model'
|
12
|
+
spec.summary = 'Easy to use form objects in Rails projects'
|
13
|
+
spec.homepage = 'https://github.com/RStankov/MiniForm'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(/^(spec)\//)
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'activemodel', '~> 4.0'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
24
|
+
spec.add_development_dependency 'rake'
|
25
|
+
spec.add_development_dependency 'rspec', '~> 2.14'
|
26
|
+
spec.add_development_dependency 'rspec-mocks', '>= 2.12.3'
|
27
|
+
spec.add_development_dependency 'coveralls'
|
28
|
+
spec.add_development_dependency 'rubocop'
|
29
|
+
spec.add_development_dependency 'rubocop-rspec'
|
30
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MiniForm
|
4
|
+
describe NestedValidator do
|
5
|
+
class User
|
6
|
+
include ActiveModel::Model
|
7
|
+
|
8
|
+
attr_accessor :name
|
9
|
+
|
10
|
+
validates :name, presence: true
|
11
|
+
end
|
12
|
+
|
13
|
+
class Record
|
14
|
+
include ActiveModel::Validations
|
15
|
+
|
16
|
+
attr_accessor :user
|
17
|
+
|
18
|
+
def initialize(user)
|
19
|
+
@user = user
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:validator) { NestedValidator.new(attributes: [:user]) }
|
24
|
+
let(:user) { User.new }
|
25
|
+
let(:record) { Record.new(user) }
|
26
|
+
|
27
|
+
it 'copies errors from submodel to model' do
|
28
|
+
validator.validate(record)
|
29
|
+
|
30
|
+
expect(record.errors[:name]).not_to be_blank
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'does not copy errors when there are not any' do
|
34
|
+
user.name = 'valid name'
|
35
|
+
|
36
|
+
validator.validate(record)
|
37
|
+
|
38
|
+
expect(record.errors[:name]).to be_blank
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MiniForm
|
4
|
+
describe Model do
|
5
|
+
let(:user) { User.new name: 'Name' }
|
6
|
+
|
7
|
+
class User
|
8
|
+
include ActiveModel::Model
|
9
|
+
|
10
|
+
attr_accessor :name
|
11
|
+
|
12
|
+
validates :name, presence: true
|
13
|
+
end
|
14
|
+
|
15
|
+
Example = Class.new do
|
16
|
+
include Model
|
17
|
+
attributes :name, :price
|
18
|
+
end
|
19
|
+
|
20
|
+
ExampleWithDelegate = Class.new do
|
21
|
+
include Model
|
22
|
+
|
23
|
+
attr_reader :user
|
24
|
+
|
25
|
+
attributes :name, delegate: :user
|
26
|
+
|
27
|
+
def initialize(user)
|
28
|
+
@user = user
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
ExampleWithModel = Class.new do
|
33
|
+
include Model
|
34
|
+
|
35
|
+
model :user, attributes: %i(name)
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'acts as ActiveModel' do
|
39
|
+
include ActiveModel::Lint::Tests
|
40
|
+
|
41
|
+
before do
|
42
|
+
@model = Example.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def assert(condition, message = nil)
|
46
|
+
expect(condition).to be_truthy, message
|
47
|
+
end
|
48
|
+
|
49
|
+
def assert_kind_of(expected_kind, object, message = nil)
|
50
|
+
expect(object).to be_kind_of(expected_kind), message
|
51
|
+
end
|
52
|
+
|
53
|
+
def assert_equal(expected_value, value, message = nil)
|
54
|
+
expect(value).to eq(expected_value), message
|
55
|
+
end
|
56
|
+
|
57
|
+
ActiveModel::Lint::Tests.public_instance_methods.map(&:to_s).grep(/^test/).each do |method|
|
58
|
+
example(method.gsub('_', ' ')) { send method }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.attributes' do
|
63
|
+
it 'generates getters' do
|
64
|
+
object = Example.new name: 'value'
|
65
|
+
expect(object.name).to eq 'value'
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'generates setters' do
|
69
|
+
object = Example.new
|
70
|
+
object.name = 'value'
|
71
|
+
|
72
|
+
expect(object.name).to eq 'value'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'can delegate getter' do
|
76
|
+
object = ExampleWithDelegate.new user
|
77
|
+
expect(object.name).to eq user.name
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'can delegate setter' do
|
81
|
+
object = ExampleWithDelegate.new user
|
82
|
+
|
83
|
+
object.name = 'New Name'
|
84
|
+
|
85
|
+
expect(object.name).to eq 'New Name'
|
86
|
+
expect(user.name).to eq 'New Name'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '.model' do
|
91
|
+
it 'generates model accessors' do
|
92
|
+
object = ExampleWithModel.new user: user
|
93
|
+
expect(object.user).to eq user
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'can delegate model attributes' do
|
97
|
+
object = ExampleWithModel.new user: user
|
98
|
+
expect(object.name).to eq user.name
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'performs nested validation for model' do
|
102
|
+
user = User.new
|
103
|
+
object = ExampleWithModel.new user: user
|
104
|
+
|
105
|
+
expect(object).not_to be_valid
|
106
|
+
expect(object.errors[:name]).to be_present
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '.attributes_names' do
|
111
|
+
it 'returns attribute names' do
|
112
|
+
expect(Example.attribute_names).to eq %i(name price)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#initialize' do
|
117
|
+
it 'can be called with no arguments' do
|
118
|
+
expect { Example.new }.not_to raise_error
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'assign the passed attributes' do
|
122
|
+
object = Example.new price: '$5'
|
123
|
+
|
124
|
+
expect(object.price).to eq '$5'
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'ignores invalid attributes' do
|
128
|
+
expect { Example.new invalid: 'attribute' }.not_to raise_error
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'handles HashWithIndifferentAccess hashes' do
|
132
|
+
hash = ActiveSupport::HashWithIndifferentAccess.new 'price' => '$5'
|
133
|
+
object = Example.new hash
|
134
|
+
|
135
|
+
expect(object.price).to eq '$5'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '#attributes' do
|
140
|
+
it 'returns attributes' do
|
141
|
+
object = Example.new name: 'iPhone', price: '$5'
|
142
|
+
expect(object.attributes).to eq name: 'iPhone', price: '$5'
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#update' do
|
147
|
+
ExampleForUpdate = Class.new do
|
148
|
+
include Model
|
149
|
+
|
150
|
+
attributes :name
|
151
|
+
|
152
|
+
validates :name, presence: true
|
153
|
+
end
|
154
|
+
|
155
|
+
ExampleForSave = Class.new do
|
156
|
+
include Model
|
157
|
+
|
158
|
+
model :user, attributes: %i(name), save: true
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'updates attributes' do
|
162
|
+
object = ExampleForUpdate.new name: 'value'
|
163
|
+
|
164
|
+
expect { object.update(name: 'new value') }.to change { object.name }.to 'new value'
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'returns true when validations pass' do
|
168
|
+
object = ExampleForUpdate.new name: 'value'
|
169
|
+
|
170
|
+
expect(object.update).to eq true
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'calls "perfom" method when validation pass' do
|
174
|
+
object = ExampleForUpdate.new name: 'value'
|
175
|
+
|
176
|
+
expect(object).to receive(:perform)
|
177
|
+
|
178
|
+
object.update
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'calls "save" for the model' do
|
182
|
+
object = ExampleForSave.new user: user
|
183
|
+
|
184
|
+
allow(user).to receive(:save!)
|
185
|
+
|
186
|
+
object.update
|
187
|
+
|
188
|
+
expect(user).to have_received(:save!)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'supports update callbacks' do
|
192
|
+
object = ExampleForUpdate.new name: 'value'
|
193
|
+
|
194
|
+
expect(object).to receive(:before_update)
|
195
|
+
expect(object).to receive(:after_update)
|
196
|
+
|
197
|
+
object.update
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'returns false when validations fail' do
|
201
|
+
object = ExampleForUpdate.new name: nil
|
202
|
+
|
203
|
+
expect(object.update).to eq false
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'does not call "perfom" method when validation fail' do
|
207
|
+
object = ExampleForUpdate.new name: nil
|
208
|
+
|
209
|
+
expect(object).not_to receive(:perform)
|
210
|
+
|
211
|
+
object.update
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe '#update!' do
|
216
|
+
it 'returns self' do
|
217
|
+
object = Example.new
|
218
|
+
expect(object.update!).to eq object
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'calls update with given arguments' do
|
222
|
+
object = Example.new
|
223
|
+
|
224
|
+
expect(object).to receive(:update).with(:attributes).and_return true
|
225
|
+
|
226
|
+
object.update! :attributes
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'raises error when update fails' do
|
230
|
+
object = Example.new
|
231
|
+
|
232
|
+
allow(object).to receive(:update).and_return false
|
233
|
+
|
234
|
+
expect { object.update! }.to raise_error InvalidForm
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mini_form
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Radoslav Stankov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activemodel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.14'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.14'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-mocks
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.12.3
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.12.3
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Sugar around ActiveModel::Model
|
126
|
+
email:
|
127
|
+
- rstankov@gmail.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".gitignore"
|
133
|
+
- ".rspec"
|
134
|
+
- ".rubocop.yml"
|
135
|
+
- ".travis.yml"
|
136
|
+
- CHANGELOG.md
|
137
|
+
- Gemfile
|
138
|
+
- LICENSE.txt
|
139
|
+
- README.md
|
140
|
+
- Rakefile
|
141
|
+
- lib/mini_form.rb
|
142
|
+
- lib/mini_form/errors.rb
|
143
|
+
- lib/mini_form/model.rb
|
144
|
+
- lib/mini_form/nested_validator.rb
|
145
|
+
- lib/mini_form/version.rb
|
146
|
+
- mini_form.gemspec
|
147
|
+
- spec/mini_form/nested_validator_spec.rb
|
148
|
+
- spec/mini_form_spec.rb
|
149
|
+
- spec/spec_helper.rb
|
150
|
+
homepage: https://github.com/RStankov/MiniForm
|
151
|
+
licenses:
|
152
|
+
- MIT
|
153
|
+
metadata: {}
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options: []
|
156
|
+
require_paths:
|
157
|
+
- lib
|
158
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
requirements: []
|
169
|
+
rubyforge_project:
|
170
|
+
rubygems_version: 2.4.5
|
171
|
+
signing_key:
|
172
|
+
specification_version: 4
|
173
|
+
summary: Easy to use form objects in Rails projects
|
174
|
+
test_files:
|
175
|
+
- spec/mini_form/nested_validator_spec.rb
|
176
|
+
- spec/mini_form_spec.rb
|
177
|
+
- spec/spec_helper.rb
|