valuable 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -1,31 +1,65 @@
1
1
  Introducing Valuable
2
2
  ====================
3
3
 
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.
4
+ Valuable enables quick modeling... it's attr_accessor on steroids. For all those times you wanted to use OO to model something but it seemed like too much of a pain, try Valuable. Its simple interface allows you to model without hassles, so you can get on with the logic specific to your application.
5
5
 
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.
6
+ Frequent Uses:
7
+
8
+ **pre-refactor modeling** to model a class you want to abstract but know would be a pain... as in, "I would love to pull Appointment out of this WorkOrder class, but since that isn't going to happen soon, let me quickly create WorkOrder.appointments... I can then create Appointment\#to\_s, appointment.end_time, appointment.duration, etc. I can use that to facilitate emitting XML or doing something with views, rather than polluting WorkOrder with appointment-related logic."
9
+
10
+ **as a presenter** as in, "I need to take in a few different models to generate this map... I need a class that models the integration, but I don't need to persist that to a database."
11
+
12
+ **creating models from non-standard data sources** to keep data from non-standard data sources in memory during an import or to render data from an API call.
13
+
14
+ Valuable provides DRY decoration like attr_accessor, but includes default values and other formatting (like, "2" => 2), and a constructor that accepts an attributes hash. It provides a class-level list of attributes, an instance-level attributes hash, and more.
7
15
 
8
16
  Type Casting in Ruby? You must be crazy...
9
17
  -------------------------------------------------------------
10
18
  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
19
+ C# over here. Rails does it, they just don't call it type casting,
20
+ so no one complains when they pass in "2" as a parameter and mysteriously
21
+ it ends up as an integer. In fact, I'm going to start using the euphamism
12
22
  'Formatting' just so people will stop looking at me that way.
13
23
 
14
- Say you have get some data from a web service via JSON.
15
- Parse the json and you get this:
24
+ Say you're getting information for a directory from a web service via JSON:
25
+
26
+ class Person < Valuable
27
+ has_value :name
28
+ has_value :age, :klass => :integer
29
+ has_value :phone_number, :klass => PhoneNumber
30
+ # see /examples/phone_number.rb
31
+
32
+ 'person' =>
33
+ 'name' => 'Mr. Freud',
34
+ 'age' => "344",
35
+ 'phone_number' => '8002195642',
36
+ 'specialization_code' => "2106"
37
+
38
+ you'll end up with this:
39
+
40
+ >> p = Person.new(params[:person])
41
+
42
+ >> p.age
43
+ => 344
44
+
45
+ >> p.phone_number
46
+ => (337) 326-3121
47
+
48
+ >> p.phone_number.class
49
+ => PhoneNumber
50
+
51
+ "Yeah, I could have just done that myself."
52
+ "Right, but now you don't have to."
16
53
 
17
- 'person' =>
18
- 'name' => 'Mr. Freud',
19
- 'phone_number' => '8002195642',
20
- 'specialization_code' => 2106
54
+ Default Values
55
+ --------------
56
+ Default values are used when no value is provided to the constructor. If the value nil is provided, nil will be used instead of the default.
21
57
 
22
- then parse it:
58
+ When a default value and a klass are specified, the default value will NOT be cast to type klass -- you must do it.
23
59
 
24
- class Person < Valuable
25
- has_value :name, :default => 'Unknown'
26
- has_value :phone_number
60
+ If a value having a default is set to null after it is constructed, it will NOT be set to the default.
27
61
 
28
-
62
+ If there is no default value, the result will be nil, EVEN if type casting is provided. Thus, a field typically cast as an Integer can be nil. See calculation of average.
29
63
 
30
64
  Examples
31
65
  -------
@@ -67,7 +101,7 @@ _setting a value to nil overrides the default._
67
101
  >> Developer.new(:name => 'KDD', :nickname => nil).nickname
68
102
  => nil
69
103
 
70
- _light weight type casting_
104
+ _formatting aka light-weight type-casting_
71
105
 
72
106
  class BaseballPlayer < Valuable
73
107
 
@@ -87,6 +121,59 @@ _light weight type casting_
87
121
  >> joe.average
88
122
  => 0.25
89
123
 
