active_presenter 0.0.6 → 1.1.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.
- data/lib/active_presenter/base.rb +55 -20
- data/lib/active_presenter/version.rb +3 -3
- data/test/base_test.rb +96 -4
- data/test/test_helper.rb +104 -4
- metadata +2 -2
@@ -3,7 +3,7 @@ module ActivePresenter
|
|
3
3
|
#
|
4
4
|
class Base
|
5
5
|
include ActiveSupport::Callbacks
|
6
|
-
define_callbacks :before_save, :after_save
|
6
|
+
define_callbacks :before_validation, :before_save, :after_save
|
7
7
|
|
8
8
|
class_inheritable_accessor :presented
|
9
9
|
self.presented = {}
|
@@ -36,8 +36,6 @@ module ActivePresenter
|
|
36
36
|
attribute_name.to_s.gsub("#{presentable_type}_", "").humanize
|
37
37
|
end
|
38
38
|
|
39
|
-
attr_accessor :errors
|
40
|
-
|
41
39
|
# Accepts arguments in two forms. For example, if you had a SignupPresenter that presented User, and Account, you could specify arguments in the following two forms:
|
42
40
|
#
|
43
41
|
# 1. SignupPresenter.new(:user_login => 'james', :user_password => 'swordfish', :user_password_confirmation => 'swordfish', :account_subdomain => 'giraffesoft')
|
@@ -60,10 +58,26 @@ module ActivePresenter
|
|
60
58
|
self.attributes = args
|
61
59
|
end
|
62
60
|
|
63
|
-
# Set the attributes of the presentable instances using
|
61
|
+
# Set the attributes of the presentable instances using
|
62
|
+
# the type_attribute form (i.e. user_login => 'james'), or
|
63
|
+
# the multiparameter attribute form (i.e. {user_birthday(1i) => "1980", user_birthday(2i) => "3"})
|
64
64
|
#
|
65
65
|
def attributes=(attrs)
|
66
|
-
|
66
|
+
multi_parameter_attributes = {}
|
67
|
+
|
68
|
+
attrs.each do |k,v|
|
69
|
+
if (base_attribute = k.to_s.split("(").first) != k.to_s
|
70
|
+
presentable = presentable_for(base_attribute)
|
71
|
+
multi_parameter_attributes[presentable] ||= {}
|
72
|
+
multi_parameter_attributes[presentable].merge!(flatten_attribute_name(k,presentable).to_sym => v)
|
73
|
+
else
|
74
|
+
send("#{k}=", v) unless attribute_protected?(k)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
multi_parameter_attributes.each do |presentable,multi_attrs|
|
79
|
+
send(presentable).send(:attributes=, multi_attrs)
|
80
|
+
end
|
67
81
|
end
|
68
82
|
|
69
83
|
# Makes sure that the presenter is accurate about responding to presentable's attributes, even though they are handled by method_missing.
|
@@ -87,13 +101,17 @@ module ActivePresenter
|
|
87
101
|
# Returns boolean based on the validity of the presentables by calling valid? on each of them.
|
88
102
|
#
|
89
103
|
def valid?
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
104
|
+
errors.clear
|
105
|
+
if run_callbacks_with_halt(:before_validation)
|
106
|
+
presented.keys.each do |type|
|
107
|
+
presented_inst = send(type)
|
108
|
+
|
109
|
+
next unless save?(type, presented_inst)
|
110
|
+
merge_errors(presented_inst, type) unless presented_inst.valid?
|
111
|
+
end
|
112
|
+
|
113
|
+
errors.empty?
|
94
114
|
end
|
95
|
-
|
96
|
-
errors.empty?
|
97
115
|
end
|
98
116
|
|
99
117
|
# Save all of the presentables, wrapped in a transaction.
|
@@ -105,29 +123,31 @@ module ActivePresenter
|
|
105
123
|
|
106
124
|
ActiveRecord::Base.transaction do
|
107
125
|
if valid? && run_callbacks_with_halt(:before_save)
|
108
|
-
saved =
|
126
|
+
saved = presented.keys.select {|key| save?(key, send(key))}.all? {|key| send(key).save}
|
109
127
|
raise ActiveRecord::Rollback unless saved # TODO: Does this happen implicitly?
|
110
128
|
end
|
129
|
+
|
130
|
+
run_callbacks_with_halt(:after_save) if saved
|
111
131
|
end
|
112
132
|
|
113
|
-
run_callbacks_with_halt(:after_save) if saved
|
114
|
-
|
115
133
|
saved
|
116
134
|
end
|
117
135
|
|
118
|
-
# Save all of the presentables
|
136
|
+
# Save all of the presentables wrapped in a transaction.
|
119
137
|
#
|
120
138
|
# Returns true on success, will raise otherwise.
|
121
139
|
#
|
122
140
|
def save!
|
141
|
+
raise ActiveRecord::RecordInvalid.new(self) unless valid?
|
123
142
|
raise ActiveRecord::RecordNotSaved unless run_callbacks_with_halt(:before_save)
|
124
143
|
|
125
144
|
ActiveRecord::Base.transaction do
|
126
|
-
|
127
|
-
|
145
|
+
presented.keys.select {|key| save?(key, send(key))}.each {|key| send(key).save!}
|
146
|
+
|
147
|
+
run_callbacks_with_halt(:after_save)
|
128
148
|
end
|
129
|
-
|
130
|
-
|
149
|
+
|
150
|
+
true
|
131
151
|
end
|
132
152
|
|
133
153
|
# Update attributes, and save the presentables
|
@@ -139,6 +159,21 @@ module ActivePresenter
|
|
139
159
|
save
|
140
160
|
end
|
141
161
|
|
162
|
+
# Should this presented instance be saved? By default, this returns true
|
163
|
+
# Called from #save and #save!
|
164
|
+
#
|
165
|
+
# For
|
166
|
+
# class SignupPresenter < ActivePresenter::Base
|
167
|
+
# presents :account, :user
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# #save? will be called twice:
|
171
|
+
# save?(:account, #<Account:0x1234dead>)
|
172
|
+
# save?(:user, #<User:0xdeadbeef>)
|
173
|
+
def save?(presented_key, presented_instance)
|
174
|
+
true
|
175
|
+
end
|
176
|
+
|
142
177
|
# We define #id and #new_record? to play nice with form_for(@presenter) in Rails
|
143
178
|
def id # :nodoc:
|
144
179
|
nil
|
@@ -147,7 +182,7 @@ module ActivePresenter
|
|
147
182
|
def new_record?
|
148
183
|
true
|
149
184
|
end
|
150
|
-
|
185
|
+
|
151
186
|
protected
|
152
187
|
def presented_instances
|
153
188
|
presented.keys.map { |key| send(key) }
|
data/test/base_test.rb
CHANGED
@@ -91,18 +91,18 @@ Expectations do
|
|
91
91
|
end
|
92
92
|
|
93
93
|
expect ActiveRecord::Base.to.receive(:transaction) do
|
94
|
-
s = SignupPresenter.new
|
94
|
+
s = SignupPresenter.new(:user_login => "da", :user_password => "seekrit")
|
95
95
|
s.save!
|
96
96
|
end
|
97
97
|
|
98
98
|
expect User.any_instance.to.receive(:save!) do
|
99
|
-
s = SignupPresenter.new
|
99
|
+
s = SignupPresenter.new(:user_login => "da", :user_password => "seekrit")
|
100
100
|
s.save!
|
101
101
|
end
|
102
102
|
|
103
103
|
expect Account.any_instance.to.receive(:save!) do
|
104
104
|
User.any_instance.stubs(:save!)
|
105
|
-
s = SignupPresenter.new
|
105
|
+
s = SignupPresenter.new(:user_login => "da", :user_password => "seekrit")
|
106
106
|
s.save!
|
107
107
|
end
|
108
108
|
|
@@ -130,7 +130,21 @@ Expectations do
|
|
130
130
|
s.update_attributes :user_login => 'Something Different'
|
131
131
|
s.user_login
|
132
132
|
end
|
133
|
-
|
133
|
+
|
134
|
+
# Multiparameter assignment
|
135
|
+
expect Time.parse('March 27 1980 9:30:59 am') do
|
136
|
+
s = SignupPresenter.new
|
137
|
+
s.update_attributes({
|
138
|
+
:"user_birthday(1i)" => '1980',
|
139
|
+
:"user_birthday(2i)" => '3',
|
140
|
+
:"user_birthday(3i)" => '27',
|
141
|
+
:"user_birthday(4i)" => '9',
|
142
|
+
:"user_birthday(5i)" => '30',
|
143
|
+
:"user_birthday(6i)" => '59'
|
144
|
+
})
|
145
|
+
s.user_birthday
|
146
|
+
end
|
147
|
+
|
134
148
|
# this is a regression test to make sure that _title is working. we had a weird conflict with using String#delete
|
135
149
|
expect 'something' do
|
136
150
|
s = SignupPresenter.new :account_title => 'something'
|
@@ -164,6 +178,17 @@ Expectations do
|
|
164
178
|
expect NoMethodError do
|
165
179
|
SignupPresenter.new({:i_dont_exist=>"blah"})
|
166
180
|
end
|
181
|
+
|
182
|
+
# ActiveRecord::Base uses nil id to signify an unsaved model
|
183
|
+
expect nil do
|
184
|
+
SignupPresenter.new.id
|
185
|
+
end
|
186
|
+
|
187
|
+
expect nil do
|
188
|
+
returning(SignupPresenter.new(:user => User.new(hash_for_user))) do |presenter|
|
189
|
+
presenter.save!
|
190
|
+
end.id
|
191
|
+
end
|
167
192
|
|
168
193
|
expect CantSavePresenter.new.not.to.be.save # it won't save because the filter chain will halt
|
169
194
|
|
@@ -185,4 +210,71 @@ Expectations do
|
|
185
210
|
|
186
211
|
expect SamePrefixPresenter.new.to.be.respond_to?(:account_title)
|
187
212
|
expect SamePrefixPresenter.new.to.be.respond_to?(:account_info_info)
|
213
|
+
|
214
|
+
expect [:before_validation, :before_save, :after_save] do
|
215
|
+
returning(CallbackOrderingPresenter.new) do |presenter|
|
216
|
+
presenter.save!
|
217
|
+
end.steps
|
218
|
+
end
|
219
|
+
|
220
|
+
expect [:before_validation, :before_save] do
|
221
|
+
returning(CallbackCantSavePresenter.new) do |presenter|
|
222
|
+
presenter.save
|
223
|
+
end.steps
|
224
|
+
end
|
225
|
+
|
226
|
+
expect [:before_validation, :before_save] do
|
227
|
+
returning(CallbackCantSavePresenter.new) do |presenter|
|
228
|
+
begin
|
229
|
+
presenter.save!
|
230
|
+
rescue ActiveRecord::RecordNotSaved
|
231
|
+
# NOP
|
232
|
+
end
|
233
|
+
end.steps
|
234
|
+
end
|
235
|
+
|
236
|
+
expect ActiveRecord::RecordNotSaved do
|
237
|
+
CallbackCantSavePresenter.new.save!
|
238
|
+
end
|
239
|
+
|
240
|
+
expect ActiveRecord::RecordInvalid do
|
241
|
+
CallbackCantValidatePresenter.new.save!
|
242
|
+
end
|
243
|
+
|
244
|
+
expect [:before_validation] do
|
245
|
+
returning(CallbackCantValidatePresenter.new) do |presenter|
|
246
|
+
begin
|
247
|
+
presenter.save!
|
248
|
+
rescue ActiveRecord::RecordInvalid
|
249
|
+
# NOP
|
250
|
+
end
|
251
|
+
end.steps
|
252
|
+
end
|
253
|
+
|
254
|
+
expect [:before_validation] do
|
255
|
+
returning(CallbackCantValidatePresenter.new) do |presenter|
|
256
|
+
presenter.save
|
257
|
+
end.steps
|
258
|
+
end
|
259
|
+
|
260
|
+
expect ActiveRecord::Errors.any_instance.to.receive(:clear) do
|
261
|
+
CallbackCantValidatePresenter.new.valid?
|
262
|
+
end
|
263
|
+
|
264
|
+
# this should act as ActiveRecord models do
|
265
|
+
expect NoMethodError do
|
266
|
+
SignupPresenter.new({:i_dont_exist=>"blah"})
|
267
|
+
end
|
268
|
+
|
269
|
+
expect false do
|
270
|
+
SignupNoNilPresenter.new.save
|
271
|
+
end
|
272
|
+
|
273
|
+
expect true do
|
274
|
+
SignupNoNilPresenter.new(:user => nil, :account => Account.new).save
|
275
|
+
end
|
276
|
+
|
277
|
+
expect true do
|
278
|
+
SignupNoNilPresenter.new(:user => nil, :account => Account.new).save!
|
279
|
+
end
|
188
280
|
end
|
data/test/test_helper.rb
CHANGED
@@ -10,9 +10,10 @@ ActiveRecord::Base.logger.level = Logger::WARN
|
|
10
10
|
|
11
11
|
ActiveRecord::Schema.define(:version => 0) do
|
12
12
|
create_table :users do |t|
|
13
|
-
t.boolean
|
14
|
-
t.string
|
15
|
-
t.string
|
13
|
+
t.boolean :admin, :default => false
|
14
|
+
t.string :login, :default => ''
|
15
|
+
t.string :password, :default => ''
|
16
|
+
t.datetime :birthday
|
16
17
|
end
|
17
18
|
|
18
19
|
create_table :accounts do |t|
|
@@ -31,7 +32,7 @@ end
|
|
31
32
|
|
32
33
|
class User < ActiveRecord::Base
|
33
34
|
validates_presence_of :login, :password
|
34
|
-
attr_accessible :login, :password
|
35
|
+
attr_accessible :login, :password, :birthday
|
35
36
|
attr_accessor :password_confirmation
|
36
37
|
end
|
37
38
|
class Account < ActiveRecord::Base; end
|
@@ -54,6 +55,14 @@ class CantSavePresenter < ActivePresenter::Base
|
|
54
55
|
def halt; false; end
|
55
56
|
end
|
56
57
|
|
58
|
+
class SignupNoNilPresenter < ActivePresenter::Base
|
59
|
+
presents :account, :user
|
60
|
+
|
61
|
+
def save?(key, instance)
|
62
|
+
!instance.nil?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
57
66
|
class AfterSavePresenter < ActivePresenter::Base
|
58
67
|
presents :address
|
59
68
|
|
@@ -68,6 +77,97 @@ class SamePrefixPresenter < ActivePresenter::Base
|
|
68
77
|
presents :account, :account_info
|
69
78
|
end
|
70
79
|
|
80
|
+
class CallbackOrderingPresenter < ActivePresenter::Base
|
81
|
+
presents :account
|
82
|
+
|
83
|
+
before_validation :do_before_validation
|
84
|
+
before_save :do_before_save
|
85
|
+
after_save :do_after_save
|
86
|
+
|
87
|
+
attr_reader :steps
|
88
|
+
|
89
|
+
def initialize(params={})
|
90
|
+
super
|
91
|
+
@steps = []
|
92
|
+
end
|
93
|
+
|
94
|
+
def do_before_validation
|
95
|
+
@steps << :before_validation
|
96
|
+
end
|
97
|
+
|
98
|
+
def do_before_save
|
99
|
+
@steps << :before_save
|
100
|
+
end
|
101
|
+
|
102
|
+
def do_after_save
|
103
|
+
@steps << :after_save
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class CallbackCantSavePresenter < ActivePresenter::Base
|
108
|
+
presents :account
|
109
|
+
|
110
|
+
before_validation :do_before_validation
|
111
|
+
before_save :do_before_save
|
112
|
+
before_save :halt
|
113
|
+
after_save :do_after_save
|
114
|
+
|
115
|
+
attr_reader :steps
|
116
|
+
|
117
|
+
def initialize(params={})
|
118
|
+
super
|
119
|
+
@steps = []
|
120
|
+
end
|
121
|
+
|
122
|
+
def do_before_validation
|
123
|
+
@steps << :before_validation
|
124
|
+
end
|
125
|
+
|
126
|
+
def do_before_save
|
127
|
+
@steps << :before_save
|
128
|
+
end
|
129
|
+
|
130
|
+
def do_after_save
|
131
|
+
@steps << :after_save
|
132
|
+
end
|
133
|
+
|
134
|
+
def halt
|
135
|
+
false
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class CallbackCantValidatePresenter < ActivePresenter::Base
|
140
|
+
presents :account
|
141
|
+
|
142
|
+
before_validation :do_before_validation
|
143
|
+
before_validation :halt
|
144
|
+
before_save :do_before_save
|
145
|
+
after_save :do_after_save
|
146
|
+
|
147
|
+
attr_reader :steps
|
148
|
+
|
149
|
+
def initialize(params={})
|
150
|
+
super
|
151
|
+
@steps = []
|
152
|
+
end
|
153
|
+
|
154
|
+
def do_before_validation
|
155
|
+
@steps << :before_validation
|
156
|
+
end
|
157
|
+
|
158
|
+
def do_before_save
|
159
|
+
@steps << :before_save
|
160
|
+
end
|
161
|
+
|
162
|
+
def do_after_save
|
163
|
+
@steps << :after_save
|
164
|
+
end
|
165
|
+
|
166
|
+
def halt
|
167
|
+
false
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
71
171
|
def hash_for_user(opts = {})
|
72
172
|
{:login => 'jane', :password => 'seekrit' }.merge(opts)
|
73
173
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_presenter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Golick & Daniel Haran
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-03-13 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|