simple_model 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/LICENSE.txt +1 -1
- data/README.md +42 -1
- data/lib/simple_model/attributes.rb +146 -137
- data/lib/simple_model/base.rb +40 -51
- data/lib/simple_model/exceptions.rb +5 -0
- data/lib/simple_model/extend_core.rb +93 -96
- data/lib/simple_model/version.rb +1 -1
- data/lib/simple_model.rb +23 -13
- data/simple_model.gemspec +1 -0
- data/spec/attributes_spec.rb +131 -56
- data/spec/extend_core_spec.rb +42 -47
- data/spec/simple_model_spec.rb +42 -21
- data/spec/spec_helper.rb +1 -1
- metadata +48 -12
- data/Gemfile.lock +0 -39
data/.gitignore
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -87,13 +87,50 @@ SimpleModel is available through [Rubygems](http://rubygems.org/gems/simple_mode
|
|
87
87
|
item.changes # => {"price"=>[#<BigDecimal:7fc61b250da8,'0.1E2',9(27)>, #<BigDecimal:7fc61b1ba600,'0.1024E4',9(27)>]}
|
88
88
|
item.my_array # => []
|
89
89
|
item.valid? # => false
|
90
|
+
items.save! # raises SimpleModel::ValidationError exception
|
90
91
|
item.my_array # => [1]
|
91
92
|
item.price = 15
|
93
|
+
item.persisted? # => false
|
92
94
|
item.save # => true
|
95
|
+
item.persisted? # => true
|
93
96
|
item.changed? # => false
|
94
97
|
item.previous_changes # => {"price"=>[#<BigDecimal:7fc61b1ba600,'0.1024E4',9(27)>, #<BigDecimal:7fc61b1730e8,'0.15E2',9(27)>], "saved"=>[nil, true]}
|
95
|
-
|
96
98
|
|
99
|
+
### Rails Session Modeling
|
100
|
+
require 'simple_model'
|
101
|
+
|
102
|
+
class SessionUser < SimpleModel::Base
|
103
|
+
has_attributes :permissions, :default => []
|
104
|
+
|
105
|
+
# Returns true only if all required permission are set
|
106
|
+
def authorized?(*required_permissions)
|
107
|
+
(permissions == (required_permissions | permissions))
|
108
|
+
end
|
109
|
+
|
110
|
+
#... lots of other handy methods...#
|
111
|
+
end
|
112
|
+
|
113
|
+
class ApplicationController < ActionController::Base
|
114
|
+
#... omitted for space ...#
|
115
|
+
# Initialize, if necessary, and return our session user object
|
116
|
+
def session_user
|
117
|
+
session[:user] ||= {:permissions => [:foo,:baz]}
|
118
|
+
@session_user ||= SessionUser.new_with_store(session[:user])
|
119
|
+
end
|
120
|
+
helper_method :session_user
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
# redirect if not authorized
|
125
|
+
def authorize(*required_permissions)
|
126
|
+
redirect_to '/sessions/error' unless session_user.authorized?(*required_permissions)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class FoosController < ApplicationController
|
131
|
+
before_filter do |c| c.send(:authorize,:foo) # Make sure session user has permission
|
132
|
+
end
|
133
|
+
|
97
134
|
|
98
135
|
## Contributing to simple_model
|
99
136
|
|
@@ -105,6 +142,10 @@ SimpleModel is available through [Rubygems](http://rubygems.org/gems/simple_mode
|
|
105
142
|
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
106
143
|
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
107
144
|
|
145
|
+
## Notes
|
146
|
+
|
147
|
+
Release 1.2+ no longer create instance variables, just uses the attributes hash as the data store.
|
148
|
+
|
108
149
|
## Thanks
|
109
150
|
|
110
151
|
Code based on Rails/ActiveRecord and [Appoxy/SimpleRecord](https://github.com/appoxy/simple_record)
|
@@ -1,175 +1,184 @@
|
|
1
|
+
require 'simple_model/exceptions'
|
1
2
|
module SimpleModel
|
2
|
-
# require all that active support we know and love
|
3
|
-
require 'active_support/core_ext/array/extract_options'
|
4
|
-
require 'active_support/core_ext/object/blank'
|
5
|
-
|
6
3
|
module Attributes
|
7
4
|
include ExtendCore
|
8
5
|
extend ActiveSupport::Concern
|
9
|
-
include ActiveModel::AttributeMethods
|
10
|
-
|
11
|
-
def initialize(*attrs)
|
12
|
-
|
6
|
+
include ActiveModel::AttributeMethods
|
7
|
+
|
8
|
+
def initialize(*attrs)
|
9
|
+
attrs = attrs.extract_options!
|
10
|
+
set(attributes_with_for_init(attrs))
|
13
11
|
end
|
14
|
-
|
15
|
-
#
|
12
|
+
|
13
|
+
# Returns true if attribute has been initialized
|
14
|
+
def initialized?(attr)
|
15
|
+
attributes.key?(attr.to_sym)
|
16
|
+
end
|
17
|
+
|
16
18
|
def attributes
|
17
|
-
@attributes ||=
|
18
|
-
@attributes
|
19
|
+
@attributes ||= HashWithIndifferentAccess.new
|
19
20
|
end
|
21
|
+
|
22
|
+
def attributes=attrs
|
23
|
+
@attributes = attrs
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(attr)
|
27
|
+
self.send(attr)
|
28
|
+
end
|
29
|
+
alias :read :get
|
20
30
|
|
21
|
-
|
22
|
-
|
23
|
-
|
31
|
+
# Accepts a hash where the keys are methods and the values are values to be set.
|
32
|
+
# set(:foo => "bar", :dime => 0.1)
|
33
|
+
def set(*attrs)
|
34
|
+
attrs.extract_options!.each do |attr,val|
|
35
|
+
self.send("#{attr.to_s}=",val)
|
24
36
|
end
|
25
37
|
end
|
38
|
+
alias :set_attributes :set
|
26
39
|
|
27
|
-
|
28
|
-
def before_attribute_set(method,val)
|
29
|
-
end
|
40
|
+
private
|
30
41
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
base.extend(ClassMethods)
|
42
|
+
def fetch_default_value(arg)
|
43
|
+
return self.send(arg) if (arg.is_a?(Symbol) && self.respond_to?(arg))
|
44
|
+
arg
|
35
45
|
end
|
36
46
|
|
37
|
-
|
47
|
+
# Returns attribute that have defaults in a hash: {:attrbute => "default value"}
|
48
|
+
def attributes_with_for_init(attrs)
|
49
|
+
d = attrs.with_indifferent_access
|
50
|
+
self.class.defined_attributes.each do |k,v|
|
51
|
+
d[k] = fetch_default_value(v[:default]) if (d[k].blank? && v[:default] && v[:initialize])
|
52
|
+
end
|
53
|
+
d
|
38
54
|
end
|
39
55
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
#
|
44
|
-
|
56
|
+
module ClassMethods
|
57
|
+
# Creates a new instance where the attributes store is set to object
|
58
|
+
# provided, which allows one to pass a session store hash or any other
|
59
|
+
# hash-like object to be used for persistance. Typically used for modeling
|
60
|
+
# session stores for authorization or shopping carts
|
61
|
+
# EX:
|
62
|
+
# class ApplicationController < ActionController::Base
|
63
|
+
# def session_user
|
64
|
+
# session[:user] ||= {}
|
65
|
+
# @session_user ||= SessionUser.new_with_store(session[:user])
|
66
|
+
# end
|
67
|
+
# helper_method :session_user
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
def new_with_store(session_hash)
|
71
|
+
new = self.new()
|
72
|
+
new.attributes = session_hash
|
73
|
+
new.set(new.send(:attributes_with_for_init,session_hash))
|
74
|
+
new
|
45
75
|
end
|
46
76
|
|
47
|
-
|
48
|
-
|
49
|
-
def define_reader_with_options(attr,options)
|
50
|
-
if options.has_key?(:default)
|
51
|
-
define_method(attr.to_s) do
|
52
|
-
default = (options[:default].is_a?(Symbol) ? self.send(options[:default]) : options[:default])
|
53
|
-
val = instance_variable_get("@#{attr.to_s}")
|
54
|
-
val = default unless instance_variable_defined?("@#{attr.to_s}")
|
55
|
-
val
|
56
|
-
end
|
57
|
-
else
|
58
|
-
attr_reader attr
|
59
|
-
end
|
77
|
+
def defined_attributes
|
78
|
+
@defined_attributes ||= {}
|
60
79
|
end
|
61
80
|
|
62
|
-
def
|
63
|
-
|
64
|
-
val = val.cast_to(cast_methods)
|
65
|
-
before_attribute_set(attr,val)
|
66
|
-
instance_variable_set("@#{attr}", val)
|
67
|
-
attributes[attr] = val
|
68
|
-
val
|
69
|
-
end
|
81
|
+
def defined_attributes=defined_attributes
|
82
|
+
@defined_attributes = defined_attributes
|
70
83
|
end
|
71
84
|
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
85
|
+
# The default settings for a SimpeModel class
|
86
|
+
# Options:
|
87
|
+
# * :on_set - accepts a lambda that is run when an attribute is set
|
88
|
+
# * :on_get - accepts a lambda that is run when you get/read an attribute
|
89
|
+
# * :default - the default value for the attribute, can be a symbol that is sent for a method
|
90
|
+
# * :initialize - informations the object whether or not it should initialize the attribute with :default value, defaults to true
|
91
|
+
# ** If :intialize is set to false you must set :allow_blank to false or it will never set the default value
|
92
|
+
# * :allow_blank - when set to false, if an attributes value is blank attempts to set the default value, defaults to true
|
93
|
+
def default_attribute_settings
|
94
|
+
@default_attribute_settings ||= {:attributes_method => :attributes,
|
95
|
+
:on_set => lambda {|obj,attr| attr},
|
96
|
+
:on_get => lambda {|obj,attr| attr},
|
97
|
+
:allow_blank => true,
|
98
|
+
:initialize => true
|
99
|
+
}
|
77
100
|
end
|
78
|
-
|
79
|
-
# Left this use a module eval for reference, saw no noticable improvement
|
80
|
-
# in speed, so I would rather use code than strings for now
|
81
|
-
# def define_setter_with_eval(attr,cast_methods)
|
82
|
-
# module_eval <<-STR, __FILE__, __LINE__
|
83
|
-
# def #{attr.to_s}=#{attr.to_s}
|
84
|
-
# val = #{attr.to_s}.cast_to(#{cast_methods})
|
85
|
-
# before_attribute_set(:#{attr.to_s},val)
|
86
|
-
# @#{attr.to_s} = val
|
87
|
-
# attributes[:#{attr.to_s}] = val
|
88
|
-
# val
|
89
|
-
# end
|
90
|
-
# STR
|
91
|
-
# end
|
92
101
|
|
93
|
-
|
94
|
-
|
95
|
-
options = attrs.extract_options!
|
96
|
-
attrs.each do |attr|
|
97
|
-
build_attribute_methods(attr,options)
|
98
|
-
end
|
102
|
+
def default_attribute_settings=default_attribute_settings
|
103
|
+
@default_attribute_settings = default_attribute_settings
|
99
104
|
end
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
options = attrs.extract_options!
|
105
|
-
attrs.each do |attr|
|
106
|
-
build_attribute_methods(attr,options,[:to_s,:to_b])
|
107
|
-
define_method ("#{attr.to_s}?") do
|
108
|
-
send("#{attr.to_s}".to_sym).to_s.to_b
|
109
|
-
end
|
110
|
-
end
|
105
|
+
|
106
|
+
def add_defined_attribute(attr,options)
|
107
|
+
self.defined_attributes[attr] = options
|
108
|
+
define_attribute_methods self.defined_attributes.keys
|
111
109
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
110
|
+
|
111
|
+
# builds the setter and getter methods
|
112
|
+
def create_attribute_methods(attributes,options)
|
113
|
+
unless attributes.blank?
|
114
|
+
attributes.each do |attr|
|
115
|
+
define_reader_with_options(attr,options)
|
116
|
+
define_setter_with_options(attr,options)
|
117
|
+
end
|
119
118
|
end
|
120
119
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
build_attribute_methods(attr,options,[:to_s,:to_currency])
|
131
|
-
|
120
|
+
|
121
|
+
def define_reader_with_options(attr,options)
|
122
|
+
add_defined_attribute(attr,options)
|
123
|
+
options = default_attribute_settings.merge(options) if options[:on_get].blank?
|
124
|
+
define_method(attr) do
|
125
|
+
if (options.key?(:default) && (!self.initialized?(attr) || (!options[:allow_blank] && self.attributes[attr].blank?)))
|
126
|
+
self.attributes[attr] = fetch_default_value(options[:default])
|
127
|
+
end
|
128
|
+
options[:on_get].call(self,self.attributes[attr])
|
132
129
|
end
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
130
|
+
define_method("#{attr.to_s}?") do
|
131
|
+
val = self.send(attr)
|
132
|
+
if val.respond_to?(:to_b)
|
133
|
+
val = val.to_b
|
134
|
+
else
|
135
|
+
val = !val.blank? if val.respond_to?(:blank?)
|
136
|
+
end
|
137
|
+
val
|
140
138
|
end
|
141
139
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
140
|
+
|
141
|
+
def define_setter_with_options(attr,options)
|
142
|
+
add_defined_attribute(attr,options)
|
143
|
+
options = default_attribute_settings.merge(options) if (options[:on_set].blank? || options[:after_set].blank?)
|
144
|
+
define_method("#{attr.to_s}=") do |val|
|
145
|
+
val = fetch_default_value(options[:default]) if (!options[:allow_blank] && options.key?(:default) && val.blank?)
|
146
|
+
begin
|
147
|
+
val = options[:on_set].call(self,val)
|
148
|
+
rescue NoMethodError => e
|
149
|
+
raise ArgumentError, "#{val} could not be set for #{attr}: #{e.message}"
|
150
|
+
end
|
151
|
+
will_change = "#{attr}_will_change!".to_sym
|
152
|
+
self.send(will_change) if (self.respond_to?(will_change) && val != self.attributes[attr])
|
153
|
+
self.attributes[attr] = val
|
154
|
+
options[:after_set].call(self,val) if options[:after_set]
|
150
155
|
end
|
151
156
|
end
|
152
|
-
|
157
|
+
|
158
|
+
AVAILABLE_ATTRIBUTE_METHODS = {
|
159
|
+
:has_attribute => {:alias => :has_attributes},
|
160
|
+
:has_boolean => {:cast_to => :to_b, :alias => :has_booleans},
|
161
|
+
:has_currency => {:cast_to => :to_d, :alias => :has_currencies},
|
162
|
+
:has_date => {:cast_to => :to_date, :alias => :has_dates},
|
163
|
+
:has_decimal => {:cast_to => :to_d, :alias => :has_decimals},
|
164
|
+
:has_float => {:cast_to => :to_f, :alias => :has_floats},
|
165
|
+
:has_int => {:cast_to => :to_i, :alias => :has_ints},
|
166
|
+
:has_time => {:cast_to => :to_time, :alias => :has_times}
|
167
|
+
}
|
153
168
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
169
|
+
AVAILABLE_ATTRIBUTE_METHODS.each do |method,method_options|
|
170
|
+
define_method(method) do |*attributes|
|
171
|
+
options = default_attribute_settings.merge(attributes.extract_options!)
|
172
|
+
options[:on_set] = lambda {|obj,val| val.send(method_options[:cast_to]) } if method_options[:cast_to]
|
173
|
+
create_attribute_methods(attributes,options)
|
160
174
|
end
|
175
|
+
module_eval("alias #{method_options[:alias]} #{method}")
|
161
176
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
attrs.each do |attr|
|
168
|
-
build_attribute_methods(attr,options,[:to_s,:to_time])
|
169
|
-
|
170
|
-
end
|
171
|
-
end
|
172
|
-
alias :has_time :has_times
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.included(base)
|
180
|
+
base.extend(Attributes::ClassMethods)
|
181
|
+
base.send(:include, ActiveModel::Dirty) if base.is_a?(Class) # Add Dirty to the class
|
173
182
|
end
|
174
183
|
end
|
175
|
-
end
|
184
|
+
end
|
data/lib/simple_model/base.rb
CHANGED
@@ -18,19 +18,22 @@ module SimpleModel
|
|
18
18
|
|
19
19
|
# == SimpleModel::Base
|
20
20
|
#
|
21
|
-
# Provides an interface for any class to build
|
21
|
+
# Provides an interface for any class to build tableless models.
|
22
22
|
#
|
23
23
|
# Implements Validations, Callbacks and Dirty from ActiveModel, and datatype specific
|
24
|
-
# attribute definitions with default options
|
24
|
+
# attribute definitions with default options. SimpleModel::Base is intended as
|
25
|
+
# an example, while it may be used in production, which it is on many of my apps
|
26
|
+
# today, it is recommend you use SimpleModel::Base as an example to implement your
|
27
|
+
# own model actions.
|
25
28
|
#
|
26
29
|
# == SimpleModel Actions:
|
27
30
|
#
|
28
31
|
# Model actions provide a tool for making use of Active Model callbacks. Each
|
29
32
|
# action creates an instance method representing the action, which calls the
|
30
|
-
# method(s) listed as
|
33
|
+
# method(s) listed as symbols when defining the actions. Model actions also accept
|
31
34
|
# a rollback option, which is called if the action fails. If you plan to
|
32
|
-
# implement SimpleModel's actions
|
33
|
-
# "create", and "update", as
|
35
|
+
# implement SimpleModel's actions, avoid naming you own methods "save", "destroy",
|
36
|
+
# "create", and "update", as these will override the methods defined by action.
|
34
37
|
#
|
35
38
|
# Available Actions:
|
36
39
|
# # save
|
@@ -53,7 +56,7 @@ module SimpleModel
|
|
53
56
|
# has_integers :first_int, :second_int, :default => 1
|
54
57
|
# has_times :now, :default => :get_now
|
55
58
|
#
|
56
|
-
# save :save_record, :rollback => :
|
59
|
+
# save :save_record, :rollback => :rollback_save
|
57
60
|
#
|
58
61
|
# def save_record
|
59
62
|
# puts "saved"
|
@@ -64,37 +67,25 @@ module SimpleModel
|
|
64
67
|
# Time.now
|
65
68
|
# end
|
66
69
|
#
|
67
|
-
# def
|
70
|
+
# def rollback_save
|
68
71
|
# puts "rolled back"
|
69
72
|
# end
|
70
73
|
# end
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
74
|
|
75
75
|
class Base
|
76
76
|
include SimpleModel::Attributes
|
77
77
|
include SimpleModel::ErrorHelpers
|
78
|
-
|
79
78
|
#Use ActiveModel Resources
|
80
79
|
include ActiveModel::Validations
|
81
80
|
include ActiveModel::Conversion
|
82
81
|
extend ActiveModel::Naming
|
83
82
|
extend ActiveModel::Callbacks
|
84
83
|
include ActiveModel::Validations::Callbacks
|
85
|
-
include ActiveModel::Dirty
|
86
84
|
|
87
85
|
define_model_callbacks :save, :update, :create, :destroy
|
88
86
|
|
89
87
|
class << self
|
90
88
|
|
91
|
-
# Collect methods as they are defined, then add to define_attribute_methods
|
92
|
-
def after_attribute_definition(method)
|
93
|
-
@defined_attribute_methods ||= []
|
94
|
-
@defined_attribute_methods << method
|
95
|
-
define_attribute_methods @defined_attribute_methods
|
96
|
-
end
|
97
|
-
|
98
89
|
def save(*methods)
|
99
90
|
define_model_action(methods,:save)
|
100
91
|
end
|
@@ -107,54 +98,48 @@ module SimpleModel
|
|
107
98
|
define_model_action(methods,:update)
|
108
99
|
end
|
109
100
|
|
110
|
-
#Destroy does not run normal validation
|
101
|
+
# Destroy does not run normal validation in Rails, but with this we can if we choose to.
|
111
102
|
def destroy(*methods)
|
112
103
|
define_model_action(methods,:destroy, {:validate => false})
|
113
104
|
end
|
114
105
|
end
|
115
106
|
|
116
|
-
has_boolean :
|
107
|
+
has_boolean :persisted
|
117
108
|
has_boolean :new_record, :default => true
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
saved?
|
122
|
-
end
|
123
|
-
|
124
|
-
def before_attribute_set(method,val)
|
125
|
-
change_methods_str = "#{method.to_s}_will_change!".to_sym
|
126
|
-
send(change_methods_str) if val != instance_variable_get("@#{method.to_s}") && self.respond_to?(change_methods_str.to_sym)
|
127
|
-
end
|
128
|
-
|
109
|
+
has_attribute :id # may not be an integer
|
110
|
+
alias :saved? :persisted?
|
111
|
+
|
129
112
|
private
|
130
113
|
|
131
114
|
# Skeleton for action instance methods
|
132
115
|
def run_model_action(methods,options)
|
133
116
|
completed = true
|
134
|
-
if !options[:validate] ||
|
135
|
-
(options[:validation_methods] && valid_using_other?(options[:validation_methods])) ||
|
136
|
-
self.valid?
|
137
|
-
|
117
|
+
if (!options[:validate] || (options[:validation_methods] && valid_using_other?(options[:validation_methods])) || self.valid?)
|
138
118
|
methods.each do |method|
|
139
119
|
ran = self.send(method)
|
140
120
|
completed = ran unless ran
|
141
121
|
end
|
142
|
-
|
143
122
|
if completed
|
144
|
-
self.
|
123
|
+
self.persisted = true
|
145
124
|
@previously_changed = changes
|
146
125
|
@changed_attributes.clear
|
147
126
|
else
|
148
127
|
self.send(options[:rollback]) unless options[:rollback].blank?
|
149
128
|
end
|
150
|
-
else
|
129
|
+
else
|
151
130
|
completed = false
|
152
|
-
end
|
131
|
+
end
|
132
|
+
if !completed && options[:raise_exception]
|
133
|
+
if !self.errors.blank?
|
134
|
+
raise ValidationError, self.errors.full_messages.join(" ")
|
135
|
+
else
|
136
|
+
raise ActionError, "failed action: #{methods.join(', ')}"
|
137
|
+
end
|
138
|
+
end
|
153
139
|
completed
|
154
|
-
end
|
155
|
-
|
140
|
+
end
|
156
141
|
|
157
|
-
# Run supplied methods as
|
142
|
+
# Run supplied methods as validation. Each method should return a boolean
|
158
143
|
# If using this option, to see if errors are present use object_name.errors.blank?,
|
159
144
|
# otherwise if you run object_name.valid? you will over write the errors
|
160
145
|
# generated here.
|
@@ -164,17 +149,21 @@ module SimpleModel
|
|
164
149
|
valid = false unless self.send(method)
|
165
150
|
end
|
166
151
|
valid
|
167
|
-
end
|
168
|
-
|
152
|
+
end
|
169
153
|
|
170
|
-
# Defines the model action's
|
171
|
-
#
|
154
|
+
# Defines the model action's instance methods and applied defaults. For every
|
155
|
+
# action defined, we also define that actions ! method which raises exceptions
|
156
|
+
# when the action fails.
|
172
157
|
def self.define_model_action(methods,action,default_options={:validate => true})
|
173
158
|
default_options.merge!(methods.extract_options!)
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
159
|
+
actions = [action,"#{action}!".to_sym]
|
160
|
+
actions.each do |a|
|
161
|
+
define_method(a) do |opts={}|
|
162
|
+
options = default_options.merge(opts)
|
163
|
+
options[:raise_exception] = a.to_s.match(/\!$/)
|
164
|
+
self.run_callbacks(action) do
|
165
|
+
run_model_action(methods,options)
|
166
|
+
end
|
178
167
|
end
|
179
168
|
end
|
180
169
|
end
|