mini_form 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d427b889425b8c2acd15827c88bc61430a70652
4
- data.tar.gz: 582b6cf7164ab75b6f4df2f306eeb5580d9a0f3c
3
+ metadata.gz: 6d0c0c470b74ebc5e81006fa9f4f226de8d821d6
4
+ data.tar.gz: 38393dfb6c142a1f85dca47a2aa16072a63f657f
5
5
  SHA512:
6
- metadata.gz: 9e0f762fd07558b4c30386d9cd2a9a51b8855efb0279bb70b7f842d0e3748d7a413a0f449a31eb1d448e8b93c649930b3b90dbbb9ece8a4151ae013dd0f4d096
7
- data.tar.gz: d23461cd996d47cbbdfe7efd5219aed986a72e83c5693e6d1e413927fa947786c200bd1e6bb1cd227573af8f4b1a8c9be7d89926111b4fc3bbd8fbd5efeec448
6
+ metadata.gz: 1f8c4900a91ceedd1b9f7c12b73e5b2fc30f033fd47783ae792a4c9e8dbb3f0bae93c65953da6ddebf1bbc75e3bb8c4484e863e33925b67672f11246e4b094b6
7
+ data.tar.gz: 5a3a73ad3547ba949a1900918a1cdcfefc73c8bf77d0764d89fa0858c6fe8ae075a0493066f80b65d62429160e5a93d2734303d75fcc66d8b8746596d3455730
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## Version 0.2.0
4
+
5
+ * Don't expose model name on `model`. _(security fix)_
6
+
7
+ * Included `ActiveModel::Validations::Callbacks` to `MiniForm::Model`
8
+
9
+ * Added read option to `model`:
10
+
11
+ ```ruby
12
+ class EditProfile
13
+ include MiniForm::Model
14
+
15
+ model :user, attributes: %i(email name), read:%(id)
16
+ end
17
+
18
+ profile = EditProfile.new(user: user)
19
+ profile.id
20
+ profile.id = 1 # raises NoMethodError
21
+ ```
22
+
23
+
24
+ * MiniForm::Model can be inherited
25
+ * Added `assigment` callbacks, called when attributes are assigned
26
+ * Added `assign_attributes` alias to `attributes=`
27
+ * Exposed `errors` on MiniForm::InvalidForm
28
+ * Added descriptive message MiniForm::InvalidForm
29
+ * Made `.attribute_names` public
30
+
3
31
  ## Version 0.1.0
4
32
 
5
33
  * Initial release
data/README.md CHANGED
@@ -46,7 +46,7 @@ class ProductsController < ApplicationController
46
46
  @product = ProductForm.new
47
47
 
48
48
  if @product.update(product_params)
49
- redirect_to product_path(product.id)
49
+ redirect_to product_path(@product.id)
50
50
  else
51
51
  render :edit
52
52
  end
@@ -130,7 +130,7 @@ Combines delegated attributes and nested validation into a single call.
130
130
  class SignUpForm
131
131
  include MiniForm::Model
132
132
 
133
- modal :user, attributes: %i(name email)
133
+ model :user, attributes: %i(name email)
134
134
  model :account, attributes: %i(company_name plan)
135
135
 
136
136
  def initialize
@@ -147,13 +147,13 @@ end
147
147
 
148
148
  ### Auto saving nested models
149
149
 
150
- In most of the time `perform` is just calling `save!`.
150
+ Most of the time `perform` is just calling `save!`. We can avoid this by using `model`'s `save` option.
151
151
 
152
152
  ```ruby
153
153
  class SignUpForm
154
154
  include MiniForm::Model
155
155
 
156
- modal :user, attributes: %i(name email), save: true
156
+ model :user, attributes: %i(name email), save: true
157
157
  model :account, attributes: %i(company_name plan), save: true
158
158
 
159
159
  def initialize
@@ -194,6 +194,27 @@ class SignUpForm
194
194
  end
195
195
  ```
196
196
 
197
+ ### Delegating model attributes
198
+
199
+ ```ruby
200
+ class SignUpForm
201
+ include MiniForm::Model
202
+
203
+ model :user, attributes: %i(name email), read: %i(id)
204
+
205
+ def initialize
206
+ @user = User.new account: @account
207
+ end
208
+ end
209
+ ```
210
+
211
+ ```
212
+ form = SignUpForm.new
213
+ form.update! form_params
214
+ form.id # => delegates to `user.id`
215
+ form.id = 42 # => raises `NoMethodError`
216
+ ```
217
+
197
218
  ### Methods
198
219
 
199
220
  <table>
@@ -209,6 +230,10 @@ end
209
230
  <td>.attributes</td>
210
231
  <td>Defines an attribute, it can delegate to sub object</td>
211
232
  </tr>
