forminate 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -71,7 +71,7 @@ module Forminate
71
71
  # validation hash.
72
72
  def association_validators
73
73
  associations.reduce({}) do |assoc_validators, (name, object)|
74
- if should_validate_assoc?(name) && object.respond_to?(:_validators)
74
+ if validate_assoc?(name) && object.respond_to?(:_validators)
75
75
  object._validators.each do |attr, validators|
76
76
  new_validators = validators.reduce([]) do |new_validators, validator|
77
77
  new_validator = validator.dup
@@ -14,13 +14,13 @@ describe Forminate::ClientSideValidations do
14
14
  attribute :tax
15
15
 
16
16
  attributes_for :dummy_user
17
- attributes_for :dummy_book, :validate => false
18
- attributes_for :dummy_credit_card, :validate => :require_credit_card?
17
+ attributes_for :dummy_book, validate: false
18
+ attributes_for :dummy_credit_card, validate: :require_credit_card?
19
19
 
20
20
  validates_numericality_of :total
21
21
 
22
22
  def self.name
23
- "Cart"
23
+ 'Cart'
24
24
  end
25
25
 
26
26
  def calculate_total
@@ -33,11 +33,11 @@ describe Forminate::ClientSideValidations do
33
33
  end
34
34
  end
35
35
 
