valuable 0.8.5 → 0.9.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 +7 -0
- data/Gemfile +10 -0
- data/README.markdown +24 -2
- data/Rakefile +30 -0
- data/examples/baseball.rb +21 -0
- data/examples/phone_number.rb +21 -0
- data/lib/valuable.rb +82 -81
- data/lib/valuable/utils.rb +92 -0
- data/test/typical_test.rb +46 -0
- data/test/write_and_read_attribute_test.rb +14 -0
- data/todo.txt +91 -0
- data/valuable.gemspec +23 -0
- data/valuable.version +1 -0
- metadata +29 -25
- data/rakefile.rb +0 -76
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
CHANGED
@@ -3,14 +3,36 @@ Introducing Valuable
|
|
3
3
|
|
4
4
|
Valuable enables quick modeling... it's attr_accessor on steroids. It intends to use a simple and intuitive interface, allowing you easily create models without hassles, so you can get on with the logic specific to your application. I find myself using it in sort of a presenter capacity, when I have to pull data from non-standard data sources, and to handle temporary data during imports.
|
5
5
|
|
6
|
-
Valuable provides DRY decoration like attr_accessor, but includes default values
|
6
|
+
Valuable provides DRY decoration like attr_accessor, but includes default values and other formatting, and a constructor that accepts an attributes hash. It provides a class-level list of attributes, an instance-level attributes hash, and more.
|
7
|
+
|
8
|
+
Type Casting in Ruby? You must be crazy...
|
9
|
+
-------------------------------------------------------------
|
10
|
+
Yeah, I get that alot. I mean, about type casting. I'm not writing
|
11
|
+
C# over here. In fact, I'm going to start using the euphamism
|
12
|
+
'Formatting' just so people will stop looking at me that way.
|
13
|
+
|
14
|
+
Say you have get some data from a web service via JSON.
|
15
|
+
Parse the json and you get this:
|
16
|
+
|
17
|
+
'person' =>
|
18
|
+
'name' => 'Mr. Freud',
|
19
|
+
'phone_number' => '8002195642',
|
20
|
+
'specialization_code' => 2106
|
21
|
+
|
22
|
+
then parse it:
|
23
|
+
|
24
|
+
class Person < Valuable
|
25
|
+
has_value :name, :default => 'Unknown'
|
26
|
+
has_value :phone_number
|
27
|
+
|
28
|
+
|
7
29
|
|
8
30
|
Examples
|
9
31
|
-------
|
10
32
|
|
11
33
|
_basic syntax_
|
12
34
|
|
13
|
-
class Fruit
|
35
|
+
class Fruit < Valuable
|
14
36
|
has_value :name
|
15
37
|
has_collection :vitamins
|
16
38
|
end
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
require 'rake'
|
7
|
+
require 'rake/testtask'
|
8
|
+
|
9
|
+
desc "Run unit tests"
|
10
|
+
Rake::TestTask.new("test") { |t|
|
11
|
+
t.libs << 'test'
|
12
|
+
t.pattern = 'test/*.rb'
|
13
|
+
t.verbose = true
|
14
|
+
t.warning = true
|
15
|
+
}
|
16
|
+
|
17
|
+
|
18
|
+
desc 'Generate HTML readme file'
|
19
|
+
task :readme do
|
20
|
+
`markdown README.markdown > README.html`
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'clean temporary files, rdoc, and gem package'
|
24
|
+
task :clean => [:clobber_package, :clobber_rdoc] do
|
25
|
+
temp_filenames = File.join('**', '*.*~')
|
26
|
+
temp_files = Dir.glob(temp_filenames)
|
27
|
+
|
28
|
+
File.delete(*temp_files)
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Jersey < String
|
2
|
+
def initialize(object)
|
3
|
+
super "Jersey Number #{object})"
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class BaseballPlayer < Valuable
|
8
|
+
|
9
|
+
has_value :at_bats, :klass => Integer
|
10
|
+
has_value :hits, :klass => Integer
|
11
|
+
has_value :league, :default => 'unknown'
|
12
|
+
has_value :name
|
13
|
+
has_value :jersey, :klass => Jersey, :default => 'Unknown'
|
14
|
+
has_value :active, :klass => Boolean
|
15
|
+
|
16
|
+
has_collection :teammates
|
17
|
+
|
18
|
+
def average
|
19
|
+
hits/at_bats.to_f if hits && at_bats
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class PhoneNumber < String
|
2
|
+
def initialize(value)
|
3
|
+
super(value.to_s)
|
4
|
+
end
|
5
|
+
|
6
|
+
def valid?
|
7
|
+
has_ten_digits?
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_ten_digits?
|
11
|
+
self =~ /\d{9}/
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
self.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"(#{self[0..2]}) #{self[3..5]}-#{self[6..9]}" if valid?
|
20
|
+
end
|
21
|
+
end
|
data/lib/valuable.rb
CHANGED
@@ -36,7 +36,7 @@ class Valuable
|
|
36
36
|
# >> bus.attributes
|
37
37
|
# => {:color => 'yellow', :number => 16}
|
38
38
|
def attributes
|
39
|
-
@attributes ||= deep_duplicate_of(self.class.defaults)
|
39
|
+
@attributes ||= Valuable::Utils.deep_duplicate_of(self.class.defaults)
|
40
40
|
end
|
41
41
|
|
42
42
|
# accepts an optional hash that will be used to populate the
|
@@ -51,18 +51,24 @@ class Valuable
|
|
51
51
|
# has_value :size
|
52
52
|
# has_value :owner
|
53
53
|
# has_value :color, :default => 'red'
|
54
|
+
#
|
55
|
+
# def big_feet?
|
56
|
+
# size && size > 15
|
57
|
+
# end
|
54
58
|
# end
|
55
59
|
#
|
56
60
|
# >> shoe = Shoe.new
|
57
61
|
# >> shoe.update_attributes(:size => 16, :owner => 'MJ')
|
58
62
|
# >> shoe.attributes
|
59
63
|
# => {:size => 16, :owner => 'MJ', :color => 'red'}
|
64
|
+
#
|
65
|
+
# can be method-chained
|
66
|
+
#
|
67
|
+
# >> Shoe.new.update_attributes(:size => 16).big_feet?
|
68
|
+
# => true
|
60
69
|
def update_attributes(atts)
|
61
70
|
atts.each{|name, value| __send__("#{name}=", value )}
|
62
|
-
|
63
|
-
|
64
|
-
def deep_duplicate_of(value)
|
65
|
-
Marshal.load(Marshal.dump(value))
|
71
|
+
self
|
66
72
|
end
|
67
73
|
|
68
74
|
def permissive?
|
@@ -78,23 +84,35 @@ class Valuable
|
|
78
84
|
end
|
79
85
|
|
80
86
|
def write_attribute(name, value)
|
81
|
-
|
87
|
+
attribute = Valuable::Utils.find_attribute_for( name, self.class._attributes )
|
88
|
+
|
89
|
+
if attribute
|
90
|
+
self.attributes[attribute] = Valuable::Utils.cast(attribute, value, self.class._attributes)
|
91
|
+
else
|
92
|
+
raise( ArgumentError, "#{self.class.to_s} does not have an attribute or alias '#{name}'", caller) unless self.permissive?
|
93
|
+
end
|
82
94
|
end
|
83
95
|
|
84
96
|
class << self
|
85
97
|
|
86
98
|
# Returns an array of the attributes available on this object.
|
87
99
|
def attributes
|
88
|
-
|
100
|
+
_attributes.keys
|
89
101
|
end
|
90
102
|
|
103
|
+
def _attributes
|
104
|
+
@_attributes ||= {}
|
105
|
+
end
|
106
|
+
|
91
107
|
# Returns a name/value set of the values that will be used on
|
92
108
|
# instanciation unless new values are provided.
|
93
109
|
#
|
94
110
|
# >> Bus.defaults
|
95
111
|
# => {:color => 'yellow'}
|
96
112
|
def defaults
|
97
|
-
|
113
|
+
out = {}
|
114
|
+
_attributes.each{|n, atts| out[n] = atts[:default] unless atts[:default].nil?}
|
115
|
+
out
|
98
116
|
end
|
99
117
|
|
100
118
|
# Decorator method that lets you specify the attributes for your
|
@@ -126,71 +144,43 @@ class Valuable
|
|
126
144
|
# Due to the way Rails handles checkboxes, '0' resolves to FALSE,
|
127
145
|
# though it would normally resolve to TRUE.
|
128
146
|
def has_value(name, options={})
|
147
|
+
Valuable::Utils.check_options_validity(self.class.name, name, options)
|
129
148
|
|
130
149
|
name = name.to_sym
|
131
|
-
|
132
|
-
attributes << name
|
133
|
-
|
134
|
-
defaults[name] = options[:default] unless options[:default].nil?
|
150
|
+
_attributes[name] = options
|
135
151
|
|
136
152
|
create_accessor_for(name)
|
137
153
|
create_question_for(name) if options[:klass] == :boolean
|
138
154
|
create_negative_question_for(name, options[:negative]) if options[:klass] == :boolean && options[:negative]
|
139
155
|
|
140
|
-
create_setter_for(name
|
156
|
+
create_setter_for(name)
|
141
157
|
|
142
158
|
sudo_alias options[:alias], name if options[:alias]
|
143
159
|
sudo_alias "#{options[:alias]}=", "#{name}=" if options[:alias]
|
144
|
-
|
145
|
-
check_options_validity(name, options)
|
146
160
|
end
|
147
161
|
|
148
|
-
# Creates the method that sets the value of an attribute.
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
|
162
|
+
# Creates the method that sets the value of an attribute.
|
163
|
+
# The setter calls write_attribute, which handles typicification.
|
164
|
+
# It is called by the constructor (rather than using
|
165
|
+
# write attribute, which would render any custom setters
|
166
|
+
# ineffective.)
|
167
|
+
#
|
168
|
+
# Setting values via the attributes hash avoids typification,
|
169
|
+
# ie:
|
170
|
+
# >> player.phone = "8778675309"
|
171
|
+
# >> player.phone
|
172
|
+
# => "(877) 867-5309"
|
173
|
+
#
|
174
|
+
# >> player.attributes[:phone] = "8778675309"
|
175
|
+
# >> player.phone
|
176
|
+
# => "8778675309"
|
177
|
+
def create_setter_for(attribute)
|
153
178
|
setter_method = "#{attribute}="
|
154
179
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
define_method setter_method do |value|
|
159
|
-
write_attribute(attribute, value)
|
160
|
-
end
|
161
|
-
|
162
|
-
when :integer
|
163
|
-
|
164
|
-
define_method setter_method do |value|
|
165
|
-
value_as_integer = value && value.to_i
|
166
|
-
write_attribute(attribute, value_as_integer)
|
167
|
-
end
|
168
|
-
|
169
|
-
when :string
|
170
|
-
|
171
|
-
define_method setter_method do |value|
|
172
|
-
value_as_string = value && value.to_s
|
173
|
-
write_attribute(attribute, value_as_string)
|
174
|
-
end
|
175
|
-
|
176
|
-
when :boolean
|
177
|
-
|
178
|
-
define_method setter_method do |value|
|
179
|
-
write_attribute(attribute, value == '0' ? false : !!value )
|
180
|
-
end
|
181
|
-
|
182
|
-
else
|
183
|
-
|
184
|
-
define_method setter_method do |value|
|
185
|
-
if value.nil?
|
186
|
-
write_attribute(attribute, nil)
|
187
|
-
elsif value.is_a? klass
|
188
|
-
write_attribute(attribute, value)
|
189
|
-
else
|
190
|
-
write_attribute(attribute, klass.new(value))
|
191
|
-
end
|
192
|
-
end
|
180
|
+
define_method setter_method do |value|
|
181
|
+
write_attribute(attribute, value)
|
193
182
|
end
|
183
|
+
|
194
184
|
end
|
195
185
|
|
196
186
|
def sudo_alias( alias_name, method_name )
|
@@ -199,11 +189,16 @@ class Valuable
|
|
199
189
|
end
|
200
190
|
end
|
201
191
|
|
202
|
-
# creates a simple accessor method named after the attribute
|
203
|
-
# value it will provide during the life of the instance.
|
192
|
+
# creates a simple accessor method named after the attribute
|
204
193
|
def create_accessor_for(name)
|
205
|
-
define_method name do
|
206
|
-
|
194
|
+
define_method name do |*args|
|
195
|
+
|
196
|
+
if args.length == 0
|
197
|
+
attributes[name]
|
198
|
+
else
|
199
|
+
send("#{name}=", *args)
|
200
|
+
self
|
201
|
+
end
|
207
202
|
end
|
208
203
|
end
|
209
204
|
|
@@ -250,8 +245,28 @@ class Valuable
|
|
250
245
|
# >> bus.riders << 'jack'
|
251
246
|
# >> bus.riders
|
252
247
|
# => ['jack']
|
253
|
-
|
254
|
-
|
248
|
+
#
|
249
|
+
# class Person
|
250
|
+
# has_collection :phone_numbers, :klass => PhoneNumber
|
251
|
+
# end
|
252
|
+
#
|
253
|
+
# >> jenny = Person.new(:phone_numbers => ['8008675309'] )
|
254
|
+
# >> jenny.phone_numbers.first.class
|
255
|
+
# => PhoneNumber
|
256
|
+
def has_collection(name, options = {})
|
257
|
+
Utils.check_options_validity( self.class.name, name, options)
|
258
|
+
name = name.to_sym
|
259
|
+
options[:item_klass] = options[:klass] if options[:klass]
|
260
|
+
options[:klass] = :collection
|
261
|
+
options[:default] = []
|
262
|
+
|
263
|
+
_attributes[name] = options
|
264
|
+
|
265
|
+
create_accessor_for(name)
|
266
|
+
create_setter_for(name)
|
267
|
+
|
268
|
+
sudo_alias options[:alias], name if options[:alias]
|
269
|
+
sudo_alias "#{options[:alias]}=", "#{name}=" if options[:alias]
|
255
270
|
end
|
256
271
|
|
257
272
|
# Instructs the class NOT to complain if any attributes are set
|
@@ -288,23 +303,9 @@ class Valuable
|
|
288
303
|
private
|
289
304
|
|
290
305
|
def inherited(child)
|
291
|
-
|
292
|
-
defaults.each {|(name, value)| child.defaults[name] = value }
|
293
|
-
end
|
294
|
-
|
295
|
-
def known_options
|
296
|
-
[:klass, :default, :negative, :alias]
|
297
|
-
end
|
298
|
-
|
299
|
-
# this helper raises an exception if the options passed to has_value
|
300
|
-
# are wrong. Mostly written because I occasionally used :class instead
|
301
|
-
# of :klass and, being a moron, wasted time trying to find the issue.
|
302
|
-
def check_options_validity(name, options)
|
303
|
-
invalid_options = options.keys - known_options
|
304
|
-
|
305
|
-
raise ArgumentError, "has_value did not know how to respond to option(s) #{invalid_options.join(', ')}. Valid (optional) arguments are: #{known_options.join(', ')}" unless invalid_options.empty?
|
306
|
+
_attributes.each {|n, atts| child._attributes[n] = atts }
|
306
307
|
end
|
307
|
-
|
308
308
|
end
|
309
|
-
|
310
309
|
end
|
310
|
+
|
311
|
+
require 'valuable/utils'
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Valuable::Utils
|
2
|
+
class << self
|
3
|
+
|
4
|
+
def find_attribute_for( name, attributes )
|
5
|
+
name = name.to_sym
|
6
|
+
|
7
|
+
if attributes.keys.include?( name )
|
8
|
+
name
|
9
|
+
elsif found=attributes.find{|n, v| v[:alias].to_sym == name }
|
10
|
+
found[0]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def deep_duplicate_of(value)
|
15
|
+
Marshal.load(Marshal.dump(value))
|
16
|
+
end
|
17
|
+
|
18
|
+
def cast( name, value, attributes, collection_item = false )
|
19
|
+
klass = collection_item ? attributes[name][:item_klass] : attributes[name][:klass]
|
20
|
+
|
21
|
+
case klass
|
22
|
+
when NilClass
|
23
|
+
|
24
|
+
value
|
25
|
+
|
26
|
+
when :collection
|
27
|
+
if( value.kind_of?(Array) )
|
28
|
+
out = value.map do |item|
|
29
|
+
Valuable::Utils.cast( name, item, attributes, true )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
when :date
|
34
|
+
|
35
|
+
case value.class.to_s
|
36
|
+
when "Date"
|
37
|
+
value
|
38
|
+
when "ActiveSupport::TimeWithZone", "Time", "DateTime"
|
39
|
+
value.to_date
|
40
|
+
when "String"
|
41
|
+
value && DateTime.parse(value)
|
42
|
+
else
|
43
|
+
value
|
44
|
+
end
|
45
|
+
|
46
|
+
when :integer
|
47
|
+
|
48
|
+
value && value.to_i
|
49
|
+
|
50
|
+
when :string
|
51
|
+
|
52
|
+
value && value.to_s
|
53
|
+
|
54
|
+
when :boolean
|
55
|
+
|
56
|
+
value == '0' ? false : !!value
|
57
|
+
|
58
|
+
else
|
59
|
+
|
60
|
+
if value.nil?
|
61
|
+
nil
|
62
|
+
elsif value.is_a? klass
|
63
|
+
value
|
64
|
+
else
|
65
|
+
klass.new(value)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def klass_options
|
73
|
+
[NilClass, :date, :integer, :string, :boolean, Class]
|
74
|
+
end
|
75
|
+
|
76
|
+
def known_options
|
77
|
+
[:klass, :default, :negative, :alias]
|
78
|
+
end
|
79
|
+
|
80
|
+
# this helper raises an exception if the options passed to has_value
|
81
|
+
# are wrong. Mostly written because I occasionally used :class instead
|
82
|
+
# of :klass and, being a moron, wasted time trying to find the issue.
|
83
|
+
def check_options_validity( class_name, attribute, options )
|
84
|
+
invalid_options = options.keys - known_options
|
85
|
+
|
86
|
+
raise ArgumentError, "has_value did not know how to respond to option(s) #{invalid_options.join(', ')}. Valid (optional) arguments are: #{known_options.join(', ')}" unless invalid_options.empty?
|
87
|
+
|
88
|
+
raise ArgumentError, "#{class_name} doesn't know how to format #{attribute} with :klass => #{options[:klass].inspect}" unless klass_options.any?{|klass| klass === options[:klass]}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'valuable.rb'
|
5
|
+
require 'date'
|
6
|
+
require File.dirname(__FILE__) + '/../examples/phone_number'
|
7
|
+
class Person < Valuable
|
8
|
+
has_value :dob, :klass => :date
|
9
|
+
end
|
10
|
+
|
11
|
+
class TypicalTest < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def test_that_dates_can_be_set_directly
|
14
|
+
born_on = Date.civil(1976, 07, 26)
|
15
|
+
me = Person.new( :dob => born_on )
|
16
|
+
assert_equal( born_on, me.dob )
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_that_dates_are_parsed_from_strings
|
20
|
+
neil_born_on = 'August 5, 1930'
|
21
|
+
neil = Person.new( :dob => neil_born_on )
|
22
|
+
assert_equal( Date.civil( 1930, 8, 5 ), neil.dob )
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_that_a_date_might_not_be_set_yet_and_that_can_be_ok
|
26
|
+
dr_who = Person.new( :dob => nil )
|
27
|
+
assert_nil( dr_who.dob )
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_that_collections_are_typified
|
31
|
+
people = Class.new(Valuable)
|
32
|
+
people.has_collection( :phones, :klass => PhoneNumber )
|
33
|
+
|
34
|
+
person = people.new(:phones => ['8668675309'])
|
35
|
+
assert_kind_of( Array, person.phones )
|
36
|
+
assert_kind_of( PhoneNumber, person.phones.first )
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_that_it_discovers_an_invalid_klass
|
40
|
+
animal = Class.new(Valuable)
|
41
|
+
assert_raises ArgumentError, "Animal doesn't know how to format species with :klass => 'invalid'" do
|
42
|
+
animal.has_value :species, :klass => :invalid
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -5,6 +5,7 @@ require 'valuable.rb'
|
|
5
5
|
|
6
6
|
class Beer < Valuable
|
7
7
|
has_value :name
|
8
|
+
has_value :brewery
|
8
9
|
end
|
9
10
|
|
10
11
|
class AliasTest < Test::Unit::TestCase
|
@@ -20,5 +21,18 @@ class AliasTest < Test::Unit::TestCase
|
|
20
21
|
beer.write_attribute('name', 'Fosters')
|
21
22
|
assert_equal 'Fosters', beer.name
|
22
23
|
end
|
24
|
+
|
25
|
+
def test_that_values_can_be_set_using_newfangled_way
|
26
|
+
beer = Beer.new
|
27
|
+
beer.name('Abita Amber')
|
28
|
+
assert_equal 'Abita Amber', beer.name
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_newfangled_fluid_chaining
|
32
|
+
beer = Beer.new
|
33
|
+
beer.name('Amber').brewery('Abita')
|
34
|
+
assert_equal 'Abita', beer.brewery
|
35
|
+
end
|
36
|
+
|
23
37
|
end
|
24
38
|
|
data/todo.txt
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
Find some way to do
|
2
|
+
|
3
|
+
has_value :something, :klass => Decimal
|
4
|
+
|
5
|
+
* must handle BigDecimal not being around
|
6
|
+
|
7
|
+
|
8
|
+
- has_value :start_date, :default => {Date.today}
|
9
|
+
- has_value :price, :default => 50, :extend => MoneyFormatter
|
10
|
+
|
11
|
+
class SomeSearchThing < Valuable
|
12
|
+
|
13
|
+
has_value :dob
|
14
|
+
has_value :sign
|
15
|
+
|
16
|
+
parser_for( dob ) do |input|
|
17
|
+
Date.parse(input)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
BEST PRACTICES DOC
|
23
|
+
|
24
|
+
When creating a searcher:
|
25
|
+
|
26
|
+
class UserSearcher < Valuable
|
27
|
+
|
28
|
+
has_value :age
|
29
|
+
has_value :query
|
30
|
+
has_value :expertise
|
31
|
+
has_value :eye_color
|
32
|
+
|
33
|
+
def conditions
|
34
|
+
[ eye_color_conditions, name_conditions, ... ].compact.join(' OR ')
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
- Rails 3 support
|
41
|
+
- Add Valuable#to_yaml and Valuable.from_yaml
|
42
|
+
- make it easy to include validatable or ActiveRecord::Validations
|
43
|
+
- make it easy to use has_value from activerecord model.
|
44
|
+
- add optional :always_klass ?
|
45
|
+
- for :klass => :integer, 'abc' resolves to 0 ... fix that.
|
46
|
+
- optionally, values should be on the class rather than the instance, as in:
|
47
|
+
Config < ValuableClass
|
48
|
+
has_value :background_color
|
49
|
+
end
|
50
|
+
|
51
|
+
Config.background_color
|
52
|
+
|
53
|
+
- 2. Create a 'Valuable::Presenter' base class that filters out rails params, handles nested params, knows 0 is false for checkboxes, works with form_for#checkbox...
|
54
|
+
|
55
|
+
class ProductsController < ApplicationController
|
56
|
+
|
57
|
+
def reviews
|
58
|
+
@presenter = ReviewPresenter.new(params)
|
59
|
+
|
60
|
+
@product = @presenter.product
|
61
|
+
@reviews = @presenter.reviews
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
which currently raises:
|
68
|
+
undefined method `action=' for #<ReviewPresenter:0x5e7fa18>
|
69
|
+
|
70
|
+
|
71
|
+
like this:
|
72
|
+
def initialize(atts = {})
|
73
|
+
super(atts.except('action', 'controller'))
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
:klass => :decimal
|
79
|
+
- When is decimal available? Only load then.
|
80
|
+
- If calling :decimal and it hasn't loaded, error
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
class Change < Decimal
|
85
|
+
extend Valuable::whatever or
|
86
|
+
extend Valuable
|
87
|
+
|
88
|
+
...
|
89
|
+
end
|
90
|
+
|
91
|
+
- do a better job with method_missing
|
data/valuable.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
version = File.read(File.expand_path("../valuable.version",__FILE__)).strip
|
3
|
+
|
4
|
+
spec = Gem::Specification.new do |s|
|
5
|
+
s.name = 'valuable'
|
6
|
+
s.version = version
|
7
|
+
s.summary = "attr_accessor on steroids with defaults, default constructor, and attribute formatting."
|
8
|
+
s.description = "Valuable is a ruby base class that is essentially attr_accessor on steroids. A simple and intuitive interface allows you to get on with modeling in your app."
|
9
|
+
s.license = 'MIT'
|
10
|
+
|
11
|
+
s.require_path = 'lib'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
|
17
|
+
s.has_rdoc = true
|
18
|
+
|
19
|
+
s.authors = ["Johnathon Wright"]
|
20
|
+
s.email = "jw@mustmodify.com"
|
21
|
+
s.homepage = "http://valuable.mustmodify.com/"
|
22
|
+
end
|
23
|
+
|
data/valuable.version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.9.0
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: valuable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 59
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Johnathon Wright
|
@@ -15,11 +15,11 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-05-18 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
22
|
-
description: Valuable is a ruby base class that is essentially attr_accessor on steroids.
|
22
|
+
description: Valuable is a ruby base class that is essentially attr_accessor on steroids. A simple and intuitive interface allows you to get on with modeling in your app.
|
23
23
|
email: jw@mustmodify.com
|
24
24
|
executables: []
|
25
25
|
|
@@ -28,19 +28,28 @@ extensions: []
|
|
28
28
|
extra_rdoc_files: []
|
29
29
|
|
30
30
|
files:
|
31
|
-
-
|
31
|
+
- .gitignore
|
32
|
+
- Gemfile
|
32
33
|
- README.markdown
|
33
|
-
-
|
34
|
-
-
|
34
|
+
- Rakefile
|
35
|
+
- examples/baseball.rb
|
36
|
+
- examples/phone_number.rb
|
37
|
+
- lib/valuable.rb
|
38
|
+
- lib/valuable/utils.rb
|
35
39
|
- test/alias_test.rb
|
36
|
-
- test/
|
37
|
-
- test/inheritance_test.rb
|
40
|
+
- test/bad_attributes_test.rb
|
38
41
|
- test/deprecated_test.rb
|
42
|
+
- test/inheritance_test.rb
|
43
|
+
- test/typical_test.rb
|
39
44
|
- test/valuable_test.rb
|
45
|
+
- test/write_and_read_attribute_test.rb
|
46
|
+
- todo.txt
|
47
|
+
- valuable.gemspec
|
48
|
+
- valuable.version
|
40
49
|
has_rdoc: true
|
41
|
-
homepage: http://valuable.mustmodify.com
|
42
|
-
licenses:
|
43
|
-
|
50
|
+
homepage: http://valuable.mustmodify.com/
|
51
|
+
licenses:
|
52
|
+
- MIT
|
44
53
|
post_install_message:
|
45
54
|
rdoc_options: []
|
46
55
|
|
@@ -66,15 +75,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
75
|
version: "0"
|
67
76
|
requirements: []
|
68
77
|
|
69
|
-
rubyforge_project:
|
70
|
-
rubygems_version: 1.
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.4.2
|
71
80
|
signing_key:
|
72
81
|
specification_version: 3
|
73
|
-
summary: attr_accessor on steroids with defaults, constructor, and
|
74
|
-
test_files:
|
75
|
-
|
76
|
-
- test/alias_test.rb
|
77
|
-
- test/write_and_read_attribute_test.rb
|
78
|
-
- test/inheritance_test.rb
|
79
|
-
- test/deprecated_test.rb
|
80
|
-
- test/valuable_test.rb
|
82
|
+
summary: attr_accessor on steroids with defaults, default constructor, and attribute formatting.
|
83
|
+
test_files: []
|
84
|
+
|
data/rakefile.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'config.rb'
|
3
|
-
|
4
|
-
require 'rake'
|
5
|
-
require 'rake/rdoctask'
|
6
|
-
require 'rake/testtask'
|
7
|
-
require 'rake/packagetask'
|
8
|
-
require 'rake/gempackagetask'
|
9
|
-
require 'rake/contrib/rubyforgepublisher'
|
10
|
-
|
11
|
-
task :default => [:test]
|
12
|
-
|
13
|
-
PKG_FILE_NAME = "#{CONFIG[:name]}-#{CONFIG[:version]}"
|
14
|
-
RUBY_FORGE_PROJECT = 'valuable'
|
15
|
-
RUBY_FORGE_USER = 'mustmodify'
|
16
|
-
|
17
|
-
desc "Run unit tests"
|
18
|
-
Rake::TestTask.new("test") { |t|
|
19
|
-
t.pattern = 'test/*_test.rb'
|
20
|
-
t.verbose = true
|
21
|
-
t.warning = true
|
22
|
-
}
|
23
|
-
|
24
|
-
desc 'clean temporary files, rdoc, and gem package'
|
25
|
-
task :clean => [:clobber_package, :clobber_rdoc] do
|
26
|
-
temp_filenames = File.join('**', '*.*~')
|
27
|
-
temp_files = Dir.glob(temp_filenames)
|
28
|
-
if temp_files.empty?
|
29
|
-
puts 'no temp files to delete'
|
30
|
-
else
|
31
|
-
puts "deleting #{temp_files.size} temp files"
|
32
|
-
end
|
33
|
-
|
34
|
-
File.delete(*temp_files)
|
35
|
-
end
|
36
|
-
|
37
|
-
desc 'Generate documentation for the Valuable plugin'
|
38
|
-
Rake::RDocTask.new(:rdoc) do |rdoc|
|
39
|
-
rdoc.title = 'Valuable - light weight modeling'
|
40
|
-
rdoc.options << '--line-numbers'
|
41
|
-
rdoc.options << '--inline-source'
|
42
|
-
rdoc.rdoc_files.include('README.markdown')
|
43
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
-
end
|
45
|
-
|
46
|
-
spec = Gem::Specification.new do |s|
|
47
|
-
s.name = CONFIG[:name]
|
48
|
-
s.version = CONFIG[:version]
|
49
|
-
s.platform = Gem::Platform::RUBY
|
50
|
-
s.summary = 'attr_accessor on steroids with defaults, constructor, and light casting.'
|
51
|
-
s.description = "Valuable is a ruby base class that is essentially attr_accessor on steroids. It intends to use a simple and intuitive interface, allowing you to get on with the logic specific to your application."
|
52
|
-
|
53
|
-
s.files = FileList["{lib, test, examples}/**/*"].to_a + %w( README.markdown rakefile.rb )
|
54
|
-
s.require_path = 'lib'
|
55
|
-
s.test_files = FileList["{test}/**/*test.rb"].to_a
|
56
|
-
s.has_rdoc = true
|
57
|
-
|
58
|
-
s.rubyforge_project = RUBY_FORGE_PROJECT
|
59
|
-
s.author = 'Johnathon Wright'
|
60
|
-
s.email = 'jw@mustmodify.com'
|
61
|
-
s.homepage = 'http://valuable.mustmodify.com'
|
62
|
-
end
|
63
|
-
|
64
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
65
|
-
#pkg.need_zip = true
|
66
|
-
#pkg.need_tar = true
|
67
|
-
end
|
68
|
-
|
69
|
-
desc "Publish the API documentation"
|
70
|
-
task :pdoc => [:rdoc] do
|
71
|
-
Rake::RubyForgePublisher.new(RUBY_FORGE_PROJECT, RUBY_FORGE_USER).upload
|
72
|
-
end
|
73
|
-
|
74
|
-
desc 'Publish the gem and API docs'
|
75
|
-
task :publish => [:pdoc, :rubyforge_upload]
|
76
|
-
|