valuable 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -27,7 +27,7 @@ Contents
27
27
  - [Collections](#collections)
28
28
  - [Formatting Collections](#formatting-collections)
29
29
  - [Registering Formatters](#registering-formatters)
30
- - [More about Attributesd](#more-about-attributesd)
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-boxe, and text field, and constants to store options. Integrates easily with Rails via @search = CustomerSearch.new(params[:search]) and form_for(@search, :url => ...)
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
- ###Class-Level Methods
54
+ Class-Level Methods
55
+ -------------------
55
56
 
56
- Use these methods to define and inspect provides the following methods:
57
+ ###has_value( field_name, options = {})
57
58
 
58
- **has_value( field_name, attributes = {})**
59
+ creates a getter and setter named field_name
59
60
 
60
- creates a getter and setter named field_name
61
- options:
62
- :default - provide a default value
63
- has_value :status, :default => 'Active'
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
- :alias - create setters and getters with the name of the attribute and _also_ with the alias.
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
- see [Aliases](#aliases) for more information.
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
- :klass - pre-format the input with one of the [predefined formatters](#pre-defined-formatters), as a class, or with your custom formatter.
72
- has_value :age, :klass => :integer
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
- :parse_with - Sometimes you want to instanciate with a method other than new... one example being Date.parse
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
- **has_collection( field_name, attributes = {} )**
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
- like has_value, this creates a getter and setter. The default value is an array.
98
+ ###has_collection( field_name, options = {} )
90
99
 
91
- class Person
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
- >> Person.new.friends
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
- **attributes**
110
+ >> Person.new.friends
111
+ => []
99
112
 
100
- an array of attributes you have defined on a model.
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
- **defaults**
125
+ ###defaults
111
126
 
112
- A hash of the attributes with their default values. Attributes defined without default values do not appear in this list.
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
- **register_formatter(name, &block)**
137
+ ###register_formatter(name, &block)
123
138
 
124
- allows you to provide custom code to pre-format attributes, if the included ones are not sufficient. For instance,
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
- NOTE: as with other formatters, nil values will not be passed to the formatter. The attribute will simply be set to nil. If this is an issue, let me know.
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
- **acts_as_permissive**
143
+ ###acts\_as\_permissive
131
144
 
132
- Valuable classes typically raise an error if you instantiate them with attributes that have not been predefined.
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
- ###Instance-Level Methods
147
+ Instance-Level Methods
148
+ ----------------------
136
149
 
137
- **attributes**
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
- **update_attributes(atts={})**
169
+ ###update_attributes(atts={})
157
170
 
158
- Accepts a hash of :attribute => :value and updates each associated attributes.
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
- **write_attribute(att_name, value)**
184
+ ###write_attribute(att_name, value)
173
185
 
174
- this method is called by all the setters and, obviously,
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 Attributesd
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
- Sometimes, .to_s isn't enough... the architypical example being Date.parse(value). In these cases, you can specify what class-level method should be used to process the input.
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
- Parse via lambda:
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
 
@@ -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 a simple accessor method named after the attribute
204
- def create_accessor_for(name)
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
- if args.length == 0
208
- attributes[name]
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]
@@ -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
+
@@ -8,7 +8,7 @@ class Beer < Valuable
8
8
  has_value :brewery
9
9
  end
10
10
 
11
- class AliasTest < Test::Unit::TestCase
11
+ class WriteAndReadAttributeTest < Test::Unit::TestCase
12
12
 
13
13
  def test_that_values_can_be_set_using_write_attribute
14
14
  beer = Beer.new
@@ -1 +1 @@
1
- 0.9.6
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.6
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: 2012-08-10 00:00:00.000000000 Z
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.11
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,