active_presenter 0.0.6 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|