valuable 0.6 → 0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +169 -0
- data/lib/valuable.rb +110 -85
- data/rakefile.rb +76 -193
- data/test/bad_attributes_test.rb +23 -23
- data/test/deprecated_test.rb +80 -0
- data/test/inheritance_test.rb +28 -0
- data/test/valuable_test.rb +28 -16
- metadata +10 -18
- data/README.txt +0 -79
- data/lib/boolean.rb +0 -2
data/README.markdown
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
Introducing Valuable
|
2
|
+
====================
|
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.
|
5
|
+
|
6
|
+
Valuable provides DRY decoration like attr_accessor, but includes default values, light weight type casting 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
|
+
Examples
|
9
|
+
-------
|
10
|
+
|
11
|
+
_basic syntax_
|
12
|
+
|
13
|
+
class Fruit
|
14
|
+
has_value :name
|
15
|
+
has_collection :vitamins
|
16
|
+
end
|
17
|
+
|
18
|
+
_constructor accepts an attributes hash_
|
19
|
+
|
20
|
+
>> apple = Fruit.new(:name => 'Apple')
|
21
|
+
|
22
|
+
>> apple.name
|
23
|
+
=> 'Apple'
|
24
|
+
|
25
|
+
>> apple.vitamins
|
26
|
+
=> []
|
27
|
+
|
28
|
+
_default values_
|
29
|
+
|
30
|
+
class Developer
|
31
|
+
has_value :name
|
32
|
+
has_value :nickname, :default => 'mort'
|
33
|
+
end
|
34
|
+
|
35
|
+
>> dev = Developer.new(:name => 'zk')
|
36
|
+
|
37
|
+
>> dev.name
|
38
|
+
=> 'zk'
|
39
|
+
|
40
|
+
>> dev.nickname
|
41
|
+
=> 'mort'
|
42
|
+
|
43
|
+
_setting a value to nil overrides the default._
|
44
|
+
|
45
|
+
>> Developer.new(:name => 'KDD', :nickname => nil).nickname
|
46
|
+
=> nil
|
47
|
+
|
48
|
+
_light weight type casting_
|
49
|
+
|
50
|
+
class BaseballPlayer < Valuable
|
51
|
+
|
52
|
+
has_value :at_bats, :klass => :integer
|
53
|
+
has_value :hits, :klass => :integer
|
54
|
+
|
55
|
+
def average
|
56
|
+
hits/at_bats.to_f if hits && at_bats
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
>> joe = BaseballPlayer.new(:hits => '5', :at_bats => '20', :on_drugs => '0' == '1')
|
61
|
+
|
62
|
+
>> joe.at_bats
|
63
|
+
=> 20
|
64
|
+
|
65
|
+
>> joe.average
|
66
|
+
=> 0.25
|
67
|
+
|
68
|
+
_I find myself using classes to format things... ( PhoneNumber is provided in `/examples` )_
|
69
|
+
|
70
|
+
class School < Valuable
|
71
|
+
has_value :name
|
72
|
+
has_value :phone, :klass => PhoneNumber
|
73
|
+
end
|
74
|
+
|
75
|
+
>> School.new(:name => 'Vanderbilt', :phone => '3332223333').phone
|
76
|
+
=> '(333) 222-3333'
|
77
|
+
|
78
|
+
_as a presenter in Rails_
|
79
|
+
|
80
|
+
class CalenderPresenter < Valuable
|
81
|
+
has_value :month, :klass => Integer, :default => Time.now.month
|
82
|
+
has_value :year, :klass => Integer, :default => Time.now.year
|
83
|
+
|
84
|
+
def start_date
|
85
|
+
Date.civil( year, month, 1)
|
86
|
+
end
|
87
|
+
|
88
|
+
def end_date
|
89
|
+
Date.civil( year, month, -1) #strange I know
|
90
|
+
end
|
91
|
+
|
92
|
+
def events
|
93
|
+
Event.find(:all, :conditions => event_conditions)
|
94
|
+
end
|
95
|
+
|
96
|
+
def event_conditions
|
97
|
+
['starts_at between ? and ?', start_date, end_date]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
_this class might appear in a controller like this:_
|
102
|
+
|
103
|
+
class CalendarController < ApplicationController
|
104
|
+
def show
|
105
|
+
@presenter = CalendarPresenter.new(params[:calendar])
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
_but it's easier to understand like this:_
|
110
|
+
|
111
|
+
>> @presenter = CalendarPresenter.new({}) # first pageload
|
112
|
+
|
113
|
+
>> @presenter.start_date
|
114
|
+
=> Tue, 01 Dec 2009
|
115
|
+
|
116
|
+
>> @presenter.end_date
|
117
|
+
=> Thu, 31 Dec 2009
|
118
|
+
|
119
|
+
>> # User selects some other month and year; the next request looks like...
|
120
|
+
|
121
|
+
>> @presenter = CalendarPresenter.new({:month => '2', :year => '2002'})
|
122
|
+
|
123
|
+
>> @presenter.start_date
|
124
|
+
=> Fri, 01 Feb 2002
|
125
|
+
|
126
|
+
>> @presenter.end_date
|
127
|
+
=> Thu, 28 Feb 2002
|
128
|
+
|
129
|
+
...
|
130
|
+
|
131
|
+
So, if you're reading this, you're probably thinking, "I could have done that!" Yes, it's true. I'll happily agree that it's a relatively simple tool if you'll agree that it lets you model a calendar with an intuitive syntax, prevents you from writing yet another obvious constructor, and allows you to keep your brain focused on your app.
|
132
|
+
|
133
|
+
_you can access the attributes via the attributes hash. Only default and specified attributes will have entries here._
|
134
|
+
|
135
|
+
class Person < Valuable
|
136
|
+
has_value :name
|
137
|
+
has_value :is_developer, :default => false
|
138
|
+
has_value :ssn
|
139
|
+
end
|
140
|
+
|
141
|
+
>> elvis = Person.new(:name => 'The King')
|
142
|
+
|
143
|
+
>> elvis.attributes
|
144
|
+
=> {:name=>"The King", :is_developer=>false}
|
145
|
+
|
146
|
+
>> elvis.attributes[:name]
|
147
|
+
=> "The King"
|
148
|
+
|
149
|
+
>> elvis.ssn
|
150
|
+
=> nil
|
151
|
+
|
152
|
+
_also, you can get a list of all the defined attributes from the class_
|
153
|
+
|
154
|
+
>> Person.attributes
|
155
|
+
=> [:name, :is_developer, :ssn]
|
156
|
+
|
157
|
+
Default Values
|
158
|
+
--------------
|
159
|
+
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.
|
160
|
+
|
161
|
+
When a default value and a klass are specified, the default value will NOT be cast to type klass -- you must do it.
|
162
|
+
|
163
|
+
If a value having a default is set to null after it is constructed, it will NOT be set to the default.
|
164
|
+
|
165
|
+
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.
|
166
|
+
|
167
|
+
KLASS-ification
|
168
|
+
---------------
|
169
|
+
`: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`.
|
data/lib/valuable.rb
CHANGED
@@ -1,40 +1,48 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# This is the base class from which all classes that intend to use
|
4
|
-
# Valuable should inherit.
|
1
|
+
# Valuable is the class from which all classes (who are so inclined)
|
2
|
+
# should inherit.
|
5
3
|
#
|
6
|
-
# Example:
|
4
|
+
# ==Example:
|
7
5
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
6
|
+
# class Bus < Valuable
|
7
|
+
#
|
8
|
+
# has_value :number, :klass => :integer
|
9
|
+
# has_value :color, :default => 'yellow'
|
10
|
+
# has_collection :riders
|
11
|
+
#
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# >> Bus.attributes
|
15
|
+
# => [:number, :color, :riders]
|
16
|
+
# >> bus = Bus.new(:number => '3', :riders => ['GOF', 'Fowler', 'Mort']
|
17
|
+
# >> bus.attributes
|
18
|
+
# => {:number => 3, :riders => ['GOF', 'Fowler', 'Mort'], :color => 'yellow'}
|
13
19
|
class Valuable
|
14
20
|
|
15
|
-
# Returns a Hash
|
16
|
-
# either because they had a default value or because they were set in
|
17
|
-
# the constructor or after instanciation. Values that have not been defined
|
18
|
-
# and which have no default value will not appear in this collection.
|
21
|
+
# Returns a Hash representing all known values. Values are set three ways:
|
19
22
|
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
+
# (1) a default value
|
24
|
+
# (2) they were passed to the constructor
|
25
|
+
# Bus.new(:color => 'green')
|
26
|
+
# (3) they were set via their namesake setter
|
27
|
+
# bus.color = 'green'
|
28
|
+
#
|
29
|
+
# Values that have not been set and have no default not appear in this
|
30
|
+
# collection. Their namesake attribute methods will respond with nil.
|
31
|
+
# Always use symbols to access these values.
|
23
32
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
33
|
+
# >> bus = Bus.new(:number => 16) # color has default value 'yellow'
|
34
|
+
# >> bus.attributes
|
35
|
+
# => {:color => 'yellow', :number => 16}
|
27
36
|
def attributes
|
28
|
-
@attributes ||=
|
37
|
+
@attributes ||= deep_duplicate_of(self.class.defaults)
|
29
38
|
end
|
30
39
|
|
31
|
-
# accepts
|
32
|
-
# for this class.
|
33
|
-
def initialize(atts =
|
34
|
-
atts.each { |name, value| __send__("#{name}=", value ) }
|
40
|
+
# accepts an optional hash that will be used to populate the
|
41
|
+
# predefined attributes for this class.
|
42
|
+
def initialize(atts = nil)
|
43
|
+
atts.each { |name, value| __send__("#{name}=", value ) } if atts
|
35
44
|
end
|
36
45
|
|
37
|
-
# Creates a duplicate of all values.
|
38
46
|
def deep_duplicate_of(value)
|
39
47
|
Marshal.load(Marshal.dump(value))
|
40
48
|
end
|
@@ -49,92 +57,88 @@ class Valuable
|
|
49
57
|
# Returns a name/value set of the values that will be used on
|
50
58
|
# instanciation unless new values are provided.
|
51
59
|
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# >> Bus.defaults
|
57
|
-
# => {:color => 'yellow'}
|
58
|
-
#
|
60
|
+
# >> Bus.defaults
|
61
|
+
# => {:color => 'yellow'}
|
59
62
|
def defaults
|
60
63
|
@defaults ||= {}
|
61
64
|
end
|
62
65
|
|
63
|
-
# Decorator method that
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
66
|
+
# Decorator method that lets you specify the attributes for your
|
67
|
+
# model. It accepts an attribute name (a symbol) and an options
|
68
|
+
# hash. Valid options are :default, :klass and (when :klass is
|
69
|
+
# Boolean) :negative.
|
70
|
+
#
|
71
|
+
# :default - for the given attribute, use this value if no other
|
72
|
+
# is provided.
|
67
73
|
#
|
68
|
-
#
|
69
|
-
#
|
74
|
+
# :klass - light weight type casting. Use :integer, :string or
|
75
|
+
# :boolean. Alternately, supply a class.
|
70
76
|
#
|
71
|
-
# :
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
77
|
+
# When a :klassified attribute is set to some new value, if the value
|
78
|
+
# is not nil and is not already of that class, the value will be cast
|
79
|
+
# to the specified klass. In the case of :integer, it wil be done via
|
80
|
+
# .to_i. In the case of a random other class, it will be done via
|
81
|
+
# Class.new(value). If the value is nil, it will not be cast.
|
75
82
|
#
|
76
|
-
# A
|
83
|
+
# A good example: PhoneNumber < String is useful if you
|
77
84
|
# want numbers to come out the other end properly formatted, when your
|
78
85
|
# input may come in as an integer, or string without formatting, or
|
79
86
|
# string with bad formatting.
|
87
|
+
#
|
88
|
+
# IMPORTANT EXCEPTION
|
89
|
+
#
|
90
|
+
# Due to the way Rails handles checkboxes, '0' resolves to FALSE,
|
91
|
+
# though it would normally resolve to TRUE.
|
80
92
|
def has_value(name, options={})
|
93
|
+
|
94
|
+
name = name.to_sym
|
95
|
+
|
81
96
|
attributes << name
|
82
97
|
|
83
98
|
defaults[name] = options[:default] unless options[:default].nil?
|
84
99
|
|
85
100
|
create_accessor_for(name)
|
86
|
-
create_question_for(name) if options[:klass] ==
|
87
|
-
create_negative_question_for(name, options[:negative]) if options[:klass] ==
|
101
|
+
create_question_for(name) if options[:klass] == :boolean
|
102
|
+
create_negative_question_for(name, options[:negative]) if options[:klass] == :boolean && options[:negative]
|
88
103
|
|
89
104
|
create_setter_for(name, options[:klass], options[:default])
|
90
105
|
|
91
106
|
check_options_validity(name, options)
|
92
107
|
end
|
93
108
|
|
94
|
-
# This method returns an array of symbols, which are the only allowed
|
95
|
-
# options for has_value.
|
96
|
-
def known_options
|
97
|
-
[:klass, :default, :negative]
|
98
|
-
end
|
99
|
-
|
100
|
-
# validates option hashes passed to has_value
|
101
|
-
def check_options_validity(name, options)
|
102
|
-
invalid_options = options.keys - known_options
|
103
|
-
|
104
|
-
raise ArgumentError, "has_value did not know how to respond to #{invalid_options.join(', ')}. Valid (optional) arguments are: #{known_options.join(', ')}" unless invalid_options.empty?
|
105
|
-
end
|
106
|
-
|
107
109
|
# Creates the method that sets the value of an attribute. This setter
|
108
110
|
# is called both by the constructor. The constructor handles type
|
109
111
|
# casting. Setting values via the attributes hash avoids the method
|
110
112
|
# defined here.
|
111
113
|
def create_setter_for(name, klass, default)
|
112
|
-
|
113
|
-
|
114
|
+
|
115
|
+
case klass
|
116
|
+
when NilClass
|
117
|
+
|
114
118
|
define_method "#{name}=" do |value|
|
115
119
|
attributes[name] = value
|
116
120
|
end
|
117
121
|
|
118
|
-
|
122
|
+
when :integer
|
119
123
|
|
120
124
|
define_method "#{name}=" do |value|
|
121
125
|
value_as_integer = value && value.to_i
|
122
126
|
attributes[name] = value_as_integer
|
123
127
|
end
|
124
128
|
|
125
|
-
|
129
|
+
when :string
|
126
130
|
|
127
131
|
define_method "#{name}=" do |value|
|
128
132
|
value_as_string = value && value.to_s
|
129
133
|
attributes[name] = value_as_string
|
130
134
|
end
|
131
135
|
|
132
|
-
|
136
|
+
when :boolean
|
133
137
|
|
134
138
|
define_method "#{name}=" do |value|
|
135
|
-
attributes[name] = !!value
|
139
|
+
attributes[name] = value == '0' ? false : !!value
|
136
140
|
end
|
137
|
-
|
141
|
+
|
138
142
|
else
|
139
143
|
|
140
144
|
define_method "#{name}=" do |value|
|
@@ -160,13 +164,13 @@ class Valuable
|
|
160
164
|
# In addition to the normal getter and setter, boolean attributes
|
161
165
|
# get a method appended with a ?.
|
162
166
|
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
167
|
+
# class Player < Valuable
|
168
|
+
# has_value :free_agent, :klass => Boolean
|
169
|
+
# end
|
166
170
|
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
171
|
+
# juan = Player.new(:free_agent => true)
|
172
|
+
# >> juan.free_agent?
|
173
|
+
# => true
|
170
174
|
def create_question_for(name)
|
171
175
|
define_method "#{name}?" do
|
172
176
|
attributes[name]
|
@@ -175,13 +179,13 @@ class Valuable
|
|
175
179
|
|
176
180
|
# In some situations, the opposite of a value may be just as interesting.
|
177
181
|
#
|
178
|
-
#
|
179
|
-
#
|
180
|
-
#
|
182
|
+
# class Coder < Valuable
|
183
|
+
# has_value :agilist, :klass => Boolean, :negative => :waterfaller
|
184
|
+
# end
|
181
185
|
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
186
|
+
# monkey = Coder.new(:agilist => false)
|
187
|
+
# >> monkey.waterfaller?
|
188
|
+
# => true
|
185
189
|
def create_negative_question_for(name, negative)
|
186
190
|
define_method "#{negative}?" do
|
187
191
|
!attributes[name]
|
@@ -191,18 +195,39 @@ class Valuable
|
|
191
195
|
# this is a more intuitive way of marking an attribute as holding a
|
192
196
|
# collection.
|
193
197
|
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
198
|
+
# class Bus < Valuable
|
199
|
+
# has_value :riders, :default => [] # meh...
|
200
|
+
# has_collection :riders # better!
|
201
|
+
# end
|
197
202
|
#
|
198
|
-
#
|
199
|
-
#
|
200
|
-
#
|
201
|
-
#
|
203
|
+
# >> bus = Bus.new
|
204
|
+
# >> bus.riders << 'jack'
|
205
|
+
# >> bus.riders
|
206
|
+
# => ['jack']
|
202
207
|
def has_collection(name)
|
203
208
|
has_value(name, :default => [] )
|
204
209
|
end
|
205
210
|
|
211
|
+
private
|
212
|
+
|
213
|
+
def inherited(child)
|
214
|
+
attributes.each {|att| child.attributes << att }
|
215
|
+
defaults.each {|(name, value)| child.defaults[name] = value }
|
216
|
+
end
|
217
|
+
|
218
|
+
def known_options
|
219
|
+
[:klass, :default, :negative]
|
220
|
+
end
|
221
|
+
|
222
|
+
# this helper raises an exception if the options passed to has_value
|
223
|
+
# are wrong. Mostly written because I occasionally used :class instead
|
224
|
+
# of :klass and, being a moron, wasted time trying to find the issue.
|
225
|
+
def check_options_validity(name, options)
|
226
|
+
invalid_options = options.keys - known_options
|
227
|
+
|
228
|
+
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?
|
229
|
+
end
|
230
|
+
|
206
231
|
end
|
207
232
|
|
208
233
|
end
|
data/rakefile.rb
CHANGED
@@ -1,193 +1,76 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
|
3
|
-
|
4
|
-
require 'rake
|
5
|
-
require 'rake/
|
6
|
-
require 'rake/
|
7
|
-
require 'rake/
|
8
|
-
require 'rake/
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
t.
|
21
|
-
t.
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
temp_files
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
rdoc.
|
41
|
-
rdoc.options << '--
|
42
|
-
rdoc.
|
43
|
-
rdoc.rdoc_files.include('
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
s.
|
49
|
-
s.
|
50
|
-
s.
|
51
|
-
s.
|
52
|
-
|
53
|
-
|
54
|
-
s.
|
55
|
-
s.
|
56
|
-
s.
|
57
|
-
|
58
|
-
s.
|
59
|
-
|
60
|
-
s.
|
61
|
-
s.
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
task :publish => [:pdoc, :rubyforge_upload]
|
78
|
-
|
79
|
-
desc "Publish the release files to RubyForge."
|
80
|
-
task :rubyforge_upload => :package do
|
81
|
-
files = %w(gem tgz).map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
|
82
|
-
|
83
|
-
if RUBY_FORGE_PROJECT then
|
84
|
-
require 'net/http'
|
85
|
-
require 'open-uri'
|
86
|
-
|
87
|
-
project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
|
88
|
-
project_data = open(project_uri) { |data| data.read }
|
89
|
-
group_id = project_data[/[?&]group_id=(\d+)/, 1]
|
90
|
-
raise "Couldn't get group id" unless group_id
|
91
|
-
|
92
|
-
# This echos password to shell which is a bit sucky
|
93
|
-
if ENV["RUBY_FORGE_PASSWORD"]
|
94
|
-
password = ENV["RUBY_FORGE_PASSWORD"]
|
95
|
-
else
|
96
|
-
print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
|
97
|
-
password = STDIN.gets.chomp
|
98
|
-
end
|
99
|
-
|
100
|
-
login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
|
101
|
-
data = [
|
102
|
-
"login=1",
|
103
|
-
"form_loginname=#{RUBY_FORGE_USER}",
|
104
|
-
"form_pw=#{password}"
|
105
|
-
].join("&")
|
106
|
-
http.post("/account/login.php", data)
|
107
|
-
end
|
108
|
-
|
109
|
-
cookie = login_response["set-cookie"]
|
110
|
-
raise "Login failed" unless cookie
|
111
|
-
headers = { "Cookie" => cookie }
|
112
|
-
|
113
|
-
release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
|
114
|
-
release_data = open(release_uri, headers) { |data| data.read }
|
115
|
-
package_id = release_data[/[?&]package_id=(\d+)/, 1]
|
116
|
-
raise "Couldn't get package id" unless package_id
|
117
|
-
|
118
|
-
first_file = true
|
119
|
-
release_id = ""
|
120
|
-
|
121
|
-
files.each do |filename|
|
122
|
-
basename = File.basename(filename)
|
123
|
-
file_ext = File.extname(filename)
|
124
|
-
file_data = File.open(filename, "rb") { |file| file.read }
|
125
|
-
|
126
|
-
puts "Releasing #{basename}..."
|
127
|
-
|
128
|
-
release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
|
129
|
-
release_date = Time.now.strftime("%Y-%m-%d %H:%M")
|
130
|
-
type_map = {
|
131
|
-
".zip" => "3000",
|
132
|
-
".tgz" => "3110",
|
133
|
-
".gz" => "3110",
|
134
|
-
".gem" => "1400"
|
135
|
-
}; type_map.default = "9999"
|
136
|
-
type = type_map[file_ext]
|
137
|
-
boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
|
138
|
-
|
139
|
-
query_hash = if first_file then
|
140
|
-
{
|
141
|
-
"group_id" => group_id,
|
142
|
-
"package_id" => package_id,
|
143
|
-
"release_name" => PKG_FILE_NAME,
|
144
|
-
"release_date" => release_date,
|
145
|
-
"type_id" => type,
|
146
|
-
"processor_id" => "8000", # Any
|
147
|
-
"release_notes" => "",
|
148
|
-
"release_changes" => "",
|
149
|
-
"preformatted" => "1",
|
150
|
-
"submit" => "1"
|
151
|
-
}
|
152
|
-
else
|
153
|
-
{
|
154
|
-
"group_id" => group_id,
|
155
|
-
"release_id" => release_id,
|
156
|
-
"package_id" => package_id,
|
157
|
-
"step2" => "1",
|
158
|
-
"type_id" => type,
|
159
|
-
"processor_id" => "8000", # Any
|
160
|
-
"submit" => "Add This File"
|
161
|
-
}
|
162
|
-
end
|
163
|
-
|
164
|
-
query = "?" + query_hash.map do |(name, value)|
|
165
|
-
[name, URI.encode(value)].join("=")
|
166
|
-
end.join("&")
|
167
|
-
|
168
|
-
data = [
|
169
|
-
"--" + boundary,
|
170
|
-
"Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
|
171
|
-
"Content-Type: application/octet-stream",
|
172
|
-
"Content-Transfer-Encoding: binary",
|
173
|
-
"", file_data, ""
|
174
|
-
].join("\x0D\x0A")
|
175
|
-
|
176
|
-
release_headers = headers.merge(
|
177
|
-
"Content-Type" => "multipart/form-data; boundary=#{boundary}"
|
178
|
-
)
|
179
|
-
|
180
|
-
target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
|
181
|
-
http.post(target + query, data, release_headers)
|
182
|
-
end
|
183
|
-
|
184
|
-
if first_file then
|
185
|
-
release_id = release_response.body[/release_id=(\d+)/, 1]
|
186
|
-
raise("Couldn't get release id") unless release_id
|
187
|
-
end
|
188
|
-
|
189
|
-
first_file = false
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
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://www.github.com/mustmodify/valuable'
|
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
|
+
|
data/test/bad_attributes_test.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
-
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
-
|
3
|
-
require 'test/unit'
|
4
|
-
require 'valuable.rb'
|
5
|
-
require 'mocha'
|
6
|
-
|
7
|
-
class Infrastructure < Valuable
|
8
|
-
end
|
9
|
-
|
10
|
-
class BadAttributesTest < Test::Unit::TestCase
|
11
|
-
|
12
|
-
def test_that_has_value_grumbles_when_it_gets_bad_attributes
|
13
|
-
assert_raises ArgumentError do
|
14
|
-
Infrastructure.has_value :fu, :invalid => 'shut your mouth'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_that_valid_arguments_cause_no_grumbling
|
19
|
-
assert_nothing_raised do
|
20
|
-
Infrastructure.has_value :bar, :klass => Integer
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'valuable.rb'
|
5
|
+
require 'mocha'
|
6
|
+
|
7
|
+
class Infrastructure < Valuable
|
8
|
+
end
|
9
|
+
|
10
|
+
class BadAttributesTest < Test::Unit::TestCase
|
11
|
+
|
12
|
+
def test_that_has_value_grumbles_when_it_gets_bad_attributes
|
13
|
+
assert_raises ArgumentError do
|
14
|
+
Infrastructure.has_value :fu, :invalid => 'shut your mouth'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_that_valid_arguments_cause_no_grumbling
|
19
|
+
assert_nothing_raised do
|
20
|
+
Infrastructure.has_value :bar, :klass => Integer
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'valuable.rb'
|
5
|
+
require 'mocha'
|
6
|
+
|
7
|
+
class Signature < String
|
8
|
+
end
|
9
|
+
|
10
|
+
class Cube < String
|
11
|
+
def initialize(number)
|
12
|
+
super "Lives in Cube #{number}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class DevCertifications < Valuable
|
17
|
+
has_value :a_plus, :default => false
|
18
|
+
has_value :mcts, :default => false
|
19
|
+
has_value :hash_rocket, :default => false
|
20
|
+
end
|
21
|
+
|
22
|
+
class Dev < Valuable
|
23
|
+
has_value :has_exposure_to_sunlight, :default => false
|
24
|
+
has_value :mindset
|
25
|
+
has_value :name, :default => 'DHH Jr.', :klass => String
|
26
|
+
has_value :signature, :klass => Signature
|
27
|
+
has_value :cubical, :klass => Cube
|
28
|
+
has_value :hacker, :default => true
|
29
|
+
has_value :certifications, :default => DevCertifications.new
|
30
|
+
has_value :quote
|
31
|
+
|
32
|
+
has_collection :favorite_gems
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
# Previously, we used :klass => Klass instead of :klass => :klass.
|
37
|
+
# I decided it was just plain dirty. On refactoring, I realized that
|
38
|
+
# most it would continue to work. Other stuff, unfortunately, would
|
39
|
+
# break horribly. (Integer.new, for instance, makes Ruby very angry.)
|
40
|
+
# The purpose of these tests is to verify that everything _either_
|
41
|
+
# breaks horribly or works, where the third option is fails silently
|
42
|
+
# and mysteriously.
|
43
|
+
class DeprecatedTest < Test::Unit::TestCase
|
44
|
+
|
45
|
+
def test_that_attributes_can_be_klassified
|
46
|
+
dev = Dev.new(:signature => 'brah brah')
|
47
|
+
assert_equal Signature, dev.signature.class
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_that_randomly_classed_attributes_persist_nils
|
51
|
+
assert_equal nil, Dev.new.signature
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_that_randomly_classed_attributes_respect_defaults
|
55
|
+
assert_equal 'DHH Jr.', Dev.new.name
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_that_constructor_casts_attributes
|
59
|
+
assert_equal 'Lives in Cube 20', Dev.new(:cubical => 20).cubical
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_that_setter_casts_attributes
|
63
|
+
golden_boy = Dev.new
|
64
|
+
golden_boy.cubical = 20
|
65
|
+
|
66
|
+
assert_equal 'Lives in Cube 20', golden_boy.cubical
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_that_properly_klassed_values_are_not_rekast
|
70
|
+
why_hammer = Signature.new('go ask your mom')
|
71
|
+
Signature.expects(:new).with(why_hammer).never
|
72
|
+
hammer = Dev.new(:signature => why_hammer)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_that_default_values_can_be_set_to_nothing
|
76
|
+
assert_equal nil, Dev.new(:hacker => nil).hacker
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'valuable.rb'
|
5
|
+
require 'mocha'
|
6
|
+
|
7
|
+
class Parent < Valuable
|
8
|
+
has_value :name, :default => 'unknown'
|
9
|
+
end
|
10
|
+
|
11
|
+
class Child < Parent
|
12
|
+
has_value :age
|
13
|
+
end
|
14
|
+
|
15
|
+
class InheritanceTest < Test::Unit::TestCase
|
16
|
+
|
17
|
+
def test_that_children_inherit_their_parents_attributes
|
18
|
+
assert Child.attributes.include?(:name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_that_children_have_distinctive_attributes
|
22
|
+
assert Child.attributes.include?(:age)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_that_parents_do_not_inherit_things_from_children
|
26
|
+
assert_equal [:name], Parent.attributes
|
27
|
+
end
|
28
|
+
end
|
data/test/valuable_test.rb
CHANGED
@@ -4,9 +4,6 @@ require 'test/unit'
|
|
4
4
|
require 'valuable.rb'
|
5
5
|
require 'mocha'
|
6
6
|
|
7
|
-
class Signature < String
|
8
|
-
end
|
9
|
-
|
10
7
|
class Cubical < String
|
11
8
|
def initialize(number)
|
12
9
|
super "Lives in Cubical #{number}"
|
@@ -20,17 +17,16 @@ class DevCertifications < Valuable
|
|
20
17
|
end
|
21
18
|
|
22
19
|
class Developer < Valuable
|
23
|
-
has_value :experience, :klass =>
|
20
|
+
has_value :experience, :klass => :integer
|
24
21
|
has_value :has_exposure_to_sunlight, :default => false
|
25
22
|
has_value :mindset
|
26
|
-
has_value :name, :default => 'DHH Jr.', :klass =>
|
27
|
-
has_value :
|
28
|
-
has_value :snacks_per_day, :klass => Integer, :default => 7
|
23
|
+
has_value :name, :default => 'DHH Jr.', :klass => :string
|
24
|
+
has_value :snacks_per_day, :klass => :integer, :default => 7
|
29
25
|
has_value :cubical, :klass => Cubical
|
30
26
|
has_value :hacker, :default => true
|
31
|
-
has_value :certifications, :default => DevCertifications.new
|
27
|
+
has_value :certifications, :klass => DevCertifications, :default => DevCertifications.new
|
32
28
|
has_value :quote
|
33
|
-
has_value :employed, :klass =>
|
29
|
+
has_value :employed, :klass => :boolean, :negative => 'unemployed'
|
34
30
|
|
35
31
|
has_collection :favorite_gems
|
36
32
|
|
@@ -71,8 +67,8 @@ class BaseTest < Test::Unit::TestCase
|
|
71
67
|
end
|
72
68
|
|
73
69
|
def test_that_attributes_can_be_klassified
|
74
|
-
dev = Developer.new(:
|
75
|
-
assert_equal
|
70
|
+
dev = Developer.new(:cubical => 12)
|
71
|
+
assert_equal Cubical, dev.cubical.class
|
76
72
|
end
|
77
73
|
|
78
74
|
def test_that_defaults_appear_in_attributes_hash
|
@@ -84,7 +80,7 @@ class BaseTest < Test::Unit::TestCase
|
|
84
80
|
end
|
85
81
|
|
86
82
|
def test_that_randomly_classed_attributes_persist_nils
|
87
|
-
assert_equal nil, Developer.new.
|
83
|
+
assert_equal nil, Developer.new.cubical
|
88
84
|
end
|
89
85
|
|
90
86
|
def test_that_randomly_classed_attributes_respect_defaults
|
@@ -103,7 +99,7 @@ class BaseTest < Test::Unit::TestCase
|
|
103
99
|
end
|
104
100
|
|
105
101
|
def test_that_attributes_are_available_as_class_method
|
106
|
-
assert Developer.attributes.include?(:
|
102
|
+
assert Developer.attributes.include?(:cubical)
|
107
103
|
end
|
108
104
|
|
109
105
|
def test_that_a_model_can_have_a_collection
|
@@ -143,9 +139,9 @@ class BaseTest < Test::Unit::TestCase
|
|
143
139
|
end
|
144
140
|
|
145
141
|
def test_that_properly_klassed_values_are_not_rekast
|
146
|
-
|
147
|
-
|
148
|
-
|
142
|
+
stapler = Cubical.new('in sub-basement')
|
143
|
+
Cubical.expects(:new).with(stapler).never
|
144
|
+
Developer.new(:cubical => stapler)
|
149
145
|
end
|
150
146
|
|
151
147
|
def test_that_values_can_be_set_to_false
|
@@ -168,6 +164,10 @@ class BaseTest < Test::Unit::TestCase
|
|
168
164
|
assert_equal false, Developer.new(:employed => nil).employed
|
169
165
|
end
|
170
166
|
|
167
|
+
def test_that_string_zero_becomes_false
|
168
|
+
assert_equal false, Developer.new(:employed => '0').employed
|
169
|
+
end
|
170
|
+
|
171
171
|
def test_that_boolean_values_get_questionmarked_methods
|
172
172
|
assert Developer.instance_methods.include?('employed?')
|
173
173
|
end
|
@@ -179,4 +179,16 @@ class BaseTest < Test::Unit::TestCase
|
|
179
179
|
def test_that_negative_methods_are_negative
|
180
180
|
assert_equal true, Developer.new(:employed => false).unemployed?
|
181
181
|
end
|
182
|
+
|
183
|
+
def test_that_constructor_can_handle_an_instance_of_nothing
|
184
|
+
assert_nothing_raised do
|
185
|
+
Developer.new(nil)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_that_klassification_does_not_break_when_stringified
|
190
|
+
assert_nothing_raised do
|
191
|
+
Developer.new(:experience => '2')
|
192
|
+
end
|
193
|
+
end
|
182
194
|
end
|
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.
|
4
|
+
version: "0.8"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Johnathon Wright
|
@@ -9,20 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-16 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
17
|
-
type: :runtime
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: "0"
|
24
|
-
version:
|
25
|
-
description: Valuable is a ruby base class that is essentially attr_accessor on steroids. Its aim is to provide Rails-like goodness where ActiveRecord isn't an option. It intends to use a simple and intuitive interface, allowing you to get on with the logic specific to your application.
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
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.
|
26
17
|
email: jw@mustmodify.com
|
27
18
|
executables: []
|
28
19
|
|
@@ -31,12 +22,11 @@ extensions: []
|
|
31
22
|
extra_rdoc_files: []
|
32
23
|
|
33
24
|
files:
|
34
|
-
- lib/boolean.rb
|
35
25
|
- lib/valuable.rb
|
36
|
-
- README.
|
26
|
+
- README.markdown
|
37
27
|
- rakefile.rb
|
38
28
|
has_rdoc: true
|
39
|
-
homepage: http://
|
29
|
+
homepage: http://www.github.com/mustmodify/valuable
|
40
30
|
licenses: []
|
41
31
|
|
42
32
|
post_install_message:
|
@@ -59,10 +49,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
49
|
requirements: []
|
60
50
|
|
61
51
|
rubyforge_project: valuable
|
62
|
-
rubygems_version: 1.3.
|
52
|
+
rubygems_version: 1.3.5
|
63
53
|
signing_key:
|
64
54
|
specification_version: 3
|
65
55
|
summary: attr_accessor on steroids with defaults, constructor, and light casting.
|
66
56
|
test_files:
|
67
57
|
- test/bad_attributes_test.rb
|
58
|
+
- test/deprecated_test.rb
|
59
|
+
- test/inheritance_test.rb
|
68
60
|
- test/valuable_test.rb
|
data/README.txt
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
|
2
|
-
=Introducing Valuable
|
3
|
-
|
4
|
-
Valuable is a ruby base class that is essentially attr_accessor on steroids. Its aim is to provide Rails-like goodness without the database. 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.
|
5
|
-
|
6
|
-
Valuable provides DRY decoration like attr_accessor, but includes default values, light weight type casting, a constructor that accepts an attributes hash, a class-level list of attributes, an instance-level attributes hash, and more.
|
7
|
-
|
8
|
-
==Example
|
9
|
-
|
10
|
-
class BaseballPlayer < Valuable
|
11
|
-
|
12
|
-
has_value :at_bats, :klass => Integer
|
13
|
-
has_value :hits, :klass => Integer
|
14
|
-
has_value :league, :default => 'unknown'
|
15
|
-
has_value :name, :dependency => true
|
16
|
-
has_value :cell, :klass => PhoneNumber, :default => 'Unknown'
|
17
|
-
has_value :active, :klass => Boolean
|
18
|
-
|
19
|
-
has_collection :teammates
|
20
|
-
|
21
|
-
def average
|
22
|
-
hits/at_bats.to_f if hits && at_bats
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
joe = BaseballPlayer.new(:name => 'Joe', :hits => 5, :at_bats => 20, :cell => '1234567890')
|
28
|
-
|
29
|
-
joe.at_bats
|
30
|
-
|
31
|
-
20
|
32
|
-
|
33
|
-
joe.active?
|
34
|
-
|
35
|
-
nil
|
36
|
-
|
37
|
-
joe.league
|
38
|
-
|
39
|
-
'unknown'
|
40
|
-
|
41
|
-
joe.average
|
42
|
-
|
43
|
-
0.25
|
44
|
-
|
45
|
-
joe.at_bats = nil
|
46
|
-
|
47
|
-
joe.average
|
48
|
-
|
49
|
-
nil
|
50
|
-
|
51
|
-
joe.teammates
|
52
|
-
|
53
|
-
[]
|
54
|
-
|
55
|
-
joe.cell
|
56
|
-
|
57
|
-
'(123) 456-7890'
|
58
|
-
|
59
|
-
joe.cell = nil
|
60
|
-
|
61
|
-
joe.cell
|
62
|
-
|
63
|
-
nil
|
64
|
-
|
65
|
-
==DEFAULT VALUES
|
66
|
-
|
67
|
-
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.
|
68
|
-
|
69
|
-
When a default value and a klass are specified, the default value will NOT be cast to type klass -- you must do it. See "jersey" example above.
|
70
|
-
|
71
|
-
If a value having a default is set to null after it is constructed, it will NOT be set to the default.
|
72
|
-
|
73
|
-
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.
|
74
|
-
|
75
|
-
==KLASS-ification
|
76
|
-
|
77
|
-
Boolean is defined as a module in lib for uniformity.
|
78
|
-
|
79
|
-
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.
|
data/lib/boolean.rb
DELETED