233
+ <tr>
234
+ <td>.attribute_names</td>
235
+ <td>Returns list of attribute names</td>
236
+ </tr>
212
237
  <tr>
213
238
  <td>#initialize</td>
214
239
  <td>Meant to be overwritten. By defaults calls `attributes=`</td>
@@ -241,6 +266,14 @@ end
241
266
  <td>#after_update</td>
242
267
  <td>Meant to be overwritten.</td>
243
268
  </tr>
269
+ <tr>
270
+ <td>#before_assigment</td>
271
+ <td>Meant to be overwritten.</td>
272
+ </tr>
273
+ <tr>
274
+ <td>#after_assigment</td>
275
+ <td>Meant to be overwritten.</td>
276
+ </tr>
244
277
  <tr>
245
278
  <td>#transaction</td>
246
279
  <td>If ActiveRecord is available, wraps `perform` in transaction.</td>
@@ -1,4 +1,11 @@
1
1
  module MiniForm
2
2
  class InvalidForm < StandardError
3
+ attr_reader :errors
4
+
5
+ def initialize(object)
6
+ @errors = object.errors
7
+
8
+ super "Form validation failed for: #{errors.keys.join(', ')}"
9
+ end
3
10
  end
4
11
  end
@@ -3,9 +3,10 @@ require 'active_model'
3
3
 
4
4
  module MiniForm
5
5
  module Model
6
- def self.included(base)
6
+ def self.included(base) # rubocop:disable MethodLength
7
7
  base.class_eval do
8
8
  include ActiveModel::Validations
9
+ include ActiveModel::Validations::Callbacks
9
10
  include ActiveModel::Conversion
10
11
 
11
12
  extend ActiveModel::Naming
@@ -14,9 +15,13 @@ module MiniForm
14
15
  extend ClassMethods
15
16
 
16
17
  define_model_callbacks :update
18
+ define_model_callbacks :assigment
17
19
 
18
20
  before_update :before_update
19
21
  after_update :after_update
22
+
23
+ before_assigment :before_assigment
24
+ after_assigment :after_assigment
20
25
  end
21
26
  end
22
27
 
@@ -29,11 +34,15 @@ module MiniForm
29
34
  end
30
35
 
31
36
  def attributes=(attributes)
32
- attributes.slice(*self.class.attribute_names).each do |name, value|
33
- public_send "#{name}=", value
37
+ run_callbacks :assigment do
38
+ attributes.slice(*self.class.attribute_names).each do |name, value|
39
+ public_send "#{name}=", value
40
+ end
34
41
  end
35
42
  end
36
43
 
44
+ alias_method :assign_attributes, :attributes=
45
+
37
46
  def attributes
38
47
  Hash[self.class.attribute_names.map { |name| [name, public_send(name)] }]
39
48
  end
@@ -54,7 +63,7 @@ module MiniForm
54
63
  end
55
64
 
56
65
  def update!(attributes = {})
57
- fail InvalidForm unless update attributes
66
+ fail InvalidForm, self unless update attributes
58
67
  self
59
68
  end
60
69
 
@@ -85,8 +94,25 @@ module MiniForm
85
94
  # noop
86
95
  end
87
96
 
97
+ def before_assigment
98
+ # noop
99
+ end
100
+
101
+ def after_assigment
102
+ # noop
103
+ end
104
+
88
105
  module ClassMethods
89
- # :api: private
106
+ def inherited(base)
107
+ new_attribute_names = attribute_names.dup
108
+ new_models_to_save = models_to_save.dup
109
+
110
+ base.instance_eval do
111
+ @attribute_names = new_attribute_names
112
+ @models_to_save = new_models_to_save
113
+ end
114
+ end
115
+
90
116
  def attribute_names
91
117
  @attribute_names ||= []
92
118
  end
@@ -102,15 +128,18 @@ module MiniForm
102
128
  if delegate.nil?
103
129
  attr_accessor(*attributes)
104
130
  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)
131
+ delegate(*attributes, to: delegate, prefix: prefix, allow_nil: allow_nil)
132
+ delegate(*attributes.map { |attr| "#{attr}=" }, to: delegate, prefix: prefix, allow_nil: allow_nil)
107
133
  end
108
134
  end
109
135
 
110
- def model(name, attributes: [], prefix: nil, allow_nil: nil, save: false)
111
- attributes(name)
136
+ def model(name, attributes: [], read: [], prefix: nil, allow_nil: nil, save: false) # rubocop:disable ParameterLists
137
+ attr_accessor name
138
+
112
139
  attributes(*attributes, delegate: name, prefix: prefix, allow_nil: allow_nil) unless attributes.empty?
113
140
 
141
+ delegate(*read, to: name, prefix: prefix, allow_nil: nil)
142
+
114
143
  validates name, 'mini_form/nested' => true
115
144
 
116
145
  models_to_save << name if save
