valuable 0.6

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.
@@ -0,0 +1,79 @@
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.
@@ -0,0 +1,2 @@
1
+ module Boolean
2
+ end
@@ -0,0 +1,208 @@
1
+ require 'active_support'
2
+ require File.dirname(__FILE__) + '/boolean.rb'
3
+ # This is the base class from which all classes that intend to use
4
+ # Valuable should inherit.
5
+ #
6
+ # Example:
7
+ #
8
+ # class Bus < Valuable
9
+ # has_value :color, :default => 'yellow'
10
+ # has_value :number, :klass => Integer
11
+ # has_collection :riders
12
+ # end
13
+ class Valuable
14
+
15
+ # Returns a Hash with all the name value pairs that have values so far,
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.
19
+ #
20
+ # Currently returns a HashWithIndifferentAccess, though that may change
21
+ # since it would remove the dependancy on ActiveSupport. Always use symbols
22
+ # to access these values.
23
+ #
24
+ # >> bus = Bus.new(:number => 16) # color has default value 'yellow'
25
+ # >> bus.attributes
26
+ # => {:color => 'yellow', :number => 16}
27
+ def attributes
28
+ @attributes ||= HashWithIndifferentAccess.new(deep_duplicate_of(self.class.defaults))
29
+ end
30
+
31
+ # accepts a hash that will be used to populate the predefined attributes
32
+ # for this class.
33
+ def initialize(atts = {})
34
+ atts.each { |name, value| __send__("#{name}=", value ) }
35
+ end
36
+
37
+ # Creates a duplicate of all values.
38
+ def deep_duplicate_of(value)
39
+ Marshal.load(Marshal.dump(value))
40
+ end
41
+
42
+ class << self
43
+
44
+ # Returns an array of the attributes available on this object.
45
+ def attributes
46
+ @attributes ||= []
47
+ end
48
+
49
+ # Returns a name/value set of the values that will be used on
50
+ # instanciation unless new values are provided.
51
+ #
52
+ # class Bus < Valuable
53
+ # has_value :color, :default => 'yellow'
54
+ # end
55
+ #
56
+ # >> Bus.defaults
57
+ # => {:color => 'yellow'}
58
+ #
59
+ def defaults
60
+ @defaults ||= {}
61
+ end
62
+
63
+ # Decorator method that determines which attributes will be available
64
+ # for each instance of the object being created. It accepts an
65
+ # attribute name (a symbol) and an options hash. Valid options are
66
+ # :default, :klass and (when :klass is Boolean) :negative.
67
+ #
68
+ # :default - for the given attribute, use this value if no other is
69
+ # provided.
70
+ #
71
+ # :klass - light weight type casting. Use Integer, String, Boolean, or
72
+ # related classes. Alternately, supply a class. When that attribute is
73
+ # set, if the value isn't already of that class, the result will be
74
+ # an instance of that class, with the value passed to the constructory.
75
+ #
76
+ # A great example: PhoneNumber < String is useful if you
77
+ # want numbers to come out the other end properly formatted, when your
78
+ # input may come in as an integer, or string without formatting, or
79
+ # string with bad formatting.
80
+ def has_value(name, options={})
81
+ attributes << name
82
+
83
+ defaults[name] = options[:default] unless options[:default].nil?
84
+
85
+ create_accessor_for(name)
86
+ create_question_for(name) if options[:klass] == Boolean
87
+ create_negative_question_for(name, options[:negative]) if options[:klass] == Boolean && options[:negative]
88
+
89
+ create_setter_for(name, options[:klass], options[:default])
90
+
91
+ check_options_validity(name, options)
92
+ end
93
+
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
+ # Creates the method that sets the value of an attribute. This setter
108
+ # is called both by the constructor. The constructor handles type
109
+ # casting. Setting values via the attributes hash avoids the method
110
+ # defined here.
111
+ def create_setter_for(name, klass, default)
112
+
113
+ if klass == nil
114
+ define_method "#{name}=" do |value|
115
+ attributes[name] = value
116
+ end
117
+
118
+ elsif klass == Integer
119
+
120
+ define_method "#{name}=" do |value|
121
+ value_as_integer = value && value.to_i
122
+ attributes[name] = value_as_integer
123
+ end
124
+
125
+ elsif klass == String
126
+
127
+ define_method "#{name}=" do |value|
128
+ value_as_string = value && value.to_s
129
+ attributes[name] = value_as_string
130
+ end
131
+
132
+ elsif klass == Boolean
133
+
134
+ define_method "#{name}=" do |value|
135
+ attributes[name] = !!value
136
+ end
137
+
138
+ else
139
+
140
+ define_method "#{name}=" do |value|
141
+ if value.nil?
142
+ attributes[name] = nil
143
+ elsif value.is_a? klass
144
+ attributes[name] = value
145
+ else
146
+ attributes[name] = klass.new(value)
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ # creates a simple accessor method named after the attribute whose
153
+ # value it will provide during the life of the instance.
154
+ def create_accessor_for(name)
155
+ define_method name do
156
+ attributes[name]
157
+ end
158
+ end
159
+
160
+ # In addition to the normal getter and setter, boolean attributes
161
+ # get a method appended with a ?.
162
+ #
163
+ # class Planer < Valuable
164
+ # has_value :free_agent, :klass => Boolean
165
+ # end
166
+ #
167
+ # juan = Bus.new(:free_agent => true)
168
+ # >> juan.free_agent?
169
+ # => true
170
+ def create_question_for(name)
171
+ define_method "#{name}?" do
172
+ attributes[name]
173
+ end
174
+ end
175
+
176
+ # In some situations, the opposite of a value may be just as interesting.
177
+ #
178
+ # class Coder < Valuable
179
+ # has_value :agilist, :klass => Boolean, :negative => :waterfaller
180
+ # end
181
+ #
182
+ # monkey = Coder.new(:agilist => false)
183
+ # >> monkey.waterfaller?
184
+ # => true
185
+ def create_negative_question_for(name, negative)
186
+ define_method "#{negative}?" do
187
+ !attributes[name]
188
+ end
189
+ end
190
+
191
+ # this is a more intuitive way of marking an attribute as holding a
192
+ # collection.
193
+ #
194
+ # class Bus < Valuable
195
+ # has_collection :riders
196
+ # end
197
+ #
198
+ # >> bus = Bus.new
199
+ # >> bus.riders << 'jack'
200
+ # >> bus.riders
201
+ # => ['jack']
202
+ def has_collection(name)
203
+ has_value(name, :default => [] )
204
+ end
205
+
206
+ end
207
+
208
+ end
@@ -0,0 +1,193 @@
1
+ require 'rubygems'
2
+
3
+ require 'rake'
4
+ require 'rake/rdoctask'
5
+ require 'rake/testtask'
6
+ require 'rake/packagetask'
7
+ require 'rake/gempackagetask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+
10
+ task :default => [:test]
11
+
12
+ PKG_NAME = 'valuable'
13
+ PKG_VERSION = '0.6'
14
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
15
+ RUBY_FORGE_PROJECT = 'valuable'
16
+ RUBY_FORGE_USER = 'mustmodify'
17
+
18
+ desc "Run unit tests"
19
+ Rake::TestTask.new("test") { |t|
20
+ t.pattern = 'test/*_test.rb'
21
+ t.verbose = true
22
+ t.warning = true
23
+ }
24
+
25
+ desc 'clean temporary files'
26
+ task :clean do
27
+ temp_filenames = File.join('**', '*.*~')
28
+ temp_files = Dir.glob(temp_filenames)
29
+ if temp_files.empty?
30
+ puts 'nothing to delete'
31
+ else
32
+ puts "deleting #{temp_files.size} files"
33
+ end
34
+
35
+ File.delete(*temp_files)
36
+ end
37
+
38
+ desc 'Generate documentation for the Valuable plugin'
39
+ Rake::RDocTask.new(:rdoc) do |rdoc|
40
+ rdoc.title = 'Valuable - light weight modeling'
41
+ rdoc.options << '--line-numbers'
42
+ rdoc.options << '--inline-source'
43
+ rdoc.rdoc_files.include('README.txt')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
46
+
47
+ spec = Gem::Specification.new do |s|
48
+ s.name = PKG_NAME
49
+ s.version = PKG_VERSION
50
+ s.platform = Gem::Platform::RUBY
51
+ s.summary = 'attr_accessor on steroids with defaults, constructor, and light casting.'
52
+ s.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."
53
+
54
+ s.files = FileList["{lib, test}/**/*"].to_a + %w( README.txt rakefile.rb )
55
+ s.add_dependency 'activesupport'
56
+ s.require_path = 'lib'
57
+ s.test_files = FileList["{test}/**/*test.rb"].to_a
58
+ s.has_rdoc = false
59
+
60
+ s.rubyforge_project = RUBY_FORGE_PROJECT
61
+ s.author = 'Johnathon Wright'
62
+ s.email = 'jw@mustmodify.com'
63
+ s.homepage = 'http://valuable.mustmodify.com'
64
+ end
65
+
66
+ Rake::GemPackageTask.new(spec) do |pkg|
67
+ pkg.need_zip = true
68
+ pkg.need_tar = true
69
+ end
70
+
71
+ desc "Publish the API documentation"
72
+ task :pdoc => [:rdoc] do
73
+ Rake::RubyForgePublisher.new(RUBY_FORGE_PROJECT, RUBY_FORGE_USER).upload
74
+ end
75
+
76
+ desc 'Publish the gem and API docs'
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
+
@@ -0,0 +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
@@ -0,0 +1,182 @@
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 Cubical < String
11
+ def initialize(number)
12
+ super "Lives in Cubical #{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 Developer < Valuable
23
+ has_value :experience, :klass => Integer
24
+ has_value :has_exposure_to_sunlight, :default => false
25
+ has_value :mindset
26
+ has_value :name, :default => 'DHH Jr.', :klass => String
27
+ has_value :signature, :klass => Signature
28
+ has_value :snacks_per_day, :klass => Integer, :default => 7
29
+ has_value :cubical, :klass => Cubical
30
+ has_value :hacker, :default => true
31
+ has_value :certifications, :default => DevCertifications.new
32
+ has_value :quote
33
+ has_value :employed, :klass => Boolean, :negative => 'unemployed'
34
+
35
+ has_collection :favorite_gems
36
+
37
+ end
38
+
39
+ class BaseTest < Test::Unit::TestCase
40
+
41
+ def test_that_an_attributes_hash_is_available
42
+ assert_kind_of(Hash, Developer.new.attributes)
43
+ end
44
+
45
+ def test_that_static_defaults_hash_is_available
46
+ assert_equal 'DHH Jr.', Developer.defaults[:name]
47
+ end
48
+
49
+ def test_that_an_accessor_is_created
50
+ dev = Developer.new(:mindset => :agile)
51
+ assert_equal :agile, dev.mindset
52
+ end
53
+
54
+ def test_that_setter_is_created
55
+ dev = Developer.new
56
+ dev.mindset = :enterprisey
57
+ assert_equal :enterprisey, dev.mindset
58
+ end
59
+
60
+ def test_that_attributes_can_be_cast_as_integer
61
+ dev = Developer.new(:experience => 9.2)
62
+ assert_equal 9, dev.experience
63
+ end
64
+
65
+ def test_that_integer_attributes_respect_default
66
+ assert_equal 7, Developer.new.snacks_per_day
67
+ end
68
+
69
+ def test_that_an_integer_attribute_with_no_value_results_in_nil
70
+ assert_equal nil, Developer.new.experience
71
+ end
72
+
73
+ def test_that_attributes_can_be_klassified
74
+ dev = Developer.new(:signature => 'brah brah')
75
+ assert_equal Signature, dev.signature.class
76
+ end
77
+
78
+ def test_that_defaults_appear_in_attributes_hash
79
+ assert_equal false, Developer.new.attributes[:has_exposure_to_sunlight]
80
+ end
81
+
82
+ def test_that_attributes_can_have_default_values
83
+ assert_equal false, Developer.new.has_exposure_to_sunlight
84
+ end
85
+
86
+ def test_that_randomly_classed_attributes_persist_nils
87
+ assert_equal nil, Developer.new.signature
88
+ end
89
+
90
+ def test_that_randomly_classed_attributes_respect_defaults
91
+ assert_equal 'DHH Jr.', Developer.new.name
92
+ end
93
+
94
+ def test_that_constructor_casts_attributes
95
+ assert_equal 'Lives in Cubical 20', Developer.new(:cubical => 20).cubical
96
+ end
97
+
98
+ def test_that_setter_casts_attributes
99
+ golden_boy = Developer.new
100
+ golden_boy.cubical = 20
101
+
102
+ assert_equal 'Lives in Cubical 20', golden_boy.cubical
103
+ end
104
+
105
+ def test_that_attributes_are_available_as_class_method
106
+ assert Developer.attributes.include?(:signature)
107
+ end
108
+
109
+ def test_that_a_model_can_have_a_collection
110
+ assert_equal [], Developer.new.favorite_gems
111
+ end
112
+
113
+ def test_that_values_do_not_mysteriously_jump_instances
114
+ panda = Developer.new
115
+ panda.mindset = 'geek'
116
+
117
+ hammer = Developer.new
118
+
119
+ assert_not_equal 'geek', hammer.mindset
120
+ end
121
+
122
+ def test_that_collection_values_do_not_roll_across_instances
123
+ jim = Developer.new
124
+ jim.favorite_gems << 'Ruby'
125
+
126
+ clark = Developer.new
127
+
128
+ assert_equal [], clark.favorite_gems
129
+ end
130
+
131
+ def test_that_attributes_are_cast
132
+ panda = Developer.new(:name => 'Code Panda', :experience => '8')
133
+ assert_kind_of Integer, panda.attributes[:experience]
134
+ end
135
+
136
+ def test_that_stringy_keys_are_tried_in_absence_of_symbolic_keys
137
+ homer = Developer.new('quote' => "D'oh!")
138
+ assert_equal "D'oh!", homer.quote
139
+ end
140
+
141
+ def test_that_default_values_from_seperate_instances_are_not_references_to_the_default_value_for_that_field
142
+ assert_not_equal Developer.new.favorite_gems.object_id, Developer.new.favorite_gems.object_id
143
+ end
144
+
145
+ def test_that_properly_klassed_values_are_not_rekast
146
+ why_hammer = Signature.new('go ask your mom')
147
+ Signature.expects(:new).with(why_hammer).never
148
+ hammer = Developer.new(:signature => why_hammer)
149
+ end
150
+
151
+ def test_that_values_can_be_set_to_false
152
+ assert_equal false, Developer.new(:hacker => false).hacker
153
+ end
154
+
155
+ def test_that_default_values_needing_deep_duplication_get_it
156
+ a = Developer.new
157
+ b = Developer.new
158
+
159
+ a.certifications.hash_rocket = true
160
+ assert_equal false, b.certifications.hash_rocket
161
+ end
162
+
163
+ def test_that_default_values_can_be_set_to_nothing
164
+ assert_equal nil, Developer.new(:hacker => nil).hacker
165
+ end
166
+
167
+ def test_that_values_are_cast_to_boolean
168
+ assert_equal false, Developer.new(:employed => nil).employed
169
+ end
170
+
171
+ def test_that_boolean_values_get_questionmarked_methods
172
+ assert Developer.instance_methods.include?('employed?')
173
+ end
174
+
175
+ def test_that_boolean_values_get_negative_methods
176
+ assert Developer.instance_methods.include?('unemployed?')
177
+ end
178
+
179
+ def test_that_negative_methods_are_negative
180
+ assert_equal true, Developer.new(:employed => false).unemployed?
181
+ end
182
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: valuable
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.6"
5
+ platform: ruby
6
+ authors:
7
+ - Johnathon Wright
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-22 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
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.
26
+ email: jw@mustmodify.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/boolean.rb
35
+ - lib/valuable.rb
36
+ - README.txt
37
+ - rakefile.rb
38
+ has_rdoc: true
39
+ homepage: http://valuable.mustmodify.com
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options: []
44
+
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project: valuable
62
+ rubygems_version: 1.3.3
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: attr_accessor on steroids with defaults, constructor, and light casting.
66
+ test_files:
67
+ - test/bad_attributes_test.rb
68
+ - test/valuable_test.rb