valuable 0.9.6 → 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +85 -50
- data/lib/valuable.rb +23 -8
- data/lib/valuable/utils.rb +1 -1
- data/test/extending_test.rb +30 -0
- data/test/write_and_read_attribute_test.rb +1 -1
- data/valuable.version +1 -1
- metadata +4 -3
data/README.markdown
CHANGED
@@ -27,7 +27,7 @@ Contents
|
|
27
27
|
- [Collections](#collections)
|
28
28
|
- [Formatting Collections](#formatting-collections)
|
29
29
|
- [Registering Formatters](#registering-formatters)
|
30
|
-
- [More about
|
30
|
+
- [More about Attributes](#more-about-attributes)
|
31
31
|
- [Advanced Input Parsing](#advanced-input-parsing)
|
32
32
|
- [Advanced Defaults](#advanced-defaults)
|
33
33
|
- [Advanced Collection Formatting](#advanced-collection-formatting)
|
@@ -40,9 +40,9 @@ Valuable was created to help you quickly model things. Things I find myself mode
|
|
40
40
|
+ **data imported from JSON, XML, etc**
|
41
41
|
+ **the result of an API call**
|
42
42
|
+ **a subset of some data in an ORM class** say you have a class Person with street, city, state and zip. It might not make sense to store this in a separate table, but you can still create an Address model to hold address-related logic and state like geocode, post_office_box? and Address#==
|
43
|
-
+ **as a presenter that wraps model** This way you keep view-specific methods out of views and models.
|
43
|
+
+ **as a presenter that wraps a model** This way you keep view-specific methods out of views and models.
|
44
44
|
+ **as a presenter that aggregates several models** Generating a map might involve coordinating several different collections of data. Create a valuable class to handle that integration.
|
45
|
-
+ **to model search forms** - Use Valuable to model an advanced search form. Create an attribute for each drop-down, check-
|
45
|
+
+ **to model search forms** - Use Valuable to model an advanced search form. Create an attribute for each drop-down, check-box, and text field, and constants to store options. Integrates easily with Rails via @search = CustomerSearch.new(params[:search]) and form_for(@search, :url => ...)
|
46
46
|
+ **to model reports** like search forms, reports can be stateful when they have critiera that can be selected via form.
|
47
47
|
+ **as a query builder** ie, "I need to create an (Arel or SQL) query based off of form input." (see previous two points)
|
48
48
|
+ **experiments / spikes**
|
@@ -51,53 +51,68 @@ Valuable was created to help you quickly model things. Things I find myself mode
|
|
51
51
|
Methods
|
52
52
|
=============
|
53
53
|
|
54
|
-
|
54
|
+
Class-Level Methods
|
55
|
+
-------------------
|
55
56
|
|
56
|
-
|
57
|
+
###has_value( field_name, options = {})
|
57
58
|
|
58
|
-
|
59
|
+
creates a getter and setter named field_name
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
options:
|
62
|
+
+ **default** - provide a default value
|
63
|
+
|
64
|
+
class Task < Valuable
|
65
|
+
has_value :status, :default => 'Active'
|
66
|
+
end
|
67
|
+
|
64
68
|
>> Task.new.status
|
65
69
|
=> 'Active'
|
66
70
|
|
67
|
-
|
71
|
+
+ **alias** - create setters and getters with the name of the attribute and _also_ with the alias. See [Aliases](#aliases) for more information.
|
68
72
|
|
69
|
-
|
73
|
+
+ **klass** - pre-format the input with one of the [predefined formatters](#pre-defined-formatters), as a class, or with your [custom formatter](#registering-formatters). See [Formatting Input](#formatting-input) for more information.
|
70
74
|
|
71
|
-
|
72
|
-
|
75
|
+
class Person < Valuable
|
76
|
+
has_value :age, :klass => :integer
|
77
|
+
has_value :phone_number, :klass => PhoneNumber
|
78
|
+
end
|
79
|
+
|
73
80
|
>> Person.new(:age => '15').age.class
|
74
81
|
=> Fixnum
|
75
82
|
|
76
|
-
has_value :phone_number, :klass => PhoneNumber
|
77
83
|
>> jenny = Person.new(:phone_number => '2018675309')
|
78
84
|
|
79
85
|
>> jenny.phone_number == PhoneNumber.new('2018675309')
|
80
86
|
=> true
|
81
87
|
|
82
|
-
see [Formatting Input](#formatting-input) for more information.
|
83
88
|
|
84
|
-
|
85
|
-
has_value :dob, :klass => Date, :parse_with => :parse
|
89
|
+
+ **parse_with** - Sometimes you want to instantiate with a method other than new... one example being Date.parse
|
86
90
|
|
87
|
-
|
91
|
+
class Person
|
92
|
+
has_value :dob, :klass => Date, :parse_with => :parse
|
93
|
+
end
|
94
|
+
|
95
|
+
# this will call Date.parse('1976-07-26')
|
96
|
+
Person.new(:dob => '1976-07-26')
|
88
97
|
|
89
|
-
|
98
|
+
###has_collection( field_name, options = {} )
|
90
99
|
|
91
|
-
|
92
|
-
has_collection :friends
|
93
|
-
end
|
100
|
+
like has_value, this creates a getter and setter. The default value is an array.
|
94
101
|
|
95
|
-
|
96
|
-
|
102
|
+
options:
|
103
|
+
+ **klass** - apply pre-defined or custom formatters to each element of the array.
|
104
|
+
+ **alias** - create additional getters and setters under this name.
|
105
|
+
|
106
|
+
class Person
|
107
|
+
has_collection :friends
|
108
|
+
end
|
97
109
|
|
98
|
-
|
110
|
+
>> Person.new.friends
|
111
|
+
=> []
|
99
112
|
|
100
|
-
|
113
|
+
###attributes
|
114
|
+
|
115
|
+
an array of attributes you have defined on a model.
|
101
116
|
|
102
117
|
class Person < Valuable
|
103
118
|
has_value :first_name
|
@@ -107,9 +122,9 @@ Use these methods to define and inspect provides the following methods:
|
|
107
122
|
>> Person.attributes
|
108
123
|
=> [:first_name, :last_name]
|
109
124
|
|
110
|
-
|
125
|
+
###defaults
|
111
126
|
|
112
|
-
|
127
|
+
A hash of the attributes with their default values. Attributes defined without default values do not appear in this list.
|
113
128
|
|
114
129
|
class Pastry < Valuable
|
115
130
|
has_value :primary_ingredient, :default => :sugar
|
@@ -119,22 +134,20 @@ Use these methods to define and inspect provides the following methods:
|
|
119
134
|
>> Pastry.defaults
|
120
135
|
=> {:primary_ingredient => :sugar}
|
121
136
|
|
122
|
-
|
137
|
+
###register_formatter(name, &block)
|
123
138
|
|
124
|
-
|
125
|
-
you might wish to register an 'orientation' formatter that accepts either angles or 'N', 'S', 'E', 'W', and converts
|
126
|
-
those to angles.
|
139
|
+
Allows you to provide custom code to pre-format attributes, if the included ones are not sufficient. For instance, you might wish to register an 'orientation' formatter that accepts either angles or 'N', 'S', 'E', 'W', and converts those to angles. See [registering formatters](#registering-formatters) for details and examples.
|
127
140
|
|
128
|
-
|
141
|
+
**Note:** as with other formatters, nil values will not be passed to the formatter. The attribute will simply be set to nil. See [nil values](#nil-values). If this is an issue, let me know.
|
129
142
|
|
130
|
-
|
143
|
+
###acts\_as\_permissive
|
131
144
|
|
132
|
-
|
133
|
-
This method makes valuable ignore any unknown attributes.
|
145
|
+
Valuable classes typically raise an error if you instantiate them with attributes that have not been predefined. This method makes valuable ignore any unknown attributes.
|
134
146
|
|
135
|
-
|
147
|
+
Instance-Level Methods
|
148
|
+
----------------------
|
136
149
|
|
137
|
-
|
150
|
+
###attributes
|
138
151
|
|
139
152
|
provides a hash of the attributes and their values.
|
140
153
|
|
@@ -153,10 +166,9 @@ Use these methods to define and inspect provides the following methods:
|
|
153
166
|
# instantiation, or via the setter method party.host=, so
|
154
167
|
# it does not appear in the attributes hash.
|
155
168
|
|
156
|
-
|
169
|
+
###update_attributes(atts={})
|
157
170
|
|
158
|
-
|
159
|
-
Will raise an exception if any of the keys isn't already set up in the class, unless you call acts_as_permissive.
|
171
|
+
Accepts a hash of :attribute => :value and updates each associated attributes. Will raise an exception if any of the keys isn't already set up in the class, unless you call acts_as_permissive.
|
160
172
|
|
161
173
|
class Tomatoe
|
162
174
|
has_value :color
|
@@ -169,11 +181,9 @@ Use these methods to define and inspect provides the following methods:
|
|
169
181
|
>> t.color
|
170
182
|
=> 'red'
|
171
183
|
|
172
|
-
|
184
|
+
###write_attribute(att_name, value)
|
173
185
|
|
174
|
-
|
175
|
-
update_attributes. Using a formatter (if specified), it
|
176
|
-
updates the attributes hash.
|
186
|
+
this method is called by all the setters and, obviously, update_attributes. Using a formatter (if specified), it updates the attributes hash.
|
177
187
|
|
178
188
|
class Chicken
|
179
189
|
has_value :gender
|
@@ -206,7 +216,8 @@ Usage & Examples
|
|
206
216
|
has_value :age, :klass => :integer
|
207
217
|
has_value :phone_number, :klass => PhoneNumber
|
208
218
|
# see /examples/phone_number.rb
|
209
|
-
|
219
|
+
end
|
220
|
+
|
210
221
|
params =
|
211
222
|
{
|
212
223
|
'person' =>
|
@@ -438,7 +449,7 @@ If the default formatters don't suit your needs, Valuable allows you to write yo
|
|
438
449
|
>> Rover.new(:orientation => 'S').orientation
|
439
450
|
=> 180
|
440
451
|
|
441
|
-
More about
|
452
|
+
More about Attributes
|
442
453
|
---------------------
|
443
454
|
|
444
455
|
Access the attributes via the attributes hash. Only default and specified attributes will have entries here.
|
@@ -478,7 +489,18 @@ Get a list of all the defined attributes from the class:
|
|
478
489
|
Advanced Input Parsing
|
479
490
|
----------------------
|
480
491
|
|
481
|
-
|
492
|
+
When you specify a klass, Valuable will pass any input (that isn't already that klass) to the constructor. If you want to use a class-level method other than the constructor, pass the method name to parse\_with. Perhaps it should have been called :construct\_with. :)
|
493
|
+
|
494
|
+
Default behavior:
|
495
|
+
|
496
|
+
class Customer
|
497
|
+
has_value :payment_method, :klass => PaymentMethod
|
498
|
+
end
|
499
|
+
|
500
|
+
# this will call PaymentMethod.new('1232123')
|
501
|
+
Customer.new(:payment_method => '1232123')
|
502
|
+
|
503
|
+
using parse_with:
|
482
504
|
|
483
505
|
require 'date'
|
484
506
|
|
@@ -494,6 +516,19 @@ Sometimes, .to_s isn't enough... the architypical example being Date.parse(value
|
|
494
516
|
>> sammy.age_in_days
|
495
517
|
=> Rational(8, 1)
|
496
518
|
|
519
|
+
example using a lookup method:
|
520
|
+
|
521
|
+
class Person < ActiveRecord::Base
|
522
|
+
def find_by_full_name( full_name )
|
523
|
+
#some finder code
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
class Photograph < Valuable
|
528
|
+
has_value :photographer, :klass => Person
|
529
|
+
end
|
530
|
+
|
531
|
+
|
497
532
|
use it to load associated data from an exising set...
|
498
533
|
|
499
534
|
class Planet < Valuable
|
@@ -521,7 +556,7 @@ use it to load associated data from an exising set...
|
|
521
556
|
>> vger.home.spaceport
|
522
557
|
=> 'KSC'
|
523
558
|
|
524
|
-
|
559
|
+
You can also provide a lambda. This is similar to specifying a custom formatter, except that it only applies to this attribute and can not be re-used.
|
525
560
|
|
526
561
|
require 'active_support'
|
527
562
|
|
data/lib/valuable.rb
CHANGED
@@ -157,10 +157,13 @@ class Valuable
|
|
157
157
|
def has_value(name, options={})
|
158
158
|
Valuable::Utils.check_options_validity(self.class.name, name, options)
|
159
159
|
|
160
|
+
options[:extend] = [options[:extend]].flatten.compact
|
161
|
+
|
160
162
|
name = name.to_sym
|
161
163
|
_attributes[name] = options
|
162
|
-
|
163
|
-
create_accessor_for(name)
|
164
|
+
|
165
|
+
create_accessor_for(name, options[:extend])
|
166
|
+
|
164
167
|
create_question_for(name) if options[:klass] == :boolean
|
165
168
|
create_negative_question_for(name, options[:negative]) if options[:klass] == :boolean && options[:negative]
|
166
169
|
|
@@ -200,12 +203,23 @@ class Valuable
|
|
200
203
|
end
|
201
204
|
end
|
202
205
|
|
203
|
-
# creates
|
204
|
-
|
206
|
+
# creates an accessor method named after the
|
207
|
+
# attribute... can be used as a chained setter,
|
208
|
+
# as in:
|
209
|
+
#
|
210
|
+
# whitehouse.windows(5).doors(4).oval_rooms(1)
|
211
|
+
#
|
212
|
+
# If NOT used as a setter, returns the value,
|
213
|
+
# extended by the modules listed in the second
|
214
|
+
# parameter.
|
215
|
+
def create_accessor_for(name, extensions)
|
205
216
|
define_method name do |*args|
|
206
|
-
|
207
|
-
|
208
|
-
|
217
|
+
if args.length == 0
|
218
|
+
attributes[name].tap do |out|
|
219
|
+
extensions.each do |extension|
|
220
|
+
out.extend( extension )
|
221
|
+
end
|
222
|
+
end
|
209
223
|
else
|
210
224
|
send("#{name}=", *args)
|
211
225
|
self
|
@@ -270,10 +284,11 @@ class Valuable
|
|
270
284
|
options[:item_klass] = options[:klass] if options[:klass]
|
271
285
|
options[:klass] = :collection
|
272
286
|
options[:default] = []
|
287
|
+
options[:extend] = [options[:extend]].flatten.compact
|
273
288
|
|
274
289
|
_attributes[name] = options
|
275
290
|
|
276
|
-
create_accessor_for(name)
|
291
|
+
create_accessor_for(name, options[:extend])
|
277
292
|
create_setter_for(name)
|
278
293
|
|
279
294
|
sudo_alias options[:alias], name if options[:alias]
|
data/lib/valuable/utils.rb
CHANGED
@@ -116,7 +116,7 @@ module Valuable::Utils
|
|
116
116
|
end
|
117
117
|
|
118
118
|
def known_options
|
119
|
-
[:klass, :default, :negative, :alias, :parse_with]
|
119
|
+
[:klass, :default, :negative, :alias, :parse_with, :extend]
|
120
120
|
end
|
121
121
|
|
122
122
|
# this helper raises an exception if the options passed to has_value
|
@@ -0,0 +1,30 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'valuable.rb'
|
5
|
+
|
6
|
+
module BookCollection
|
7
|
+
end
|
8
|
+
|
9
|
+
module PirateFormatter
|
10
|
+
def to_pirate
|
11
|
+
"#{self}, ARRRGGGhhhhh!"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Series < Valuable
|
16
|
+
has_collection :books, :extend => BookCollection
|
17
|
+
has_value :name, :extend => PirateFormatter
|
18
|
+
end
|
19
|
+
|
20
|
+
class ExtendingTest < Test::Unit::TestCase
|
21
|
+
def test_that_collections_are_extended
|
22
|
+
assert Series.new.books.is_a?(BookCollection)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_that_values_are_extended
|
26
|
+
assert_equal 'Walk The Plank, ARRRGGGhhhhh!', Series.new(:name => 'Walk The Plank').name.to_pirate
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
data/valuable.version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.7
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: valuable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-02-15 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Valuable is a ruby base class that is essentially attr_accessor on steroids.
|
15
15
|
A simple and intuitive interface allows you to get on with modeling in your app.
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- test/custom_initializer_test.rb
|
35
35
|
- test/default_values_from_anon_methods.rb
|
36
36
|
- test/deprecated_test.rb
|
37
|
+
- test/extending_test.rb
|
37
38
|
- test/inheritance_test.rb
|
38
39
|
- test/parse_with_test.rb
|
39
40
|
- test/typical_test.rb
|
@@ -63,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
64
|
version: '0'
|
64
65
|
requirements: []
|
65
66
|
rubyforge_project:
|
66
|
-
rubygems_version: 1.8.
|
67
|
+
rubygems_version: 1.8.23
|
67
68
|
signing_key:
|
68
69
|
specification_version: 3
|
69
70
|
summary: attr_accessor on steroids with defaults, attribute formatting, alias methods,
|