124
+ # Currently supports:
125
+ # - integer
126
+ # - decimal ( casts to BigDecimal... NOTE: nil remains nil, not 0 as in nil.to_i )
127
+ # - string
128
+ # - boolean ( NOTE: '0' casts to FALSE... I would be fascinated to know when this is not the correct behavior. )
129
+ # - or any class ( formats as SomeClass.new( ) unless value.is_a?( SomeClass ) )
130
+
131
+
132
+ _collections_
133
+
134
+ class MailingList < Valuable
135
+ has_collection :emails
136
+ end
137
+
138
+ >> m = MailingList.new
139
+
140
+ >> m.emails
141
+ => []
142
+
143
+ >> m = MailingList.new(:emails => [ 'johnathon.e.wright@nasa.gov', 'other.people@wherever.com' ])
144
+
145
+ => m.emails
146
+ >> [ 'johnathon.e.wright@nasa.gov', 'other.people@wherever.com' ]
147
+
148
+ _formatting collections_
149
+
150
+ class Player < Valuable
151
+ has_value :first_name
152
+ has_value :last_name
153
+ has_value :salary
154
+ end
155
+
156
+ class Team < Valuable
157
+ has_value :name
158
+ has_value :long_name
159
+
160
+ has_collection :players, :klass => Player
161
+ end
162
+
163
+ t = Team.new(:name => 'Toronto', :long_name => 'The Toronto Blue Jays',
164
+ 'players' => [
165
+ {'first_name' => 'Chad', 'last_name' => 'Beck', :salary => 'n/a'},
166
+ {'first_name' => 'Shawn', 'last_name' => 'Camp', :salary => '2250000'},
167
+ {'first_name' => 'Brett', 'last_name' => 'Cecil', :salary => '443100'},
168
+ Player.new(:first_name => 'Travis', :last_name => 'Snider', :salary => '435800')
169
+ ])
170
+
171
+ >> t.players.first
172
+ => #<Player:0x7fa51e4a1da0 @attributes={:salary=>"n/a", :first_name=>"Chad", :last_name=>"Beck"}>
173
+
174
+ >> t.players.last
175
+ => #<Player:0x7fa51ea6a9f8 @attributes={:salary=>"435800", :first_name=>"Travis", :last_name=>"Snider"}>
176
+
90
177
  _aliases_
91
178
 
92
179
  # This example requires active_support because of Hash.from_xml
@@ -97,22 +184,12 @@ _aliases_
97
184
 
98
185
  >> xml = '<software><Title>Windows XP</Title></software>'
99
186
 
100
- >> xp = Software.new(:Title => Hash.from_xml(xml)['software'])
187
+ >> xp = Software.new(Hash.from_xml(xml)['software'])
101
188
 
102
189
  >> xp.name
103
190
  => "Windows XP"
104
191
 
105
192
 
106
- _I find myself using classes to format things... ( PhoneNumber is provided in `/examples` )_
107
-
108
- class School < Valuable
109
- has_value :name
110
- has_value :phone, :klass => PhoneNumber
111
- end
112
-
113
- >> School.new(:name => 'Vanderbilt', :phone => '3332223333').phone
114
- => '(333) 222-3333'
115
-
116
193
  _as a presenter in Rails_
117
194
 
118
195
  class CalenderPresenter < Valuable
@@ -130,13 +207,13 @@ _as a presenter in Rails_
130
207
  def events
131
208
  Event.find(:all, :conditions => event_conditions)
132
209
  end
133
-
210
+
134
211
  def event_conditions
135
212
  ['starts_at between ? and ?', start_date, end_date]
136
213
  end
137
214
  end
138
215
 
139
- _this class might appear in a controller like this:_
216
+ this class might appear in a controller like this:
140
217
 
141
218
  class CalendarController < ApplicationController
142
219
  def show
@@ -144,7 +221,7 @@ _this class might appear in a controller like this:_
144
221
  end
145
222
  end
146
223
 
147
- _but it's easier to understand like this:_
224
+ but it's easier to understand like this:
148
225
 
149
226
  >> @presenter = CalendarPresenter.new({}) # first pageload
150
227
 
@@ -155,7 +232,7 @@ _but it's easier to understand like this:_
155
232
  => Thu, 31 Dec 2009
156
233
 
157
234
  >> # User selects some other month and year; the next request looks like...
158
-
235
+
159
236
  >> @presenter = CalendarPresenter.new({:month => '2', :year => '2002'})
160
237
 
161
238
  >> @presenter.start_date
@@ -179,7 +256,7 @@ _you can access the attributes via the attributes hash. Only default and specifi
179
256
  >> elvis = Person.new(:name => 'The King')
180
257
 
181
258
  >> elvis.attributes
182
- => {:name=>"The King", :is_developer=>false}
259
+ => {:name=>"The King", :is_developer=>false}
183
260
 
184
261
  >> elvis.attributes[:name]
185
262
  => "The King"
@@ -188,20 +265,8 @@ _you can access the attributes via the attributes hash. Only default and specifi
188
265
  => nil
189
266
 
190
267
  _also, you can get a list of all the defined attributes from the class_
191
-
268
+
192
269
  >> Person.attributes
193
270
  => [:name, :is_developer, :ssn]
194
271
 
195
- Default Values
196
- --------------
197
- Default values are used when no value is provided to the constructor. If the value nil is provided, nil will be used instead of the default.
198
-
199
- When a default value and a klass are specified, the default value will NOT be cast to type klass -- you must do it.
200
-
201
- If a value having a default is set to null after it is constructed, it will NOT be set to the default.
202
-
203
- If there is no default value, the result will be nil, EVEN if type casting is provided. Thus, a field typically cast as an Integer can be nil. See calculation of average.
204
272
 
