simple_model 1.0.1 → 1.1.0

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/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- simple_model (1.0.0)
4
+ simple_model (1.1.0)
5
5
  activemodel (~> 3.0)
6
6
  activesupport (~> 3.0)
7
7
 
data/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # SimpleModel
2
- A collection of convenience methods for building table-less models. If ActiveModel
3
- gem is installed, SimpleModel::Based will include ActiveModel::Validations,
4
- include ActiveModel::Conversion and extend ActiveModel::Naming. If ActiveModel
5
- gem is not available, SimpleModel::Base defaults to its own built-in Error and Validation modules.
2
+ A collection of convenience methods for building table-less models.
3
+ SimpleModel implements:
4
+
5
+ * ActiveModel::Validations
6
+ * ActiveModel::Conversion
7
+ * ActiveModel::Validations::Callbacks
8
+ * ActiveModel::Dirty
9
+ * ActiveModel::Naming
10
+ * ActiveModel::Callbacks
11
+ * ActiveSupport core extentions (see [SimpleModel::Base](https://github.com/JoshMcKin/simple_model/blob/master/lib/simple_model/base.rb))
12
+
13
+ Additionally SimpleModel implements basic model actions
6
14
 
7
15
  ## Installation
8
16
 
@@ -16,11 +24,20 @@ SimpleModel is available through [Rubygems](http://rubygems.org/gems/simple_mode
16
24
  require 'simple_model'
17
25
 
18
26
  class Item < SimpleModel::Base
27
+ # Model Actions
19
28
  save :save_item, :rollback => :undo_save
29
+
30
+ # Callbacks
31
+ before_validation :add_to_array
32
+
33
+ # Attributes
20
34
  has_booleans :active, :default => true
21
35
  has_booleans :paid
22
- has_currency :price, :default => 10.0
36
+ has_currency :price, :default => 10.0.to_currency
23
37
  has_times :created_at, :default => :now
38
+ has_attribute :my_array, :default => []
39
+
40
+ # Validation
24
41
  validates_inclusion_of :price, :in => 10..25
25
42
 
26
43
  def now
@@ -30,7 +47,13 @@ SimpleModel is available through [Rubygems](http://rubygems.org/gems/simple_mode
30
47
  def file_name
31
48
  @file_name ||= "receipt-#{self.created_at.to_i}.txt"
32
49
  end
33
-
50
+
51
+ private
52
+
53
+ def add_to_array
54
+ my_array << 1
55
+ end
56
+
34
57
  def save_item
35
58
  begin
36
59
  File.open(self.file_name, 'w') do |receipt|
@@ -50,17 +73,25 @@ SimpleModel is available through [Rubygems](http://rubygems.org/gems/simple_mode
50
73
  end
51
74
 
52
75
  item = Item.new
53
- item.created_at # => 2011-10-23 21:56:07 -0500
54
- item.created_at # => 2011-10-23 21:56:08 -0500
55
- item.active # => true
56
- item.paid # => nil
57
- item.paid? # => false
58
- item.price # => 10.0
76
+ item.changed? # => false
77
+ item.created_at # => 2011-10-23 21:56:07 -0500
78
+ item.created_at # => 2011-10-23 21:56:08 -0500
79
+ item.active # => true
80
+ item.paid # => nil
81
+ item.paid? # => false
82
+ item.price # => 10.0
59
83
  item.price = '$1,024.00'
60
- item.price # => #<BigDecimal:100c989d8,'0.1024E4',9(27)>
61
- item.valid? # => false
84
+ item.price # => #<BigDecimal:100c989d8,'0.1024E4',9(27)>
85
+ item.changed? # => true
86
+ item.price_changed? # => true
87
+ item.changes # => {"price"=>[#<BigDecimal:7fc61b250da8,'0.1E2',9(27)>, #<BigDecimal:7fc61b1ba600,'0.1024E4',9(27)>]}
88
+ item.my_array # => []
89
+ item.valid? # => false
90
+ item.my_array # => [1]
62
91
  item.price = 15
63
- item.save # => true
92
+ item.save # => true
93
+ item.changed? # => false
94
+ item.previous_changes # => {"price"=>[#<BigDecimal:7fc61b1ba600,'0.1024E4',9(27)>, #<BigDecimal:7fc61b1730e8,'0.15E2',9(27)>], "saved"=>[nil, true]}
64
95
 
65
96
 
66
97
 
@@ -5,7 +5,8 @@ module SimpleModel
5
5
 
6
6
  module Attributes
7
7
  include ExtendCore
8
-
8
+ extend ActiveSupport::Concern
9
+ include ActiveModel::AttributeMethods
9
10
  #Set attribute values to those supplied at initialization
10
11
  def initialize(*attrs)
11
12
  set_attributes(attrs.extract_options!)
@@ -22,63 +23,99 @@ module SimpleModel
22
23
  self.send("#{attr[0].to_sym}=",attr[1])
23
24
  end
24
25
  end
26
+
27
+ # Hook to run method after attribute is converted but before it is set
28
+ def before_attribute_set(method,val)
29
+ end
30
+
25
31
  alias :update_attributes :set_attributes
26
32
 
27
33
  def self.included(base)
28
34
  base.extend(ClassMethods)
29
35
  end
36
+
37
+ def fetch_default
38
+ end
39
+
30
40
 
31
41
  module ClassMethods
32
-
42
+
43
+ # Hook to call class method after attribute method definitions
44
+ def after_attribute_definition(attr)
45
+ end
46
+
47
+ # Defines a reader method that returns a default value if current value
48
+ # is nil, if :default is present in the options hash
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
60
+ end
61
+
62
+ def define_setter(attr,cast_methods)
63
+ define_method("#{attr.to_s}=") do |val|
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
70
+ end
71
+
72
+ # Builder for attribute methods
73
+ def build_attribute_methods(attr,options={},cast_methods=[])
74
+ define_reader_with_options(attr,options)
75
+ define_setter(attr,cast_methods)
76
+ after_attribute_definition attr
77
+ 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
+
33
93
  #creates setter and getter datatype special attribute
34
94
  def has_attributes(*attrs)
35
95
  options = attrs.extract_options!
36
96
  attrs.each do |attr|
37
-
38
- attr_reader attr
39
- define_reader_with_options(attr,options)
40
- define_method("#{attr.to_s}=") do |val|
41
- instance_variable_set("@#{attr}", val)
42
- attributes[attr] = val
43
- val
44
-
45
- end
97
+ build_attribute_methods(attr,options)
46
98
  end
47
99
  end
100
+ alias :has_attribute :has_attributes
48
101
 
49
102
  # Creates setter and getter methods for boolean attributes
50
103
  def has_booleans(*attrs)
51
104
  options = attrs.extract_options!
52
- attrs.each do |attr|
53
-
54
- attr_reader attr
55
- define_reader_with_options(attr,options)
56
- define_method("#{attr.to_s}=") do |val|
57
- instance_variable_set("@#{attr}", val.to_s.to_b)
58
- attributes[attr] = val
59
- val
60
- end
61
-
105
+ attrs.each do |attr|
106
+ build_attribute_methods(attr,options,[:to_s,:to_b])
62
107
  define_method ("#{attr.to_s}?") do
63
108
  send("#{attr.to_s}".to_sym).to_s.to_b
64
109
  end
65
110
  end
66
111
  end
67
- alias :has_boolean :has_booleans
112
+ alias :has_boolean :has_booleans
68
113
 
69
114
  # Creates setter and getter methods for integer attributes
70
115
  def has_ints(*attrs)
71
116
  options = attrs.extract_options!
72
117
  attrs.each do |attr|
73
- attr_reader attr
74
- define_reader_with_options(attr,options)
75
-
76
- define_method("#{attr.to_s}=") do |val|
77
- instance_variable_set("@#{attr}", val.to_i)
78
- attributes[attr] = val
79
- val
80
-
81
- end
118
+ build_attribute_methods(attr,options,[:to_i])
82
119
  end
83
120
  end
84
121
  alias :has_int :has_ints
@@ -90,26 +127,16 @@ module SimpleModel
90
127
  def has_currency(*attrs)
91
128
  options = attrs.extract_options!
92
129
  attrs.each do |attr|
93
- attr_reader attr
94
- define_reader_with_options(attr,options)
95
- define_method("#{attr.to_s}=") do |val|
96
- instance_variable_set("@#{attr}", val.to_s.to_currency)
97
- attributes[attr] = val
98
- val
99
- end
130
+ build_attribute_methods(attr,options,[:to_s,:to_currency])
131
+
100
132
  end
101
133
  end
102
134
 
103
135
  def has_decimals(*attrs)
104
136
  options = attrs.extract_options!
105
137
  attrs.each do |attr|
106
- attr_reader attr
107
- define_reader_with_options(attr,options)
108
- define_method("#{attr.to_s}=") do |val|
109
- instance_variable_set("@#{attr}", BigDecimal("#{val}"))
110
- attributes[attr] = val
111
- val
112
- end
138
+ build_attribute_methods(attr,options,[:to_f,:to_d])
139
+
113
140
  end
114
141
  end
115
142
  alias :has_decimal :has_decimals
@@ -118,70 +145,31 @@ module SimpleModel
118
145
  def has_floats(*attrs)
119
146
  options = attrs.extract_options!
120
147
  attrs.each do |attr|
148
+ build_attribute_methods(attr,options,[:to_f])
121
149
 
122
- attr_reader attr
123
- define_reader_with_options(attr,options)
124
-
125
- define_method("#{attr.to_s}=") do |val|
126
- instance_variable_set("@#{attr}", val.to_f)
127
- attributes[attr] = val
128
- val
129
-
130
- end
131
150
  end
132
151
  end
133
152
  alias :has_float :has_floats
153
+
134
154
  # Creates setter and getter methods for date attributes
135
155
  def has_dates(*attrs)
136
156
  options = attrs.extract_options!
137
157
  attrs.each do |attr|
158
+ build_attribute_methods(attr,options,[:to_s,:to_date])
138
159
 
139
- attr_reader attr
140
- define_reader_with_options(attr,options)
141
- define_method("#{attr.to_s}=") do |val|
142
- val = val.to_date unless val.nil?
143
- instance_variable_set("@#{attr}", val )
144
- attributes[attr] = val
145
- val
146
-
147
- end
148
160
  end
149
161
  end
150
162
  alias :has_date :has_dates
163
+
151
164
  # Creates setter and getter methods for time attributes
152
165
  def has_times(*attrs)
153
166
  options = attrs.extract_options!
154
167
  attrs.each do |attr|
155
- attr_reader attr
156
- define_reader_with_options(attr,options)
157
- define_method("#{attr.to_s}=") do |val|
158
- val = val.to_time unless val.nil?
159
- instance_variable_set("@#{attr}", val)
160
- attributes[attr] = val
161
- val
162
- end
163
- end
164
- end
165
-
166
- def fetch_alias_name(attr)
167
- alias_name = (attr.to_s << "_old=").to_sym
168
- self.module_eval("alias #{alias_name} #{attr}")
169
-
170
- alias_name
171
- end
168
+ build_attribute_methods(attr,options,[:to_s,:to_time])
172
169
 
173
- # Defines a reader method that returns a default value if current value
174
- # is nil, if :default is present in the options hash
175
- def define_reader_with_options(attr,options)
176
- unless options[:default].blank?
177
- define_method (attr.to_s) do
178
- default = options[:default].is_a?(Symbol) ? self.send(options[:default]) : options[:default]
179
- val = instance_variable_get("@#{attr.to_s}")
180
- val = default if val.nil?
181
- val
182
- end
183
170
  end
184
- end
171
+ end
172
+ alias :has_time :has_times
185
173
  end
186
174
  end
187
175
  end
@@ -20,8 +20,8 @@ module SimpleModel
20
20
  #
21
21
  # Provides an interface for any class to build tabless models.
22
22
  #
23
- # Implements Validations and Callsbacks from ActiveModel and attribute datatype specific
24
- # attribute definition with default options
23
+ # Implements Validations, Callbacks and Dirty from ActiveModel, and datatype specific
24
+ # attribute definitions with default options
25
25
  #
26
26
  # == SimpleModel Actions:
27
27
  #
@@ -82,38 +82,51 @@ module SimpleModel
82
82
  extend ActiveModel::Naming
83
83
  extend ActiveModel::Callbacks
84
84
  include ActiveModel::Validations::Callbacks
85
+ include ActiveModel::Dirty
86
+
85
87
  define_model_callbacks :save, :update, :create, :destroy
86
-
88
+
89
+ class << self
90
+
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
+ def save(*methods)
99
+ define_model_action(methods,:save)
100
+ end
101
+
102
+ def create(*methods)
103
+ define_model_action(methods,:create)
104
+ end
105
+
106
+ def update(*methods)
107
+ define_model_action(methods,:update)
108
+ end
109
+
110
+ #Destroy does not run normal validation by default.
111
+ def destroy(*methods)
112
+ define_model_action(methods,:destroy, {:validate => false})
113
+ end
114
+ end
115
+
87
116
  has_boolean :saved
88
117
  has_boolean :new_record, :default => true
89
-
90
118
  attr_accessor :id
91
-
119
+
92
120
  def persisted?
93
121
  saved?
94
122
  end
95
-
96
123
 
97
- def self.save(*methods)
98
- define_model_action(methods,:save)
99
- end
100
-
101
- def self.create(*methods)
102
- define_model_action(methods,:create)
103
- end
104
-
105
- def self.update(*methods)
106
- define_model_action(methods,:update)
107
- end
108
-
109
- #Destroy does not run normal validation by default.
110
- def self.destroy(*methods)
111
- define_model_action(methods,:destroy, {:validate => false})
112
- end
124
+ def before_attribute_set(method,val)
125
+ send("#{method.to_s}_will_change!") unless val == instance_variable_get("@#{method.to_s}")
126
+ end
113
127
 
114
128
  private
115
-
116
-
129
+
117
130
  # Skeleton for action instance methods
118
131
  def run_model_action(methods,options)
119
132
  completed = true
@@ -125,8 +138,11 @@ module SimpleModel
125
138
  ran = self.send(method)
126
139
  completed = ran unless ran
127
140
  end
141
+
128
142
  if completed
129
143
  self.saved = true
144
+ @previously_changed = changes
145
+ @changed_attributes.clear
130
146
  else
131
147
  self.send(options[:rollback]) unless options[:rollback].blank?
132
148
  end
@@ -5,6 +5,19 @@ module SimpleModel
5
5
  require 'bigdecimal'
6
6
  require 'bigdecimal/util'
7
7
 
8
+ Object.class_eval do
9
+
10
+ # Expects an array of symboles representing methods for casting the object
11
+ # EX: "1".cast_to(:float) # => 1.0
12
+ def cast_to(methods=[])
13
+ val = self
14
+ methods.each do |method|
15
+ val = val.send(method)
16
+ end
17
+ val
18
+ end
19
+ end
20
+
8
21
  Float.class_eval do
9
22
 
10
23
  # any value greater than 0.0 is true
@@ -1,11 +1,13 @@
1
1
  module SimpleModel
2
2
  class SimpleModelRailtie < ::Rails::Railtie
3
3
  initializer "simple_model.extend_core" do
4
- extend SimpleModel::ExtendCore
4
+ extend SimpleModel::ExtendCore
5
5
  end
6
- initializer "simple_model.ar_error_helper" do
7
- class ActiveRecord::Base
8
- include SimpleModel::ErrorHelpers
6
+ if Object.const_defined?(:ActiveRecord)
7
+ initializer "simple_model.ar_error_helper" do
8
+ class ActiveRecord::Base
9
+ include SimpleModel::ErrorHelpers
10
+ end
9
11
  end
10
12
  end
11
13
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleModel
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -75,3 +75,18 @@ describe SimpleModel::Attributes, 'has_booleans' do
75
75
  end
76
76
 
77
77
  end
78
+ describe SimpleModel::Attributes, 'has_attributes' do
79
+ before(:all) do
80
+ class TestBoolean
81
+ include SimpleModel::Attributes
82
+ has_attributes :my_array, :default => []
83
+ end
84
+ end
85
+
86
+ it "should allow default value to be an empty array" do
87
+ test = TestBoolean.new
88
+ test.my_array.should eql([])
89
+ end
90
+
91
+ end
92
+
@@ -147,6 +147,25 @@ describe SimpleModel do
147
147
  t.foo.should eql('foo')
148
148
  t.bar.should eql('bar')
149
149
  end
150
+
151
+ it 'Should implement ActiveModel::Dirty' do
152
+ class TestStuff < SimpleModel::Base
153
+ save :my_save_method
154
+ has_attributes :foo,:bar, :default => "def"
155
+ def my_save_method
156
+ true
157
+ end
158
+ end
159
+
160
+ t = TestStuff.new
161
+ t.foo = "bar"
162
+ t.foo_changed?.should be_true
163
+ t.foo_change.should eql(["def","bar"])
164
+ t.changed?.should be_true
165
+ t.save
166
+ t.changed?.should be_false
167
+ end
168
+
150
169
  end
151
170
 
152
171
  #describe SimpleModel::Errors do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-24 00:00:00.000000000Z
12
+ date: 2011-11-16 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &70355814091020 !ruby/object:Gem::Requirement
16
+ requirement: &70101481150080 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70355814091020
24
+ version_requirements: *70101481150080
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activemodel
27
- requirement: &70355814090520 !ruby/object:Gem::Requirement
27
+ requirement: &70101481149580 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '3.0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70355814090520
35
+ version_requirements: *70101481149580
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70355814090140 !ruby/object:Gem::Requirement
38
+ requirement: &70101481149200 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70355814090140
46
+ version_requirements: *70101481149200
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: autotest
49
- requirement: &70355814089680 !ruby/object:Gem::Requirement
49
+ requirement: &70101481148740 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70355814089680
57
+ version_requirements: *70101481148740
58
58
  description: Simpifies building tableless models or models backed by webservices.
59
59
  Create data type specific attributes with default if values.
60
60
  email: