active_presenter 1.4.0 → 2.0.0a
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/README +10 -1
- data/Rakefile +2 -2
- data/lib/active_presenter/base.rb +98 -136
- data/lib/active_presenter/version.rb +5 -4
- data/lib/tasks/gem.rake +2 -2
- data/test/base_test.rb +28 -29
- data/test/lint_test.rb +10 -0
- data/test/test_helper.rb +13 -11
- metadata +17 -11
data/README
CHANGED
@@ -28,7 +28,7 @@ Creating a presenter is as simple as subclassing ActivePresenter::Base. Use the
|
|
28
28
|
presents :user, :account
|
29
29
|
end
|
30
30
|
|
31
|
-
In the above example, :user will (predictably) become User. If you want to override this
|
31
|
+
In the above example, :user will (predictably) become User. If you want to override this behavior, specify the desired types in a hash, as so:
|
32
32
|
|
33
33
|
class PresenterWithTwoAddresses < ActivePresenter::Base
|
34
34
|
presents :primary_address => Address, :secondary_address => Address
|
@@ -68,6 +68,15 @@ You can retrieve the errors in two ways.
|
|
68
68
|
|
69
69
|
Both of these methods are compatible with error_messages_for. It just depends whether you'd like to show all the errors in one block, or whether you'd prefer to break them up.
|
70
70
|
|
71
|
+
=== Protected and Accessible Attributes
|
72
|
+
|
73
|
+
ActivePresenter supports +attr_protected+ and +attr_accessible+ just like an ActiveRecord object to avoid mass assignment. This can be leveraged to provide an additional layer of protection at the presenter level.
|
74
|
+
|
75
|
+
class AccountPresenter < ActivePresenter::Base
|
76
|
+
presents :user, :profile
|
77
|
+
attr_accessible :user_email, :profile_birthday
|
78
|
+
end
|
79
|
+
|
71
80
|
=== Saving
|
72
81
|
|
73
82
|
You can save your presenter the same way you'd save an ActiveRecord object. Both #save, and #save! behave the same way they do on a normal AR model.
|
data/Rakefile
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'rake'
|
2
|
-
require '
|
2
|
+
require 'rdoc/task'
|
3
3
|
require File.dirname(__FILE__)+'/lib/active_presenter'
|
4
4
|
Dir.glob(File.dirname(__FILE__)+'/lib/tasks/**/*.rake').each { |l| load l }
|
5
5
|
|
6
6
|
task :default => :test
|
7
7
|
|
8
8
|
task :test do
|
9
|
-
Dir['test/**/*_test.rb'].each { |l| require l
|
9
|
+
Dir['test/**/*_test.rb'].each { |l| require File.join(File.dirname(__FILE__),l)}
|
10
10
|
end
|
@@ -2,13 +2,19 @@ module ActivePresenter
|
|
2
2
|
# Base class for presenters. See README for usage.
|
3
3
|
#
|
4
4
|
class Base
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
extend ActiveModel::Callbacks
|
6
|
+
extend ActiveModel::Naming
|
7
|
+
extend ActiveModel::Translation
|
8
|
+
include ActiveModel::MassAssignmentSecurity
|
9
|
+
include ActiveModel::Conversion
|
10
|
+
|
11
|
+
attr_reader :errors
|
12
|
+
|
13
|
+
define_model_callbacks :validation, :save
|
14
|
+
|
8
15
|
class_inheritable_accessor :presented
|
9
|
-
class_inheritable_accessor :attr_protected, :attr_accessible
|
10
16
|
self.presented = {}
|
11
|
-
|
17
|
+
|
12
18
|
# Indicates which models are to be presented by this presenter.
|
13
19
|
# i.e.
|
14
20
|
#
|
@@ -27,29 +33,29 @@ module ActivePresenter
|
|
27
33
|
types.each { |t| types_and_classes[t] = t.to_s.tableize.classify.constantize }
|
28
34
|
|
29
35
|
attr_accessor *types_and_classes.keys
|
30
|
-
|
36
|
+
|
31
37
|
types_and_classes.keys.each do |t|
|
32
38
|
define_method("#{t}_errors") do
|
33
39
|
send(t).errors
|
34
40
|
end
|
35
|
-
|
41
|
+
|
36
42
|
presented[t] = types_and_classes[t]
|
37
43
|
end
|
38
44
|
end
|
39
|
-
|
45
|
+
|
40
46
|
def self.human_attribute_name(attribute_key_name, options = {})
|
41
47
|
presentable_type = presented.keys.detect do |type|
|
42
48
|
attribute_key_name.to_s.starts_with?("#{type}_") || attribute_key_name.to_s == type.to_s
|
43
49
|
end
|
44
50
|
attribute_key_name_without_class = attribute_key_name.to_s.gsub("#{presentable_type}_", "")
|
45
|
-
|
51
|
+
|
46
52
|
if presented[presentable_type] and attribute_key_name_without_class != presentable_type.to_s
|
47
53
|
presented[presentable_type].human_attribute_name(attribute_key_name_without_class, options)
|
48
54
|
else
|
49
55
|
I18n.translate(presentable_type, options.merge(:default => presentable_type.to_s.humanize, :scope => [:activerecord, :models]))
|
50
56
|
end
|
51
57
|
end
|
52
|
-
|
58
|
+
|
53
59
|
# Since ActivePresenter does not descend from ActiveRecord, we need to
|
54
60
|
# mimic some ActiveRecord behavior in order for the ActiveRecord::Errors
|
55
61
|
# object we're using to work properly.
|
@@ -59,7 +65,7 @@ module ActivePresenter
|
|
59
65
|
def self.self_and_descendants_from_active_record # :nodoc:
|
60
66
|
[self]
|
61
67
|
end
|
62
|
-
|
68
|
+
|
63
69
|
def self.human_name(options = {}) # :nodoc:
|
64
70
|
defaults = self_and_descendants_from_active_record.map do |klass|
|
65
71
|
:"#{klass.name.underscore}"
|
@@ -67,31 +73,7 @@ module ActivePresenter
|
|
67
73
|
defaults << self.name.humanize
|
68
74
|
I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge(options))
|
69
75
|
end
|
70
|
-
|
71
|
-
# Note that +attr_protected+ is still applied to the received hash. Thus,
|
72
|
-
# with this technique you can at most _extend_ the list of protected
|
73
|
-
# attributes for a particular mass-assignment call.
|
74
|
-
def self.attr_protected(*attributes)
|
75
|
-
write_inheritable_attribute(:attr_protected, Set.new(attributes.map {|a| a.to_s}) + (protected_attributes || []))
|
76
|
-
end
|
77
|
-
|
78
|
-
# Returns an array of all the attributes that have been protected from mass-assignment.
|
79
|
-
def self.protected_attributes # :nodoc:
|
80
|
-
read_inheritable_attribute(:attr_protected)
|
81
|
-
end
|
82
|
-
|
83
|
-
# Note that +attr_accessible+ is still applied to the received hash. Thus,
|
84
|
-
# with this technique you can at most _narrow_ the list of accessible
|
85
|
-
# attributes for a particular mass-assignment call.
|
86
|
-
def self.attr_accessible(*attributes)
|
87
|
-
write_inheritable_attribute(:attr_accessible, Set.new(attributes.map(&:to_s)) + (accessible_attributes || []))
|
88
|
-
end
|
89
|
-
|
90
|
-
# Returns an array of all the attributes that have been made accessible to mass-assignment.
|
91
|
-
def self.accessible_attributes # :nodoc:
|
92
|
-
read_inheritable_attribute(:attr_accessible)
|
93
|
-
end
|
94
|
-
|
76
|
+
|
95
77
|
# 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:
|
96
78
|
#
|
97
79
|
# 1. SignupPresenter.new(:user_login => 'james', :user_password => 'swordfish', :user_password_confirmation => 'swordfish', :account_subdomain => 'giraffesoft')
|
@@ -105,13 +87,12 @@ module ActivePresenter
|
|
105
87
|
# If you don't specify an instance, one will be created by calling Model.new
|
106
88
|
#
|
107
89
|
def initialize(args = {})
|
108
|
-
|
109
|
-
|
90
|
+
@errors = ActiveModel::Errors.new(self)
|
91
|
+
return self unless args
|
110
92
|
presented.each do |type, klass|
|
111
93
|
value = args.delete(type)
|
112
94
|
send("#{type}=", value.is_a?(klass) ? value : klass.new)
|
113
95
|
end
|
114
|
-
|
115
96
|
self.attributes = args
|
116
97
|
end
|
117
98
|
|
@@ -121,11 +102,11 @@ module ActivePresenter
|
|
121
102
|
#
|
122
103
|
def attributes=(attrs)
|
123
104
|
return if attrs.nil?
|
124
|
-
|
125
|
-
attrs = attrs.stringify_keys
|
105
|
+
|
106
|
+
attrs = attrs.stringify_keys
|
126
107
|
multi_parameter_attributes = {}
|
127
|
-
attrs =
|
128
|
-
|
108
|
+
attrs = sanitize_for_mass_assignment(attrs)
|
109
|
+
|
129
110
|
attrs.each do |k,v|
|
130
111
|
if (base_attribute = k.to_s.split("(").first) != k.to_s
|
131
112
|
presentable = presentable_for(base_attribute)
|
@@ -135,87 +116,79 @@ module ActivePresenter
|
|
135
116
|
send("#{k}=", v) unless attribute_protected?(k)
|
136
117
|
end
|
137
118
|
end
|
138
|
-
|
119
|
+
|
139
120
|
multi_parameter_attributes.each do |presentable,multi_attrs|
|
140
121
|
send(presentable).send(:attributes=, multi_attrs)
|
141
122
|
end
|
142
123
|
end
|
143
|
-
|
124
|
+
|
144
125
|
# Makes sure that the presenter is accurate about responding to presentable's attributes, even though they are handled by method_missing.
|
145
126
|
#
|
146
127
|
def respond_to?(method, include_private = false)
|
147
128
|
presented_attribute?(method) || super
|
148
129
|
end
|
149
|
-
|
130
|
+
|
150
131
|
# Handles the decision about whether to delegate getters and setters to presentable instances.
|
151
132
|
#
|
152
133
|
def method_missing(method_name, *args, &block)
|
153
134
|
presented_attribute?(method_name) ? delegate_message(method_name, *args, &block) : super
|
154
135
|
end
|
155
|
-
|
156
|
-
# Returns an instance of ActiveRecord::Errors with all the errors from the presentables merged in using the type_attribute form (i.e. user_login).
|
157
|
-
#
|
158
|
-
def errors
|
159
|
-
@errors ||= ActiveRecord::Errors.new(self)
|
160
|
-
end
|
161
|
-
|
136
|
+
|
162
137
|
# Returns boolean based on the validity of the presentables by calling valid? on each of them.
|
163
138
|
#
|
164
139
|
def valid?
|
140
|
+
validated = false
|
165
141
|
errors.clear
|
166
|
-
|
142
|
+
result = _run_validation_callbacks do
|
167
143
|
presented.keys.each do |type|
|
168
144
|
presented_inst = send(type)
|
169
|
-
|
170
145
|
next unless save?(type, presented_inst)
|
171
146
|
merge_errors(presented_inst, type) unless presented_inst.valid?
|
172
147
|
end
|
173
|
-
|
174
|
-
errors.empty?
|
148
|
+
validated = true
|
175
149
|
end
|
150
|
+
errors.empty? && validated
|
176
151
|
end
|
177
|
-
|
152
|
+
|
178
153
|
# Do any of the attributes have unsaved changes?
|
179
154
|
def changed?
|
180
155
|
presented_instances.map(&:changed?).any?
|
181
156
|
end
|
182
|
-
|
157
|
+
|
183
158
|
# Save all of the presentables, wrapped in a transaction.
|
184
159
|
#
|
185
160
|
# Returns true or false based on success.
|
186
161
|
#
|
187
162
|
def save
|
188
163
|
saved = false
|
189
|
-
|
190
164
|
ActiveRecord::Base.transaction do
|
191
|
-
if valid?
|
192
|
-
|
193
|
-
|
165
|
+
if valid?
|
166
|
+
_run_save_callbacks do
|
167
|
+
saved = presented.keys.select {|key| save?(key, send(key))}.all? {|key| send(key).save}
|
168
|
+
raise ActiveRecord::Rollback unless saved
|
169
|
+
end
|
194
170
|
end
|
195
|
-
|
196
|
-
run_callbacks_with_halt(:after_save) if saved
|
197
171
|
end
|
198
|
-
|
199
172
|
saved
|
200
173
|
end
|
201
|
-
|
174
|
+
|
202
175
|
# Save all of the presentables wrapped in a transaction.
|
203
176
|
#
|
204
177
|
# Returns true on success, will raise otherwise.
|
205
178
|
#
|
206
179
|
def save!
|
207
|
-
|
208
|
-
raise ActiveRecord::RecordNotSaved unless run_callbacks_with_halt(:before_save)
|
209
|
-
|
180
|
+
saved = false
|
210
181
|
ActiveRecord::Base.transaction do
|
211
|
-
|
212
|
-
|
213
|
-
|
182
|
+
raise ActiveRecord::RecordInvalid.new(self) unless valid?
|
183
|
+
_run_save_callbacks do
|
184
|
+
presented.keys.select {|key| save?(key, send(key))}.all? {|key| send(key).save!}
|
185
|
+
saved = true
|
186
|
+
end
|
187
|
+
raise ActiveRecord::RecordNotSaved.new(self) unless saved
|
214
188
|
end
|
215
|
-
|
216
|
-
true
|
189
|
+
saved
|
217
190
|
end
|
218
|
-
|
191
|
+
|
219
192
|
# Update attributes, and save the presentables
|
220
193
|
#
|
221
194
|
# Returns true or false based on success.
|
@@ -224,7 +197,7 @@ module ActivePresenter
|
|
224
197
|
self.attributes = attrs
|
225
198
|
save
|
226
199
|
end
|
227
|
-
|
200
|
+
|
228
201
|
# Should this presented instance be saved? By default, this returns true
|
229
202
|
# Called from #save and #save!
|
230
203
|
#
|
@@ -244,72 +217,61 @@ module ActivePresenter
|
|
244
217
|
def id # :nodoc:
|
245
218
|
nil
|
246
219
|
end
|
247
|
-
|
220
|
+
|
248
221
|
def new_record?
|
249
|
-
|
222
|
+
presented_instances.map(&:new_record?).all?
|
223
|
+
end
|
224
|
+
|
225
|
+
def persisted?
|
226
|
+
presented_instances.map(&:persisted?).all?
|
250
227
|
end
|
251
228
|
|
252
229
|
protected
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
end
|
267
|
-
|
268
|
-
def presented_attribute?(method_name)
|
269
|
-
p = presentable_for(method_name)
|
270
|
-
!p.nil? && send(p).respond_to?(flatten_attribute_name(method_name,p))
|
271
|
-
end
|
272
|
-
|
273
|
-
def flatten_attribute_name(name, type)
|
274
|
-
name.to_s.gsub(/^#{attribute_prefix(type)}/, '')
|
275
|
-
end
|
276
|
-
|
277
|
-
def attribute_prefix(type)
|
278
|
-
"#{type}_"
|
279
|
-
end
|
280
|
-
|
281
|
-
def merge_errors(presented_inst, type)
|
282
|
-
presented_inst.errors.each do |att,msg|
|
283
|
-
if att == 'base'
|
284
|
-
errors.add(type, msg)
|
285
|
-
else
|
286
|
-
errors.add(attribute_prefix(type)+att, msg)
|
287
|
-
end
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
def attribute_protected?(name)
|
292
|
-
presentable = presentable_for(name)
|
293
|
-
return false unless presentable
|
294
|
-
flat_attribute = {flatten_attribute_name(name, presentable) => ''} #remove_att... normally takes a hash, so we use a ''
|
295
|
-
presented[presentable].new.send(:remove_attributes_protected_from_mass_assignment, flat_attribute).empty?
|
296
|
-
end
|
297
|
-
|
298
|
-
def run_callbacks_with_halt(callback)
|
299
|
-
run_callbacks(callback) { |result, object| result == false }
|
230
|
+
|
231
|
+
def presented_instances
|
232
|
+
presented.keys.map { |key| send(key) }
|
233
|
+
end
|
234
|
+
|
235
|
+
def delegate_message(method_name, *args, &block)
|
236
|
+
presentable = presentable_for(method_name)
|
237
|
+
send(presentable).send(flatten_attribute_name(method_name, presentable), *args, &block)
|
238
|
+
end
|
239
|
+
|
240
|
+
def presentable_for(method_name)
|
241
|
+
presented.keys.sort_by { |k| k.to_s.size }.reverse.detect do |type|
|
242
|
+
method_name.to_s.starts_with?(attribute_prefix(type))
|
300
243
|
end
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
244
|
+
end
|
245
|
+
|
246
|
+
def presented_attribute?(method_name)
|
247
|
+
p = presentable_for(method_name)
|
248
|
+
!p.nil? && send(p).respond_to?(flatten_attribute_name(method_name,p))
|
249
|
+
end
|
250
|
+
|
251
|
+
def flatten_attribute_name(name, type)
|
252
|
+
name.to_s.gsub(/^#{attribute_prefix(type)}/, '')
|
253
|
+
end
|
254
|
+
|
255
|
+
def attribute_prefix(type)
|
256
|
+
"#{type}_"
|
257
|
+
end
|
258
|
+
|
259
|
+
def merge_errors(presented_inst, type)
|
260
|
+
presented_inst.errors.each do |att,msg|
|
261
|
+
if att == :base
|
262
|
+
errors.add(type, msg)
|
309
263
|
else
|
310
|
-
|
264
|
+
errors.add(attribute_prefix(type)+att.to_s, msg)
|
311
265
|
end
|
312
266
|
end
|
313
|
-
|
267
|
+
end
|
268
|
+
|
269
|
+
def attribute_protected?(name)
|
270
|
+
presentable = presentable_for(name)
|
271
|
+
return false unless presentable
|
272
|
+
flat_attribute = {flatten_attribute_name(name, presentable) => ''} #remove_att... normally takes a hash, so we use a ''
|
273
|
+
presented[presentable].new.send(:sanitize_for_mass_assignment, flat_attribute).empty?
|
274
|
+
end
|
275
|
+
|
314
276
|
end
|
315
277
|
end
|
data/lib/tasks/gem.rake
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'rubygems/package_task'
|
2
2
|
|
3
3
|
task :clean => :clobber_package
|
4
4
|
|
@@ -21,7 +21,7 @@ spec = Gem::Specification.new do |s|
|
|
21
21
|
s.require_path = "lib"
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
Gem::PackageTask.new(spec) do |p|
|
25
25
|
p.gem_spec = spec
|
26
26
|
end
|
27
27
|
|
data/test/base_test.rb
CHANGED
@@ -45,34 +45,34 @@ Expectations do
|
|
45
45
|
expect SignupPresenter.new.not.to.be.valid?
|
46
46
|
expect SignupPresenter.new(:user => User.new(hash_for_user)).to.be.valid?
|
47
47
|
|
48
|
-
expect
|
48
|
+
expect ActiveModel::Errors do
|
49
49
|
s = SignupPresenter.new
|
50
50
|
s.valid?
|
51
51
|
s.errors
|
52
52
|
end
|
53
53
|
|
54
|
-
expect
|
54
|
+
expect ActiveModel::Errors do
|
55
55
|
s = SignupPresenter.new
|
56
56
|
s.valid?
|
57
57
|
s.user_errors
|
58
58
|
end
|
59
59
|
|
60
|
-
expect
|
60
|
+
expect ActiveModel::Errors do
|
61
61
|
s = SignupPresenter.new
|
62
62
|
s.valid?
|
63
63
|
s.account_errors
|
64
64
|
end
|
65
65
|
|
66
|
-
expect
|
66
|
+
expect ["can't be blank"] do
|
67
67
|
s = SignupPresenter.new
|
68
68
|
s.valid?
|
69
|
-
s.errors
|
69
|
+
s.errors[:user_login]
|
70
70
|
end
|
71
71
|
|
72
|
-
expect "can't be blank" do
|
72
|
+
expect ["can't be blank"] do
|
73
73
|
s = SignupPresenter.new
|
74
74
|
s.valid?
|
75
|
-
s.errors
|
75
|
+
s.errors[:user_login]
|
76
76
|
end
|
77
77
|
|
78
78
|
expect ["User Password can't be blank"] do
|
@@ -81,13 +81,13 @@ Expectations do
|
|
81
81
|
s.errors.full_messages
|
82
82
|
end
|
83
83
|
|
84
|
-
expect 'c4N n07 83 8L4nK' do
|
84
|
+
expect ['c4N n07 83 8L4nK'] do
|
85
85
|
old_locale = I18n.locale
|
86
86
|
I18n.locale = '1337'
|
87
87
|
|
88
88
|
s = SignupPresenter.new(:user_login => nil)
|
89
89
|
s.valid?
|
90
|
-
message = s.errors
|
90
|
+
message = s.errors[:user_login]
|
91
91
|
|
92
92
|
I18n.locale = old_locale
|
93
93
|
|
@@ -143,7 +143,7 @@ Expectations do
|
|
143
143
|
end
|
144
144
|
|
145
145
|
expect Account.any_instance.to.receive(:save!) do
|
146
|
-
User.any_instance.stubs(:save!)
|
146
|
+
User.any_instance.stubs(:save!).returns(true)
|
147
147
|
s = SignupPresenter.new(:user_login => "da", :user_password => "seekrit")
|
148
148
|
s.save!
|
149
149
|
end
|
@@ -173,7 +173,7 @@ Expectations do
|
|
173
173
|
s.update_attributes :user_login => 'Something Different'
|
174
174
|
s.user_login
|
175
175
|
end
|
176
|
-
|
176
|
+
|
177
177
|
# Multiparameter assignment
|
178
178
|
expect Time.parse('March 27 1980 9:30:59 am') do
|
179
179
|
s = SignupPresenter.new
|
@@ -192,23 +192,23 @@ Expectations do
|
|
192
192
|
s = SignupPresenter.new
|
193
193
|
s.attributes = nil
|
194
194
|
end
|
195
|
-
|
195
|
+
|
196
196
|
# this is a regression test to make sure that _title is working. we had a weird conflict with using String#delete
|
197
197
|
expect 'something' do
|
198
198
|
s = SignupPresenter.new :account_title => 'something'
|
199
199
|
s.account_title
|
200
200
|
end
|
201
201
|
|
202
|
-
expect
|
202
|
+
expect ["can't be blank"] do
|
203
203
|
s = SignupPresenter.new
|
204
204
|
s.save
|
205
|
-
s.errors
|
205
|
+
s.errors[:user_login]
|
206
206
|
end
|
207
207
|
|
208
|
-
expect
|
208
|
+
expect ["can't be blank"] do
|
209
209
|
s = SignupPresenter.new
|
210
210
|
s.save! rescue
|
211
|
-
s.errors
|
211
|
+
s.errors[:user_login]
|
212
212
|
end
|
213
213
|
|
214
214
|
expect 'Login' do
|
@@ -219,7 +219,7 @@ Expectations do
|
|
219
219
|
expect SignupPresenter do
|
220
220
|
SignupPresenter.new(nil)
|
221
221
|
end
|
222
|
-
|
222
|
+
|
223
223
|
expect EndingWithSPresenter.new.address.not.to.be.nil?
|
224
224
|
|
225
225
|
# this should act as ActiveRecord models do
|
@@ -237,25 +237,25 @@ Expectations do
|
|
237
237
|
presenter.save!
|
238
238
|
end.id
|
239
239
|
end
|
240
|
-
|
240
|
+
|
241
241
|
expect CantSavePresenter.new.not.to.be.save # it won't save because the filter chain will halt
|
242
|
-
|
242
|
+
|
243
243
|
expect ActiveRecord::RecordNotSaved do
|
244
244
|
CantSavePresenter.new.save!
|
245
245
|
end
|
246
|
-
|
246
|
+
|
247
247
|
expect 'Some Street' do
|
248
248
|
p = AfterSavePresenter.new
|
249
249
|
p.save
|
250
250
|
p.address.street
|
251
251
|
end
|
252
|
-
|
252
|
+
|
253
253
|
expect 'Some Street' do
|
254
254
|
p = AfterSavePresenter.new
|
255
255
|
p.save!
|
256
256
|
p.address.street
|
257
257
|
end
|
258
|
-
|
258
|
+
|
259
259
|
expect SamePrefixPresenter.new.to.be.respond_to?(:account_title)
|
260
260
|
expect SamePrefixPresenter.new.to.be.respond_to?(:account_info_info)
|
261
261
|
|
@@ -305,7 +305,7 @@ Expectations do
|
|
305
305
|
end.steps
|
306
306
|
end
|
307
307
|
|
308
|
-
expect
|
308
|
+
expect ActiveModel::Errors.any_instance.to.receive(:clear) do
|
309
309
|
CallbackCantValidatePresenter.new.valid?
|
310
310
|
end
|
311
311
|
|
@@ -329,34 +329,33 @@ Expectations do
|
|
329
329
|
expect Address do
|
330
330
|
PresenterWithTwoAddresses.new.secondary_address
|
331
331
|
end
|
332
|
-
|
332
|
+
|
333
333
|
expect "123 awesome st" do
|
334
334
|
p = PresenterWithTwoAddresses.new(:secondary_address_street => "123 awesome st")
|
335
335
|
p.save
|
336
336
|
p.secondary_address_street
|
337
337
|
end
|
338
|
-
|
338
|
+
|
339
339
|
# attr_protected
|
340
340
|
expect "" do
|
341
341
|
p = SignupPresenter.new(:account_secret => 'swordfish')
|
342
342
|
p.account.secret
|
343
343
|
end
|
344
|
-
|
344
|
+
|
345
345
|
expect "comment" do
|
346
346
|
p = HistoricalPresenter.new(:history_comment => 'comment', :user => User.new(hash_for_user))
|
347
347
|
p.save
|
348
348
|
p.history_comment
|
349
349
|
end
|
350
|
-
|
350
|
+
|
351
351
|
expect false do
|
352
352
|
SignupPresenter.new.changed?
|
353
353
|
end
|
354
|
-
|
354
|
+
|
355
355
|
expect true do
|
356
356
|
p = SignupPresenter.new(:user => User.new(hash_for_user))
|
357
357
|
p.save
|
358
358
|
p.user_login = 'something_else'
|
359
359
|
p.changed?
|
360
360
|
end
|
361
|
-
|
362
361
|
end
|
data/test/lint_test.rb
ADDED
data/test/test_helper.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require File.dirname(__FILE__)+'/../lib/active_presenter' unless defined?(ActivePresenter)
|
2
1
|
require 'expectations'
|
3
2
|
require 'logger'
|
4
3
|
|
@@ -51,7 +50,6 @@ ActiveRecord::Schema.define(:version => 0) do
|
|
51
50
|
t.string :action, :default => ''
|
52
51
|
t.datetime :created_at
|
53
52
|
end
|
54
|
-
|
55
53
|
end
|
56
54
|
|
57
55
|
class User < ActiveRecord::Base
|
@@ -64,12 +62,12 @@ class User < ActiveRecord::Base
|
|
64
62
|
if password.blank?
|
65
63
|
attribute_name = I18n.t(:password, {:default => "Password", :scope => [:activerecord, :attributes, :user]})
|
66
64
|
error_message = I18n.t(:blank, {:default => "can't be blank", :scope => [:activerecord, :errors, :messages]})
|
67
|
-
errors
|
65
|
+
errors[:base] << ("#{attribute_name} #{error_message}")
|
68
66
|
end
|
69
67
|
end
|
70
68
|
end
|
71
|
-
class Account < ActiveRecord::Base;
|
72
|
-
class History < ActiveRecord::Base;
|
69
|
+
class Account < ActiveRecord::Base ;end
|
70
|
+
class History < ActiveRecord::Base ;end
|
73
71
|
class Address < ActiveRecord::Base; end
|
74
72
|
class AccountInfo < ActiveRecord::Base; end
|
75
73
|
|
@@ -86,11 +84,6 @@ class EndingWithSPresenter < ActivePresenter::Base
|
|
86
84
|
presents :address
|
87
85
|
end
|
88
86
|
|
89
|
-
class HistoricalPresenter < ActivePresenter::Base
|
90
|
-
presents :user, :history
|
91
|
-
attr_accessible :history_comment
|
92
|
-
end
|
93
|
-
|
94
87
|
class CantSavePresenter < ActivePresenter::Base
|
95
88
|
presents :address
|
96
89
|
|
@@ -103,7 +96,7 @@ class SignupNoAccountPresenter < ActivePresenter::Base
|
|
103
96
|
presents :account, :user
|
104
97
|
|
105
98
|
def save?(key, instance)
|
106
|
-
key != :account
|
99
|
+
key.to_sym != :account
|
107
100
|
end
|
108
101
|
end
|
109
102
|
|
@@ -212,7 +205,16 @@ class CallbackCantValidatePresenter < ActivePresenter::Base
|
|
212
205
|
end
|
213
206
|
end
|
214
207
|
|
208
|
+
class HistoricalPresenter < ActivePresenter::Base
|
209
|
+
presents :user, :history
|
210
|
+
attr_accessible :history_comment
|
211
|
+
end
|
212
|
+
|
215
213
|
def hash_for_user(opts = {})
|
216
214
|
{:login => 'jane', :password => 'seekrit' }.merge(opts)
|
217
215
|
end
|
218
216
|
|
217
|
+
def returning(value)
|
218
|
+
yield(value)
|
219
|
+
value
|
220
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_presenter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 10
|
5
|
+
prerelease: 5
|
6
6
|
segments:
|
7
|
-
-
|
8
|
-
- 4
|
7
|
+
- 2
|
9
8
|
- 0
|
10
|
-
|
9
|
+
- 0
|
10
|
+
- a
|
11
|
+
version: 2.0.0a
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- James Golick & Daniel Haran
|
@@ -15,7 +16,8 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2011-09-
|
19
|
+
date: 2011-09-26 00:00:00 -07:00
|
20
|
+
default_executable:
|
19
21
|
dependencies: []
|
20
22
|
|
21
23
|
description: ActivePresenter is the presenter library you already know! (...if you know ActiveRecord)
|
@@ -36,7 +38,9 @@ files:
|
|
36
38
|
- lib/tasks/doc.rake
|
37
39
|
- lib/tasks/gem.rake
|
38
40
|
- test/base_test.rb
|
41
|
+
- test/lint_test.rb
|
39
42
|
- test/test_helper.rb
|
43
|
+
has_rdoc: true
|
40
44
|
homepage: http://jamesgolick.com/active_presenter
|
41
45
|
licenses: []
|
42
46
|
|
@@ -59,16 +63,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
63
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
64
|
none: false
|
61
65
|
requirements:
|
62
|
-
- - "
|
66
|
+
- - ">"
|
63
67
|
- !ruby/object:Gem::Version
|
64
|
-
hash:
|
68
|
+
hash: 25
|
65
69
|
segments:
|
66
|
-
-
|
67
|
-
|
70
|
+
- 1
|
71
|
+
- 3
|
72
|
+
- 1
|
73
|
+
version: 1.3.1
|
68
74
|
requirements: []
|
69
75
|
|
70
76
|
rubyforge_project: active_presenter
|
71
|
-
rubygems_version: 1.
|
77
|
+
rubygems_version: 1.3.9.3
|
72
78
|
signing_key:
|
73
79
|
specification_version: 3
|
74
80
|
summary: ActivePresenter is the presenter library you already know! (...if you know ActiveRecord)
|