@@ -1,3 +1,3 @@
1
1
  module MiniForm
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -2,18 +2,19 @@ require 'spec_helper'
2
2
 
3
3
  module MiniForm
4
4
  describe Model do
5
- let(:user) { User.new name: 'Name' }
5
+ let(:user) { User.new id: 1, name: 'name', age: 28 }
6
6
 
7
7
  class User
8
8
  include ActiveModel::Model
9
9
 
10
- attr_accessor :name
10
+ attr_accessor :id, :name, :age
11
11
 
12
12
  validates :name, presence: true
13
13
  end
14
14
 
15
15
  Example = Class.new do
16
16
  include Model
17
+
17
18
  attributes :name, :price
18
19
  end
19
20
 
@@ -29,12 +30,6 @@ module MiniForm
29
30
  end
30
31
  end
31
32
 
32
- ExampleWithModel = Class.new do
33
- include Model
34
-
35
- model :user, attributes: %i(name)
36
- end
37
-
38
33
  describe 'acts as ActiveModel' do
39
34
  include ActiveModel::Lint::Tests
40
35
 
@@ -59,6 +54,23 @@ module MiniForm
59
54
  end
60
55
  end
61
56
 
57
+ describe 'inheritance' do
58
+ it 'can be inherited' do
59
+ parent_class = Class.new do
60
+ include Model
61
+
62
+ attributes :name
63
+ end
64
+
65
+ child_class = Class.new(parent_class) do
66
+ attributes :age
67
+ end
68
+
69
+ expect(parent_class.attribute_names).to eq %i(name)
70
+ expect(child_class.attribute_names).to eq %i(name age)
71
+ end
72
+ end
73
+
62
74
  describe '.attributes' do
63
75
  it 'generates getters' do
64
76
  object = Example.new name: 'value'
@@ -88,19 +100,36 @@ module MiniForm
88
100
  end
89
101
 
90
102
  describe '.model' do
103
+ ExampleWithModel = Class.new do
104
+ include Model
105
+
106
+ model :user, attributes: %i(name), read: %i(id)
107
+
108
+ def initialize(user)
109
+ self.user = user
110
+ end
111
+ end
112
+
91
113
  it 'generates model accessors' do
92
- object = ExampleWithModel.new user: user
114
+ object = ExampleWithModel.new user
93
115
  expect(object.user).to eq user
94
116
  end
95
117
 
118
+ it 'can delegate only a reader' do
119
+ object = ExampleWithModel.new user
120
+
121
+ expect(object).not_to respond_to :id=
122
+ expect(object.id).to eq user.id
123
+ end
124
+
96
125
  it 'can delegate model attributes' do
97
- object = ExampleWithModel.new user: user
126
+ object = ExampleWithModel.new user
98
127
  expect(object.name).to eq user.name
99
128
  end
100
129
 
101
130
  it 'performs nested validation for model' do
102
131
  user = User.new
103
- object = ExampleWithModel.new user: user
132
+ object = ExampleWithModel.new user
104
133
 
105
134
  expect(object).not_to be_valid
106
135
  expect(object.errors[:name]).to be_present
@@ -143,6 +172,24 @@ module MiniForm
143
172
  end
144
173
  end
145
174
 
175
+ ['attributes=', 'assign_attributes'].each do |method_name|
176
+ describe "##{method_name}" do
177
+ it 'sets attributes' do
178
+ object = Example.new
179
+ object.public_send method_name, name: 'iPhone', price: '$5'
180
+
181
+ expect(object.attributes).to eq name: 'iPhone', price: '$5'
182
+ end
183
+
184
+ it 'ignores not listed attributes' do
185
+ object = Example.new
186
+ object.public_send method_name, invalid: 'value'
187
+
188
+ expect(object.attributes).to eq name: nil, price: nil
189
+ end
190
+ end
191
+ end
192
+
146
193
  describe '#update' do
147
194
  ExampleForUpdate = Class.new do
148
195
  include Model
@@ -156,6 +203,10 @@ module MiniForm
156
203
  include Model
157
204
 
158
205
  model :user, attributes: %i(name), save: true
206
+
207
+ def initialize(user: user)
208
+ self.user = user
209
+ end
159
210
  end
160
211
 
161
212
  it 'updates attributes' do
@@ -197,6 +248,15 @@ module MiniForm
197
248
  object.update
198
249
  end
199
250
 
251
+ it 'supports assign callbacks' do
252
+ object = ExampleForUpdate.new
253
+
254
+ expect(object).to receive(:before_assigment)
255
+ expect(object).to receive(:after_assigment)
256
+
257
+ object.update name: 'value'
258
+ end
259
+
200
260
  it 'returns false when validations fail' do
201
261
  object = ExampleForUpdate.new name: nil
202
262
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radoslav Stankov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-02 00:00:00.000000000 Z
11
+ date: 2015-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel