simple_model 1.1.1 → 1.2.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/.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
@@ -4,26 +4,13 @@ module SimpleModel
|
|
4
4
|
require 'date'
|
5
5
|
require 'bigdecimal'
|
6
6
|
require 'bigdecimal/util'
|
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
7
|
|
21
|
-
Float.class_eval do
|
22
|
-
|
23
|
-
# any value greater than 0.0 is true
|
8
|
+
Float.class_eval do
|
9
|
+
# that does not equal 0.0 is true
|
24
10
|
def to_b
|
25
|
-
self
|
11
|
+
self != 0.0
|
26
12
|
end
|
13
|
+
|
27
14
|
# Rounds float to the precision specified
|
28
15
|
# 100.5235.round_to #=> 101.0
|
29
16
|
# 100.5235.round_to(1) #=> 101.5
|
@@ -40,8 +27,7 @@ module SimpleModel
|
|
40
27
|
end
|
41
28
|
|
42
29
|
# Returns a string with representation of currency, rounded to nearest hundredth
|
43
|
-
def to_currency_s(symbol="$")
|
44
|
-
|
30
|
+
def to_currency_s(symbol="$")
|
45
31
|
num = self.round_to(2).to_s
|
46
32
|
neg = num.include?("-")
|
47
33
|
while num.index('.') != (num.length-3)
|
@@ -59,9 +45,16 @@ module SimpleModel
|
|
59
45
|
end
|
60
46
|
num
|
61
47
|
end
|
48
|
+
|
49
|
+
def to_time
|
50
|
+
Time.at(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_date
|
54
|
+
Time.at(self).to_date
|
55
|
+
end
|
62
56
|
end
|
63
57
|
|
64
|
-
|
65
58
|
#Extend Ruby String.rb
|
66
59
|
String.class_eval do
|
67
60
|
|
@@ -70,21 +63,22 @@ module SimpleModel
|
|
70
63
|
['1',"true", "t"].include?(self)
|
71
64
|
end
|
72
65
|
|
73
|
-
|
74
66
|
# Takes known US formatted date/time strings (MM/DD/YYYY TIME) and converts
|
75
|
-
# them to international format (YYYY/MM/DD TIME)
|
67
|
+
# them to international format (YYYY/MM/DD TIME). Also cleans up C# JSON
|
68
|
+
# Date (really a time integer) value.
|
76
69
|
#
|
77
|
-
# * safe_date_string("12/31/2010")
|
78
|
-
# * safe_date_string("12/31/2010T23:30:25")
|
79
|
-
# * safe_date_string("12/31/2010 23:30:25")
|
70
|
+
# * safe_date_string("12/31/2010") # => '2010-12-31'
|
71
|
+
# * safe_date_string("12/31/2010T23:30:25") # => '2010-12-31T23:30:25'
|
72
|
+
# * safe_date_string("12/31/2010 23:30:25") # => '2010-12-31 23:30:25'
|
73
|
+
# * safe_date_string("\/Date(1310669017000)\/") # =>
|
80
74
|
def safe_datetime_string
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
if
|
85
|
-
split =
|
75
|
+
safe_date = nil
|
76
|
+
if self[0..9].match(/^(0[1-9]|[1-9]|1[012])[- \/.]([1-9]|0[1-9]|[12][0-9]|3[01])[- \/.][0-9][0-9][0-9][0-9]/)
|
77
|
+
safe_date = ""
|
78
|
+
if self.include?("/")
|
79
|
+
split = self.split("/")
|
86
80
|
else
|
87
|
-
split =
|
81
|
+
split = self.split("-")
|
88
82
|
end
|
89
83
|
time = ""
|
90
84
|
if split[2].length > 4
|
@@ -92,108 +86,111 @@ module SimpleModel
|
|
92
86
|
split[2] = split[2][0..3]
|
93
87
|
end
|
94
88
|
if split.length == 3 && split[2].length == 4
|
95
|
-
|
96
|
-
|
89
|
+
safe_date << "#{split[2]}-#{split[0]}-#{split[1]}"
|
90
|
+
safe_date << "#{time}" unless time.nil? || time.to_s.length == 0
|
97
91
|
end
|
92
|
+
elsif self.match(/^\/Date\(/)
|
93
|
+
safe_date = Time.at(((self.gsub(/(\/Date\()/,"")).gsub(/\)\/$/,"").to_f) / 1000).to_s
|
94
|
+
else
|
95
|
+
safe_date = self
|
98
96
|
end
|
99
|
-
|
100
|
-
date_string
|
97
|
+
safe_date
|
101
98
|
end
|
102
99
|
|
103
100
|
# Use safe_datetime_string help with those pesky US date formats in Ruby 1.9
|
101
|
+
# or to change an integer string to date
|
104
102
|
def to_date
|
103
|
+
return safe_datetime_string.to_i.to_date if self.match(/^+d$/)
|
105
104
|
Date.parse(safe_datetime_string)
|
106
105
|
end
|
107
106
|
|
108
107
|
# Use safe_datetime_string help with those pesky US date formats in Ruby 1.9
|
108
|
+
# or to change an integer string to date
|
109
109
|
def to_time
|
110
|
+
return safe_datetime_string.to_i.to_time if self.match(/^+d$/)
|
110
111
|
Time.parse(safe_datetime_string)
|
111
112
|
end
|
112
113
|
|
113
|
-
|
114
|
-
alias :old_to_f :to_f
|
114
|
+
alias :core_to_f :to_f
|
115
115
|
|
116
116
|
# Remove none numeric characters then run default ruby float cast
|
117
117
|
def to_f
|
118
|
-
gsub(/[^0-9\.\+\-]/, '').
|
118
|
+
gsub(/[^0-9\.\+\-]/, '').core_to_f
|
119
119
|
end
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
def
|
124
|
-
gsub(/[^0-9\.\+\-]/, '').
|
120
|
+
|
121
|
+
alias :core_to_d :to_d
|
122
|
+
|
123
|
+
def to_d
|
124
|
+
gsub(/[^0-9\.\+\-]/, '').core_to_d
|
125
125
|
end
|
126
|
-
|
126
|
+
alias :to_currency :to_d
|
127
|
+
|
128
|
+
end
|
127
129
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
def parse_name(seperate_middle_name=true)
|
136
|
-
str = self
|
137
|
-
if str.include?(',') # Rearrange names formatted as Doe, John C. to John C. Doe
|
138
|
-
temp = str.split(',')
|
139
|
-
temp << temp[0]
|
140
|
-
temp.delete_at(0)
|
141
|
-
str = temp.join(" ")
|
142
|
-
|
143
|
-
end
|
144
|
-
parts = str.split # First, split the name into an array
|
145
|
-
|
146
|
-
parts.each_with_index do |part, i|
|
147
|
-
# If any part is "and", then put together the two parts around it
|
148
|
-
# For example, "Mr. and Mrs." or "Mickey and Minnie"
|
149
|
-
if part=~/^(and|&)$/i && i > 0
|
150
|
-
parts[i-1] = [parts.delete_at(i+1), parts.at(i).downcase, parts.delete_at(i-1)].reverse * " "
|
151
|
-
end
|
152
|
-
end if self=~/\s(and|&)\s/i # optimize
|
153
|
-
|
154
|
-
{ :prefix => (parts.shift if parts[0]=~/^\w+\./),
|
155
|
-
:first_name => parts.shift || "", # if name is "", then atleast first_name should be ""
|
156
|
-
:suffix => (parts.pop if parts[-1]=~/(\w+\.|[IVXLM]+|[A-Z]+\.|(?i)jr|(?i)sr )$/),
|
157
|
-
:last_name => (seperate_middle_name ? parts.pop : parts.slice!(0..-1) * " "),
|
158
|
-
:middle_name => (parts * " " unless parts.empty?) }
|
130
|
+
BigDecimal.class_eval do
|
131
|
+
def to_currency_s(symbol="$")
|
132
|
+
self.to_f.to_currency_s(symbol)
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_b
|
136
|
+
self != 0.0
|
159
137
|
end
|
160
138
|
end
|
161
139
|
|
162
|
-
BigDecimal.class_eval do
|
163
|
-
def to_currency_s(symbol="$")
|
164
|
-
self.to_f.to_currency_s(symbol)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
140
|
Fixnum.class_eval do
|
169
|
-
|
170
|
-
|
171
|
-
self > 0
|
141
|
+
def to_currency_s(symbol="$")
|
142
|
+
self.to_f.to_currency_s(symbol)
|
172
143
|
end
|
173
144
|
|
174
|
-
|
175
|
-
|
145
|
+
unless Fixnum.instance_methods.include?(:to_b)
|
146
|
+
#Any value greater than 0 is true
|
147
|
+
def to_b
|
148
|
+
self > 0
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
unless Fixnum.instance_methods.include?(:to_d)
|
153
|
+
def to_d
|
154
|
+
BigDecimal.new("#{self}.0")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
unless Fixnum.instance_methods.include?(:to_date)
|
159
|
+
def to_date
|
160
|
+
Time.at(self).to_date
|
161
|
+
end
|
162
|
+
end
|
163
|
+
unless Fixnum.instance_methods.include?(:to_time)
|
164
|
+
def to_time
|
165
|
+
Time.at(self)
|
166
|
+
end
|
176
167
|
end
|
177
168
|
end
|
178
169
|
|
179
170
|
NilClass.class_eval do
|
180
|
-
|
181
|
-
|
171
|
+
unless NilClass.instance_methods.include?(:to_b)
|
172
|
+
def to_b
|
173
|
+
false
|
174
|
+
end
|
182
175
|
end
|
183
|
-
|
184
|
-
|
185
|
-
|
176
|
+
unless NilClass.instance_methods.include?(:to_d)
|
177
|
+
def to_d
|
178
|
+
BigDecimal.new("0.0")
|
179
|
+
end
|
186
180
|
end
|
187
181
|
end
|
188
182
|
TrueClass.class_eval do
|
189
|
-
|
190
|
-
|
191
|
-
|
183
|
+
unless TrueClass.instance_methods.include?(:to_b)
|
184
|
+
def to_b
|
185
|
+
self
|
186
|
+
end
|
192
187
|
end
|
193
188
|
end
|
194
189
|
FalseClass.class_eval do
|
195
|
-
|
196
|
-
|
190
|
+
unless FalseClass.instance_methods.include?(:to_b)
|
191
|
+
def to_b
|
192
|
+
self
|
193
|
+
end
|
197
194
|
end
|
198
195
|
end
|
199
196
|
end
|
data/lib/simple_model/version.rb
CHANGED
data/lib/simple_model.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2010-
|
2
|
+
# Copyright (c) 2010-2012 Joshua Thomas Mckinney
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -21,20 +21,30 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
module SimpleModel
|
24
|
-
|
25
|
-
#Get those rails goodies
|
26
24
|
require 'active_support'
|
27
25
|
require 'active_support/i18n'
|
26
|
+
require 'active_support/time'
|
27
|
+
require 'active_support/core_ext/class/attribute'
|
28
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
29
|
+
require 'active_support/core_ext/class/delegating_attributes'
|
30
|
+
require 'active_support/core_ext/class/attribute'
|
31
|
+
require 'active_support/core_ext/array/extract_options'
|
32
|
+
require 'active_support/core_ext/hash/deep_merge'
|
33
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
34
|
+
require 'active_support/core_ext/hash/slice'
|
35
|
+
require 'active_support/core_ext/string/behavior'
|
36
|
+
require 'active_support/core_ext/kernel/singleton_class'
|
37
|
+
require 'active_support/core_ext/module/delegation'
|
38
|
+
require 'active_support/core_ext/module/introspection'
|
39
|
+
require 'active_support/core_ext/object/duplicable'
|
40
|
+
require 'active_support/core_ext/object/blank'
|
28
41
|
require 'active_model'
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
autoload :Base, "simple_model/base"
|
36
|
-
|
37
|
-
#Railtie
|
42
|
+
require "simple_model/extend_core"
|
43
|
+
require 'simple_model/exceptions'
|
44
|
+
require "simple_model/attributes"
|
45
|
+
require "simple_model/error_helpers"
|
46
|
+
require "simple_model/validation"
|
47
|
+
require "simple_model/base"
|
38
48
|
require 'simple_model/simple_model_railtie.rb' if defined?(Rails)
|
39
|
-
|
49
|
+
|
40
50
|
end
|
data/simple_model.gemspec
CHANGED
data/spec/attributes_spec.rb
CHANGED
@@ -9,84 +9,159 @@ describe SimpleModel::Attributes do
|
|
9
9
|
@init = TestInit.new(:test1 => "1", :test2 => '2')
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
12
|
it "should set provided attributes on initialize" do
|
14
|
-
|
15
13
|
@init.test1.should eql("1")
|
16
14
|
@init.test2.should eql("2")
|
17
15
|
end
|
18
16
|
|
19
17
|
it "should include set attributes in attributes hash" do
|
20
|
-
@init.attributes.
|
18
|
+
@init.attributes.should be_kind_of(ActiveSupport::HashWithIndifferentAccess)
|
21
19
|
@init.attributes[:test1].should eql("1")
|
22
20
|
@init.attributes[:test2].should eql("2")
|
23
21
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
|
23
|
+
context '#new_with_store'do
|
24
|
+
it "should use the provided object as the attribute store" do
|
25
|
+
my_store = {:test1 => 1,:test2 => 2}
|
26
|
+
new = TestInit.new_with_store(my_store)
|
27
|
+
new.test1 = 3
|
28
|
+
new.test1.should eql(3)
|
29
|
+
my_store[:test1].should eql(new.test1)
|
32
30
|
end
|
33
31
|
end
|
34
|
-
|
35
|
-
it "should define setter method with default value" do
|
36
|
-
default = TestDefault.new
|
37
|
-
default.test.should eql("test")
|
38
|
-
end
|
39
|
-
it "should not intefer with setting" do
|
40
|
-
default = TestDefault.new
|
41
|
-
default.test = "New"
|
42
|
-
default.test.should eql("New")
|
43
|
-
end
|
44
32
|
|
45
|
-
context
|
46
|
-
|
33
|
+
context "AVAILABLE_ATTRIBUTE_METHODS" do
|
34
|
+
SimpleModel::Attributes::ClassMethods::AVAILABLE_ATTRIBUTE_METHODS.each do |m,options|
|
35
|
+
it "should respond to #{m}" do
|
36
|
+
TestInit.respond_to?(m).should be_true
|
37
|
+
end
|
38
|
+
it "should respond to alias #{options[:alias]}" do
|
39
|
+
TestInit.respond_to?(options[:alias]).should be_true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context '#has_attribute' do
|
45
|
+
before(:all) do
|
47
46
|
class TestDefault
|
48
47
|
include SimpleModel::Attributes
|
49
|
-
|
50
|
-
|
48
|
+
has_attribute :foo, :default => "foo", :allow_blank => false
|
49
|
+
has_attribute :bar, :default => :default_value
|
50
|
+
has_attribute :fab, :default => :some_symbol
|
51
|
+
has_attribute :hop, :default => :default_hop, :allow_blank => false
|
52
|
+
has_attribute :tip, :default => "2", :initialize => false, :allow_blank => false
|
53
|
+
has_attribute :nap
|
54
|
+
has_attribute :my_array, :default => []
|
51
55
|
def default_value
|
52
|
-
"
|
56
|
+
"bar"
|
57
|
+
end
|
58
|
+
|
59
|
+
def default_hop
|
60
|
+
"hop" if nap
|
53
61
|
end
|
54
62
|
end
|
55
|
-
|
56
|
-
default = TestDefault.new
|
57
|
-
default.test.should eql("test")
|
58
63
|
end
|
59
|
-
|
60
|
-
|
64
|
+
|
65
|
+
before(:each) do
|
66
|
+
@default = TestDefault.new
|
67
|
+
end
|
61
68
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
69
|
+
it "should define setter method" do
|
70
|
+
@default.respond_to?(:foo=).should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should define reader/getter method" do
|
74
|
+
@default.respond_to?(:foo).should be_true
|
75
|
+
end
|
76
|
+
|
77
|
+
context ':initialize => false' do
|
78
|
+
it "should not initialize with the default value" do
|
79
|
+
@default.attributes[:tip].should be_nil
|
80
|
+
@default.tip.should eql("2")
|
81
|
+
end
|
82
|
+
context "allow_blank => false"do
|
83
|
+
it "should not initialize, but should set the value on get" do
|
84
|
+
@default.attributes[:tip].should be_nil
|
85
|
+
@default.tip.should eql("2")
|
86
|
+
end
|
87
|
+
end
|
68
88
|
end
|
89
|
+
|
90
|
+
it "should call the method it describe by the default value if it exists" do
|
91
|
+
@default.attributes[:bar].should eql("bar")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should set the defaul to the supplied symbol, if the method does not exist" do
|
95
|
+
@default.attributes[:fab].should eql(:some_symbol)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should allow default value to be an empty array" do
|
99
|
+
@default.my_array.should eql([])
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should create a boolean? method for each attribute" do
|
103
|
+
@default.respond_to?(:foo?).should be_true
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should return !blank?" do
|
107
|
+
@default.my_array.should eql([]) # blank array
|
108
|
+
@default.my_array?.should be_false
|
109
|
+
@default.my_array << 1
|
110
|
+
@default.my_array?.should be_true
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should not allow blank if set" do
|
114
|
+
@default.foo.should eql("foo")
|
115
|
+
@default.foo = ""
|
116
|
+
@default.foo.should eql("foo")
|
117
|
+
@default.foo = "not blank"
|
118
|
+
@default.foo.should eql("not blank")
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should try for the default if its blank on get" do
|
122
|
+
@default.hop.blank?.should be_true
|
123
|
+
@default.nap = "yep"
|
124
|
+
@default.hop.should eql("hop")
|
125
|
+
end
|
126
|
+
|
127
|
+
|
69
128
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
129
|
+
|
130
|
+
context "on get" do
|
131
|
+
it "should perform on_get when set" do
|
132
|
+
class OnGet
|
133
|
+
include SimpleModel::Attributes
|
134
|
+
has_attribute :foo, :on_get => lambda{|obj,attr| (attr.blank? ? obj.send(:foo_default) : attr)}
|
135
|
+
|
136
|
+
def foo_default
|
137
|
+
"test"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
new = OnGet.new
|
142
|
+
new.foo.should eql("test")
|
143
|
+
new.foo = "foo"
|
144
|
+
new.foo.should eql("foo")
|
145
|
+
end
|
75
146
|
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
147
|
+
|
148
|
+
context 'if supplied value can be cast' do
|
149
|
+
it "should throw an exception" do
|
150
|
+
class TestThrow
|
151
|
+
include SimpleModel::Attributes
|
152
|
+
has_booleans :boo
|
153
|
+
end
|
154
|
+
|
155
|
+
lambda{TestThrow.new(:boo => [])}.should raise_error(SimpleModel::ArgumentError)
|
83
156
|
end
|
157
|
+
|
84
158
|
end
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
159
|
+
|
160
|
+
|
161
|
+
after(:all) do
|
162
|
+
Object.send(:remove_const,:TestThrow)
|
163
|
+
Object.send(:remove_const,:OnGet)
|
164
|
+
Object.send(:remove_const,:TestDefault)
|
165
|
+
Object.send(:remove_const,:TestInit)
|
89
166
|
end
|
90
|
-
|
91
|
-
end
|
92
|
-
|
167
|
+
end
|