36
- describe "#client_side_validation_hash" do
37
- it "constructs a hash of validations and messages for use with the client_side_validations gem" do
36
+ describe '#client_side_validation_hash' do
37
+ it 'constructs a hash of validations and messages for use with the client_side_validations gem' do
38
38
  expected_hash = {
39
39
  total: {
40
- numericality: [{ messages: { numericality: "is not a number" } }]
40
+ numericality: [{ messages: { numericality: 'is not a number' } }]
41
41
  },
42
42
  dummy_user_email: {
43
43
  presence: [{ message: "can't be blank" }]
@@ -46,7 +46,7 @@ describe Forminate::ClientSideValidations do
46
46
  expect(model.client_side_validation_hash).to eq(expected_hash)
47
47
  expected_hash = {
48
48
  total: {
49
- numericality: [{ messages: { numericality: "is not a number" } }]
49
+ numericality: [{ messages: { numericality: 'is not a number' } }]
50
50
  },
51
51
  dummy_user_email: {
52
52
  presence: [{ message: "can't be blank" }]
@@ -55,13 +55,13 @@ describe Forminate::ClientSideValidations do
55
55
  presence: [{ message: "can't be blank" }],
56
56
  length: [{
57
57
  messages: {
58
- minimum: "is too short (minimum is 12 characters)",
59
- maximum: "is too long (maximum is 19 characters)"
58
+ minimum: 'is too short (minimum is 12 characters)',
59
+ maximum: 'is too long (maximum is 19 characters)'
60
60
  },
61
61
  minimum: 12,
62
62
  maximum: 19
63
63
  }]
64
- },
64
+ }
65
65
  }
66
66
  model.dummy_book_price = 12.95
67
67
  expect(model.client_side_validation_hash).to eq(expected_hash)
@@ -0,0 +1,250 @@
1
+ require 'spec_helper'
2
+
3
+ describe Forminate do
4
+ subject(:model) { model_class.new }
5
+
6
+ let :model_class do
7
+ Class.new do
8
+ include Forminate
9
+
10
+ attribute :total
11
+ attribute :tax
12
+
13
+ attributes_for :dummy_user
14
+ attributes_for :dummy_book, validate: false
15
+ attributes_for :dummy_credit_card, validate: :require_credit_card?
16
+
17
+ validates_numericality_of :total
18
+
19
+ def self.name
20
+ 'Cart'
21
+ end
22
+
23
+ def calculate_total
24
+ self.total = dummy_book.price || 0.0
25
+ end
26
+
27
+ def require_credit_card?
28
+ dummy_book.price && dummy_book.price > 0.0
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '.attributes_for' do
34
+ it 'adds a reader method for each attribute of the associated model' do
35
+ expect(model.respond_to?(:dummy_user_first_name)).to be_true
36
+ end
37
+
38
+ it 'adds reader and writer methods for each attribute of the associated model' do
39
+ model.dummy_user_first_name = 'Mo'
40
+ expect(model.dummy_user_first_name).to eq('Mo')
41
+ end
42
+
43
+ it 'adds reader methods for each associated model' do
44
+ expect(model.dummy_user).to be_an_instance_of(DummyUser)
45
+ end
46
+
47
+ it 'adds the association to the list of association names' do
48
+ expect(model_class.association_names).to include(:dummy_user)
49
+ end
50
+
51
+ describe ':validate option' do
52
+ context 'true or false' do
53
+ it 'validates associated object based value given' do
54
+ model.calculate_total
55
+ expect(model.valid?).to be_false
56
+ model.dummy_user_email = 'bob@example.com'
57
+ expect(model.valid?).to be_true
58
+ end
59
+ end
60
+
61
+ context 'method name (as a symbol) that evaluates to true or false' do
62
+ it 'validates associated objects based on result of method call' do
63
+ model.calculate_total
64
+ model.dummy_user_email = 'bob@example.com'
65
+ expect(model.valid?).to be_true
66
+ model.dummy_book_price = 12.95
67
+ expect(model.valid?).to be_false
68
+ model.dummy_credit_card_number = 4242424242424242
69
+ expect(model.valid?).to be_true
70
+ end
71
+ end
72
+
73
+ context 'invalid value' do
74
+ let(:bad_model_class) do
75
+ Class.new do
76
+ include Forminate
77
+ attributes_for :dummy_book, validate: [:say, :what?]
78
+ end
79
+ end
80
+
81
+ it 'raises an NotImplemented error' do
82
+ expect { bad_model_class.new.valid? }
83
+ .to raise_error(NotImplementedError)
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '.attribute_names' do
90
+ it 'includes the names of its own attributes and the attributes of associated models' do
91
+ expected_attributes = %w(
92
+ total
93
+ tax
94
+ dummy_user_id
95
+ dummy_user_first_name
96
+ dummy_user_last_name
97
+ dummy_user_email
98
+ dummy_book_title
99
+ dummy_book_price
100
+ dummy_credit_card_number
101
+ dummy_credit_card_expiration
102
+ dummy_credit_card_cvv
103
+ )
104
+ expect(model_class.attribute_names).to eq(expected_attributes)
105
+ end
106
+ end
107
+
108
+ describe '.association_names' do
109
+ it 'includes the names of associated models' do
110
+ expect(model_class.association_names)
111
+ .to eq([:dummy_user, :dummy_book, :dummy_credit_card])
112
+ end
113
+ end
114
+
115
+ describe '.association_validations' do
116
+ it 'includes the names and conditions of association validations' do
117
+ expect(model_class.association_validations)
118
+ .to eq(
119
+ dummy_user: true,
120
+ dummy_book: false,
121
+ dummy_credit_card: :require_credit_card?
122
+ )
123
+ end
124
+ end
125
+
126
+ describe '#initialize' do
127
+ it 'builds associated objects and creates reader methods' do
128
+ expect(model.dummy_user).to be_an_instance_of(DummyUser)
129
+ end
130
+
131
+ it 'creates writer methods for associated objects' do
132
+ new_dummy_user = DummyUser.new(first_name: 'Mo')
133
+ expect(model.dummy_user).to_not be(new_dummy_user)
134
+ model.dummy_user = new_dummy_user
135
+ expect(model.dummy_user).to be(new_dummy_user)
136
+ end
137
+
138
+ it 'sets association attributes based on an options hash' do
139
+ new_model = model_class.new(
140
+ dummy_user_first_name: 'Mo',
141
+ dummy_user_last_name: 'Lawson',
142
+ dummy_book_title: 'The Hobbit'
143
+ )
144
+ expect(new_model.dummy_user_first_name).to eq('Mo')
145
+ expect(new_model.dummy_user_last_name).to eq('Lawson')
146
+ end
147
+
148
+ context 'primary key of an associated AR model is present' do
149
+ it 'populates the matching model with values from the database' do
150
+ user = DummyUser.create(
151
+ first_name: 'Mo',
152
+ last_name: 'Lawson',
153
+ email: 'mo@example.com'
154
+ )
155
+ new_model = model_class.new(dummy_user_id: user.id)
156
+ expect(new_model.dummy_user_first_name).to eq('Mo')
157
+ end
158
+ end
159
+
160
+ it 'sets attributes based on an options hash' do
161
+ new_model = model_class.new(total: 21.49)
162
+ expect(new_model.total).to eq(21.49)
163
+ end
164
+ end
165
+
166
+ describe '#association_names' do
167
+ it 'delegates to self.association_names' do
168
+ expect(model.association_names).to eq(model_class.association_names)
169
+ end
170
+ end
171
+
172
+ describe '#associations' do
173
+ it 'returns a hash of association names and associated objects' do
174
+ expect(model.associations[:dummy_user]).to be_an_instance_of(DummyUser)
175
+ end
176
+ end
177
+
178
+ describe '#save' do
179
+ context 'object is valid' do
180
+ it 'saves associations and returns self' do
181
+ model.dummy_user_email = 'bob@example.com'
182
+ model.calculate_total
183
+ DummyUser.any_instance.should_receive(:save)
184
+ expect(model.save).to eq(model)
185
+ end
186
+ end
187
+
188
+ context 'object is not valid' do
189
+ it 'does not save associations and returns false' do
190
+ DummyUser.any_instance.should_not_receive(:save)
191
+ expect(model.save).to be_false
192
+ end
193
+ end
194
+ end
195
+
196
+ context 'setting an attribute using the attribute name' do
197
+ it 'reflects the change on the associated object' do
198
+ model.dummy_user_first_name = 'Mo'
199
+ expect(model.dummy_user.first_name).to eq('Mo')
200
+ end
201
+ end
202
+
203
+ context "setting an attribute using the association's attribute" do
204
+ it 'reflects the change on the attribute name' do
205
+ model.dummy_user.last_name = 'Lawson'
206
+ expect(model.dummy_user_last_name).to eq('Lawson')
207
+ end
208
+ end
209
+
210
+ describe '#method_missing' do
211
+ context 'associated object responds to method' do
212
+ it 'returns the value from the association' do
213
+ new_model = model_class.new(dummy_user_temporary_note: 'Only stopping by')
214
+ expect(new_model.dummy_user_temporary_note).to eq('Only stopping by')
215
+ end
216
+ end
217
+
218
+ context 'associated object does not respond to method' do
219
+ it 'raises a NoMethodError' do
220
+ expect { model.dummy_user_bogus_method }.to raise_error(NoMethodError)
221
+ end
222
+ end
223
+ end
224
+
225
+ describe '#respond_to_missing?' do
226
+ context 'associated object responds to method' do
227
+ it 'returns true' do
228
+ expect(model.respond_to?(:dummy_user_temporary_note)).to be_true
229
+ end
230
+ end
231
+
232
+ context 'associated object does not respond to method' do
233
+ it 'returns false' do
234
+ expect(model.respond_to?(:dummy_user_bogus_method)).to be_false
235
+ end
236
+ end
237
+ end
238
+
239
+ it 'delegates to attr_accessors of associated objects' do
240
+ model.dummy_user_temporary_note = 'Only stopping by'
241
+ expect(model.dummy_user.temporary_note).to eq('Only stopping by')
242
+ expect(model.dummy_user_temporary_note).to eq('Only stopping by')
243
+ end
244
+
245
+ it 'inherits the validations of its associated objects' do
246
+ model.valid?
247
+ expect(model.errors.full_messages)
248
+ .to eq(["Dummy user email can't be blank", 'Total is not a number'])
249
+ end
250
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,23 @@
1
+ require 'codeclimate-test-reporter'
2
+ CodeClimate::TestReporter.start
3
+
1
4
  require 'bundler/setup'
2
5
  require 'forminate'
3
6
 
7
+ require 'active_record'
8
+ ActiveRecord::Base.establish_connection(
9
+ adapter: 'sqlite3',
10
+ database: ':memory:'
11
+ )
12
+ ActiveRecord::Schema.define do
13
+ suppress_messages do
14
+ create_table :dummy_users do |t|
15
+ t.string :first_name
16
+ t.string :last_name
17
+ t.string :email
18
+ end
19
+ end
20
+ end
21
+
4
22
  # Requires supporting files in spec/support/
5
23
  Dir["#{File.dirname(__FILE__)}/support/*.rb"].each { |file| require file }
@@ -1,5 +1,3 @@
1
- require 'active_attr'
2
-
3
1
  class DummyBook
4
2
  include ActiveAttr::Model
5
3
 
@@ -1,5 +1,3 @@
1
- require 'active_attr'
2
-
3
1
  class DummyCreditCard
4
2
  include ActiveAttr::Model
5
3
 
@@ -1,17 +1,5 @@
1
- require 'active_attr'
2
-
3
- class DummyUser
4
- include ActiveAttr::Model
5
-
6
- attribute :first_name
7
- attribute :last_name
8
- attribute :email
9
-
10
- attr_accessor :full_name
11
-
1
+ class DummyUser < ActiveRecord::Base
12
2
  validates_presence_of :email
13
3
 
14
- def save
15
- # fake a persisted model
16
- end
4
+ attr_accessor :temporary_note
17
5
  end
metadata CHANGED
@@ -1,125 +1,157 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forminate
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
  - Mo Lawson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-06 00:00:00.000000000 Z
11
+ date: 2014-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_attr
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0.8'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.8'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 3.0.2
34
- - - <
34
+ - - "<"
35
35
  - !ruby/object:Gem::Version
36
36
  version: '4.1'
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
- - - '>='
41
+ - - ">="
42
42
  - !ruby/object:Gem::Version
43
43
  version: 3.0.2
44
- - - <
44
+ - - "<"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '4.1'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: client_side_validations
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ~>
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '3.2'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ~>
58
+ - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '3.2'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: bundler
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - ~>
65
+ - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '1.3'
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
- - - ~>
72
+ - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: '1.3'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: rake
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - '>='
79
+ - - ">="
80
80
  - !ruby/object:Gem::Version
81
81
  version: '0'
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - '>='
86
+ - - ">="
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: rspec
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - ~>
93
+ - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '2.11'
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
- - - ~>
100
+ - - "~>"
101
101
  - !ruby/object:Gem::Version
102
102
  version: '2.11'
103
+ - !ruby/object:Gem::Dependency
104
+ name: activerecord
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.2'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.2'
117
+ - !ruby/object:Gem::Dependency
118
+ name: sqlite3
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
103
131
  description: Form objects for Rails applications
104
132
  email:
105
- - moklawson@gmail.com
133
+ - mo@molawson.com
106
134
  executables: []
107
135
  extensions: []
108
136
  extra_rdoc_files: []
109
137
  files:
110
- - .gitignore
111
- - .travis.yml
138
+ - ".gitignore"
139
+ - ".travis.yml"
112
140
  - Gemfile
113
141
  - LICENSE.txt
114
142
  - README.md
115
143
  - Rakefile
116
144
  - forminate.gemspec
117
145
  - lib/forminate.rb
146
+ - lib/forminate/association_builder.rb
147
+ - lib/forminate/association_definition.rb
118
148
  - lib/forminate/client_side_validations.rb
119
149
  - lib/forminate/version.rb
120
- - spec/lib/forminate/client_side_validations.rb
121
- - spec/lib/forminate/client_side_validations_spec.rb
122
- - spec/lib/forminate_spec.rb
150
+ - spec/forminate/association_builder_spec.rb
151
+ - spec/forminate/association_definition_spec.rb
152
+ - spec/forminate/client_side_validations.rb
153
+ - spec/forminate/client_side_validations_spec.rb
154
+ - spec/forminate_spec.rb
123
155
  - spec/spec_helper.rb
124
156
  - spec/support/dummy_book.rb
125
157
  - spec/support/dummy_credit_card.rb
@@ -134,24 +166,26 @@ require_paths:
134
166
  - lib
135
167
  required_ruby_version: !ruby/object:Gem::Requirement
136
168
  requirements:
137
- - - '>='
169
+ - - ">="
138
170
  - !ruby/object:Gem::Version
139
171
  version: '0'
140
172
  required_rubygems_version: !ruby/object:Gem::Requirement
141
173
  requirements:
142
- - - '>='
174
+ - - ">="
143
175
  - !ruby/object:Gem::Version
144
176
  version: '0'
145
177
  requirements: []
146
178
  rubyforge_project:
147
- rubygems_version: 2.0.3
179
+ rubygems_version: 2.2.2
148
180
  signing_key:
149
181
  specification_version: 4
150
182
  summary: Create form objects from multiple Active Record and/or ActiveAttr models.
151
183
  test_files:
152
- - spec/lib/forminate/client_side_validations.rb
153
- - spec/lib/forminate/client_side_validations_spec.rb
154
- - spec/lib/forminate_spec.rb
184
+ - spec/forminate/association_builder_spec.rb
185
+ - spec/forminate/association_definition_spec.rb
186
+ - spec/forminate/client_side_validations.rb
187
+ - spec/forminate/client_side_validations_spec.rb
188
+ - spec/forminate_spec.rb
155
189
  - spec/spec_helper.rb
156
190
  - spec/support/dummy_book.rb
157
191
  - spec/support/dummy_credit_card.rb