205
- KLASS-ification
206
- ---------------
207
- `:integer`, `:string` and `:boolean` use `to_i`, `to_s` and `!!` respectively. All other klasses use `klass.new(value)` unless the value `is_a?(klass)`, in which case it is unmolested. Nils are never klassified. In the example above, hits, which is an integer, is `nil` if not set, rather than `nil.to_i = 0`.
@@ -1,3 +1,5 @@
1
+ # Trying to extract as much logic as possible to minimize the memory
2
+ # footprint of individual instances. Feedback welcome.
1
3
  require 'bigdecimal'
2
4
 
3
5
  module Valuable::Utils
@@ -17,7 +19,7 @@ module Valuable::Utils
17
19
  Marshal.load(Marshal.dump(value))
18
20
  end
19
21
 
20
- def cast( name, value, attributes, collection_item = false )
22
+ def format( name, value, attributes, collection_item = false )
21
23
  klass = collection_item ? attributes[name][:item_klass] : attributes[name][:klass]
22
24
 
23
25
  case klass
@@ -28,7 +30,7 @@ module Valuable::Utils
28
30
  when :collection
29
31
  if( value.kind_of?(Array) )
30
32
  out = value.map do |item|
31
- Valuable::Utils.cast( name, item, attributes, true )
33
+ Valuable::Utils.format( name, item, attributes, true )
32
34
  end
33
35
  end
34
36
 
data/lib/valuable.rb CHANGED
@@ -87,7 +87,7 @@ class Valuable
87
87
  attribute = Valuable::Utils.find_attribute_for( name, self.class._attributes )
88
88
 
89
89
  if attribute
90
- self.attributes[attribute] = Valuable::Utils.cast(attribute, value, self.class._attributes)
90
+ self.attributes[attribute] = Valuable::Utils.format(attribute, value, self.class._attributes)
91
91
  else
92
92
  raise( ArgumentError, "#{self.class.to_s} does not have an attribute or alias '#{name}'", caller) unless self.permissive?
93
93
  end
data/test/typical_test.rb CHANGED
@@ -3,7 +3,7 @@ $: << File.expand_path(File.dirname(__FILE__) + '/../lib')
3
3
  require 'test/unit'
4
4
  require 'valuable.rb'
5
5
  require 'date'
6
- require File.dirname(__FILE__) + '/../examples/phone_number'
6
+ require File.expand_path(File.dirname(__FILE__) + '/../examples/phone_number')
7
7
  class Person < Valuable
8
8
  has_value :dob, :klass => :date
9
9
  end
@@ -169,11 +169,11 @@ class BaseTest < Test::Unit::TestCase
169
169
  end
170
170
 
171
171
  def test_that_boolean_values_get_questionmarked_methods
172
- assert Developer.instance_methods.include?('employed?')
172
+ assert Developer.instance_methods.map(&:to_sym).include?(:employed?)
173
173
  end
174
174
 
175
175
  def test_that_boolean_values_get_negative_methods
176
- assert Developer.instance_methods.include?('unemployed?')
176
+ assert Developer.instance_methods.map(&:to_sym).include?(:unemployed?)
177
177
  end
178
178
 
179
179
  def test_that_negative_methods_are_negative
data/todo.txt CHANGED
@@ -1,10 +1,3 @@
1
- Find some way to do
2
-
3
- has_value :something, :klass => Decimal
4
-
5
- * must handle BigDecimal not being around
6
-
7
-
8
1
  - has_value :start_date, :default => {Date.today}
9
2
  - has_value :price, :default => 50, :extend => MoneyFormatter
10
3
 
@@ -19,6 +12,17 @@ class SomeSearchThing < Valuable
19
12
 
20
13
  end
21
14
 
15
+ class Datum < Valuable
16
+ has_index :by_name
17
+ has_list :calculations
18
+ end
19
+ -or-
20
+ class Datum < Valuable
21
+ lists :calculations
22
+ indexes :by_name
23
+ end
24
+
25
+
22
26
  BEST PRACTICES DOC
23
27
 
24
28
  When creating a searcher:
@@ -74,18 +78,6 @@ like this:
74
78
  end
75
79
 
76
80
 
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
81
+ what about has_value :number, :construct => 'Decimal#parse'
90
82
 
91
83
  - do a better job with method_missing
data/valuable.version CHANGED
@@ -1 +1 @@
1
- 0.9.1
1
+ 0.9.2
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: 57
4
+ hash: 63
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 1
10
- version: 0.9.1
9
+ - 2
10
+ version: 0.9.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Johnathon Wright
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-18 00:00:00 -05:00
18
+ date: 2011-09-27 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies: []
21
21