hattr_accessor 1.1.0
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.
- data/CHANGELOG +31 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +115 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/hattr_accessor.gemspec +51 -0
- data/lib/hattr_accessor.rb +115 -0
- data/test/active_record_test.rb +98 -0
- data/test/fixtures/manufacturers.yml +11 -0
- data/test/fixtures/products.yml +11 -0
- data/test/hattr_accessor_test.rb +286 -0
- metadata +66 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
2009-03-05 - Sean Huber (shuber@huberry.com)
|
2
|
+
* Merge pixeltrix/master
|
3
|
+
* Include Huberry::HattrAccessor in Object instead of Class
|
4
|
+
* Update README
|
5
|
+
* Update gemspec
|
6
|
+
|
7
|
+
2009-01-18 - Sean Huber (shuber@huberry.com)
|
8
|
+
* Boolean type casted empty strings should return false
|
9
|
+
|
10
|
+
2009-01-12 - Sean Huber (shuber@huberry.com)
|
11
|
+
* Check for existing reader and writer methods separately before creating default ones for options[:attribute]
|
12
|
+
* Remove alias_method_chain - unnecessary
|
13
|
+
|
14
|
+
2009-01-10 - Sean Huber (shuber@huberry.com)
|
15
|
+
* Object includes Huberry::AliasMethodChain instead of Module
|
16
|
+
* Remove init.rb
|
17
|
+
|
18
|
+
2009-01-08 - Sean Huber (shuber@huberry.com)
|
19
|
+
* Update CHANGELOG email addresses
|
20
|
+
* Remove .gitignore
|
21
|
+
* Update README
|
22
|
+
* Move include logic into the main hattr_accessor file
|
23
|
+
|
24
|
+
2008-10-17 - Sean Huber (shuber@huberry.com)
|
25
|
+
* Refactor #{attribute}_with_hattr_accessor method
|
26
|
+
* Rename doc/README.markdown to doc/README
|
27
|
+
|
28
|
+
2008-10-14 - Sean Huber (shuber@huberry.com)
|
29
|
+
* Initial import
|
30
|
+
* Update README
|
31
|
+
* Add tests to ensure errors are raised when :attribute does not exist
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Sean Huber (shuber@huberry.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
hattr\_accessor
|
2
|
+
===============
|
3
|
+
|
4
|
+
Allows you to define attr\_accessors that reference members of a hash
|
5
|
+
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
gem install shuber-hattr_accessor --source http://gems.github.com
|
11
|
+
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
The hattr\_accessor method requires an option named `:attribute` which should be name of an attribute which will store the hash. For example:
|
17
|
+
|
18
|
+
class DataSource
|
19
|
+
hattr_accessor :adapter, :username, :password, :attribute => :credentials
|
20
|
+
end
|
21
|
+
|
22
|
+
The reader and writer methods for `:attribute` (`:credentials` in the example above) would be automatically created unless they exist already.
|
23
|
+
You can then use those attributes like normal ones:
|
24
|
+
|
25
|
+
@data_source = DataSource.new
|
26
|
+
@data_source.adapter = 'mysql'
|
27
|
+
@data_source.username = 'root'
|
28
|
+
@data_source.credentials # { :adapter => 'mysql', :username => 'root' }
|
29
|
+
|
30
|
+
@data_source.credentials = {}
|
31
|
+
@data_source.adapter # nil
|
32
|
+
|
33
|
+
The reader method for `:attribute` is overwritten with logic to ensure that it returns a hash by default.
|
34
|
+
|
35
|
+
@data_source = DataSource.new
|
36
|
+
@data_source.credentials # {}
|
37
|
+
|
38
|
+
You may optionally pass a `:type` option which will type cast the values when calling their getter methods. This is useful if you're using this
|
39
|
+
gem with rails which will pass values as strings if submitted from a form. For example:
|
40
|
+
|
41
|
+
class CustomField::Date < CustomField
|
42
|
+
hattr_accessor :offset, :type => :integer, :attribute => :configuration
|
43
|
+
hattr_accessor :unit, :reference, :type => :string, :attribute => :configuration
|
44
|
+
|
45
|
+
def default_value
|
46
|
+
self.offset.send(self.unit.to_sym).send(self.reference.to_sym)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@custom_field = CustomField::Date.new(:offset => '5', :unit => 'days', :reference => 'from_now')
|
51
|
+
@custom_field.offset # 5 (notice it's an integer, not a string)
|
52
|
+
@custom_field.default_value # evaluates 5.days.from_now
|
53
|
+
|
54
|
+
The current options (email me for suggestions for others) for `:type` are:
|
55
|
+
|
56
|
+
:string
|
57
|
+
:integer
|
58
|
+
:float
|
59
|
+
:boolean
|
60
|
+
:decimal
|
61
|
+
:array
|
62
|
+
|
63
|
+
To specify a default value for a member use the `:default` option. For example:
|
64
|
+
|
65
|
+
class DataSource
|
66
|
+
hattr_accessor :adapter, :default => 'mysql', :attribute => :credentials
|
67
|
+
hattr_accessor :username, :default => 'root', :attribute => :credentials
|
68
|
+
hattr_accessor :password, :attribute => :credentials
|
69
|
+
end
|
70
|
+
|
71
|
+
@data_source = DataSource.new
|
72
|
+
@data_source.adapter # 'mysql'
|
73
|
+
|
74
|
+
You can also specify a proc for the default value. For example:
|
75
|
+
|
76
|
+
class DataSource
|
77
|
+
hattr_accessor :adapter, :default => 'mysql', :attribute => :credentials
|
78
|
+
hattr_accessor :username, :attribute => :credentials,
|
79
|
+
:default => lambda { |datasource| Etc.getpwuid(Process.uid).name }
|
80
|
+
hattr_accessor :password, :attribute => :credentials
|
81
|
+
end
|
82
|
+
|
83
|
+
@data_source = DataSource.new
|
84
|
+
@data_source.username # 'process_username'
|
85
|
+
|
86
|
+
If you want to take advantage of type casting but also want to return `nil` if a value has not been set then use the `:allow_nil` option.
|
87
|
+
By default `:allow_nil` is false for typed members but true for non-typed members. For example:
|
88
|
+
|
89
|
+
class DataSource
|
90
|
+
hattr_accessor :adapter, :type => :string, :allow_nil => true, :attribute => :credentials
|
91
|
+
hattr_accessor :username, :type => :string, :attribute => :credentials
|
92
|
+
hattr_accessor :password, :attribute => :credentials
|
93
|
+
end
|
94
|
+
|
95
|
+
@data_source = DataSource.new
|
96
|
+
@data_source.adapter # nil
|
97
|
+
@data_source.username # ''
|
98
|
+
@data_source.password # nil
|
99
|
+
|
100
|
+
NOTE: Make sure your call `define_attribute_methods` before calling `hattr_accessor` when you're using ActiveRecord and your `:attribute` is a
|
101
|
+
database field. The call to `define_attribute_methods` must be after the `serialize` call so that `define_attribute_methods` knows about the
|
102
|
+
serialized field.
|
103
|
+
|
104
|
+
class CustomField < ActiveRecord::Base
|
105
|
+
serialize :configuration, Hash
|
106
|
+
define_attribute_methods
|
107
|
+
|
108
|
+
hattr_accessor :testing, :attribute => :configuration
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
Contact
|
113
|
+
-------
|
114
|
+
|
115
|
+
Problems, comments, and suggestions all welcome: [shuber@huberry.com](mailto:shuber@huberry.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = 'hattr_accessor'
|
8
|
+
gem.summary = 'Allows you to define attr_accessors that reference members of a hash'
|
9
|
+
gem.description = 'Allows you to define attr_accessors that reference members of a hash'
|
10
|
+
gem.email = 'shuber@huberry.com'
|
11
|
+
gem.homepage = 'http://github.com/shuber/hattr_accessor'
|
12
|
+
gem.authors = ['Sean Huber']
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts 'Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler'
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort 'RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
task :test => :check_dependencies
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
if File.exist?('VERSION')
|
46
|
+
version = File.read('VERSION')
|
47
|
+
else
|
48
|
+
version = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "hattr_accessor #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.1.0
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{hattr_accessor}
|
8
|
+
s.version = "1.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Sean Huber"]
|
12
|
+
s.date = %q{2010-01-06}
|
13
|
+
s.description = %q{Allows you to define attr_accessors that reference members of a hash}
|
14
|
+
s.email = %q{shuber@huberry.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.markdown"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"CHANGELOG",
|
20
|
+
"MIT-LICENSE",
|
21
|
+
"README.markdown",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"hattr_accessor.gemspec",
|
25
|
+
"lib/hattr_accessor.rb",
|
26
|
+
"test/active_record_test.rb",
|
27
|
+
"test/fixtures/manufacturers.yml",
|
28
|
+
"test/fixtures/products.yml",
|
29
|
+
"test/hattr_accessor_test.rb"
|
30
|
+
]
|
31
|
+
s.homepage = %q{http://github.com/shuber/hattr_accessor}
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubygems_version = %q{1.3.5}
|
35
|
+
s.summary = %q{Allows you to define attr_accessors that reference members of a hash}
|
36
|
+
s.test_files = [
|
37
|
+
"test/active_record_test.rb",
|
38
|
+
"test/hattr_accessor_test.rb"
|
39
|
+
]
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
|
+
s.specification_version = 3
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
46
|
+
else
|
47
|
+
end
|
48
|
+
else
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
module Huberry
|
4
|
+
module HattrAccessor
|
5
|
+
class MissingAttributeError < StandardError; self; end
|
6
|
+
|
7
|
+
def hattr_defined?(attribute)
|
8
|
+
hattr_attributes.include?(attribute.to_sym)
|
9
|
+
end
|
10
|
+
|
11
|
+
def hattr_attributes
|
12
|
+
@hattr_attributes ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def hattr_accessor(*attrs)
|
16
|
+
options = attrs.last.is_a?(Hash) ? attrs.pop : {}
|
17
|
+
|
18
|
+
raise MissingAttributeError, 'Must specify the :attribute option with the name of an attribute which will store the hash' if options[:attribute].nil?
|
19
|
+
|
20
|
+
attrs.each do |name|
|
21
|
+
hattr_attributes << name
|
22
|
+
|
23
|
+
# Defines a type casting getter method for each attribute
|
24
|
+
#
|
25
|
+
define_method name do
|
26
|
+
value = send("#{name}_before_type_cast".to_sym)
|
27
|
+
return value if options[:allow_nil] && value.nil?
|
28
|
+
case options[:type]
|
29
|
+
when :string
|
30
|
+
value.to_s
|
31
|
+
when :symbol
|
32
|
+
value.to_s.to_sym
|
33
|
+
when :integer
|
34
|
+
value.to_i
|
35
|
+
when :float
|
36
|
+
value.to_f
|
37
|
+
when :boolean
|
38
|
+
![false, nil, 0, '0', ''].include?(value)
|
39
|
+
when :decimal
|
40
|
+
BigDecimal.new(value.to_s)
|
41
|
+
when :array
|
42
|
+
Array(value)
|
43
|
+
else
|
44
|
+
value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Defines a predicate method for each attribute
|
49
|
+
#
|
50
|
+
define_method "#{name}?" do
|
51
|
+
send(name) ? true : false
|
52
|
+
end
|
53
|
+
|
54
|
+
# Defines a setter method for each attribute
|
55
|
+
#
|
56
|
+
define_method "#{name}=" do |value|
|
57
|
+
send(options[:attribute])[name] = value
|
58
|
+
end
|
59
|
+
|
60
|
+
# Define a *_before_type_cast method so that we can validate
|
61
|
+
#
|
62
|
+
define_method "#{name}_before_type_cast" do
|
63
|
+
send(options[:attribute]).key?(name) ? send(options[:attribute])[name] : (options[:default].respond_to?(:call) ? options[:default].call(self) : options[:default])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Create the reader for #{options[:attribute]} unless it exists already
|
68
|
+
#
|
69
|
+
attr_reader options[:attribute] unless instance_methods.include?(options[:attribute].to_s)
|
70
|
+
|
71
|
+
# Create the writer for #{options[:attribute]} unless it exists already
|
72
|
+
#
|
73
|
+
attr_writer options[:attribute] unless instance_methods.include?("#{options[:attribute]}=")
|
74
|
+
|
75
|
+
# Overwrites the method passed as the :attribute option to ensure that it is a hash by default
|
76
|
+
#
|
77
|
+
unless instance_methods.include?("#{options[:attribute]}_with_hattr_accessor")
|
78
|
+
class_eval <<-EOF
|
79
|
+
def #{options[:attribute]}_with_hattr_accessor
|
80
|
+
self.#{options[:attribute]} = {} if #{options[:attribute]}_without_hattr_accessor.nil?
|
81
|
+
#{options[:attribute]}_without_hattr_accessor
|
82
|
+
end
|
83
|
+
alias_method :#{options[:attribute]}_without_hattr_accessor, :#{options[:attribute]}
|
84
|
+
alias_method :#{options[:attribute]}, :#{options[:attribute]}_with_hattr_accessor
|
85
|
+
EOF
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
module ActiveRecordExtensions
|
90
|
+
def self.included(base)
|
91
|
+
base.class_eval do
|
92
|
+
|
93
|
+
def read_attribute_with_hattr_accessor(attribute)
|
94
|
+
self.class.hattr_defined?(attribute) ? send(attribute) : read_attribute_without_hattr_accessor(attribute)
|
95
|
+
end
|
96
|
+
alias_method_chain :read_attribute, :hattr_accessor
|
97
|
+
|
98
|
+
def write_attribute_with_hattr_accessor(attribute, value)
|
99
|
+
self.class.hattr_defined?(attribute) ? send("#{attribute}=".to_sym, value) : write_attribute_without_hattr_accessor(attribute, value)
|
100
|
+
end
|
101
|
+
alias_method_chain :write_attribute, :hattr_accessor
|
102
|
+
|
103
|
+
def query_attribute_with_hattr_accessor(attribute)
|
104
|
+
self.class.hattr_defined?(attribute) ? send("#{attribute}?".to_sym) : query_attribute_without_hattr_accessor(attribute)
|
105
|
+
end
|
106
|
+
alias_method_chain :query_attribute, :hattr_accessor
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
Object.send :include, Huberry::HattrAccessor
|
115
|
+
ActiveRecord::Base.send :include, Huberry::HattrAccessor::ActiveRecordExtensions if Object.const_defined?(:ActiveRecord)
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_record/fixtures'
|
5
|
+
require File.dirname(__FILE__) + '/../lib/hattr_accessor'
|
6
|
+
|
7
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
|
8
|
+
|
9
|
+
ActiveRecord::Migration.suppress_messages do
|
10
|
+
ActiveRecord::Schema.define(:version => 1) do
|
11
|
+
create_table :manufacturers do |t|
|
12
|
+
t.string :name, :limit => 30, :null => false
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :products do |t|
|
16
|
+
t.string :properties, :limit => 255, :null => false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Manufacturer < ActiveRecord::Base
|
22
|
+
end
|
23
|
+
|
24
|
+
class Product < ActiveRecord::Base
|
25
|
+
serialize :properties, Hash
|
26
|
+
define_attribute_methods
|
27
|
+
|
28
|
+
hattr_accessor :name, :type => :string, :attribute => :properties
|
29
|
+
hattr_accessor :manufacturer_id, :type => :integer, :attribute => :properties, :allow_nil => true
|
30
|
+
hattr_accessor :colour, :type => :string, :attribute => :properties
|
31
|
+
hattr_accessor :weight, :type => :decimal, :attribute => :properties
|
32
|
+
|
33
|
+
belongs_to :manufacturer
|
34
|
+
|
35
|
+
validates_presence_of :name, :manufacturer_id, :colour, :weight
|
36
|
+
validates_length_of :name, :maximum => 30
|
37
|
+
validates_inclusion_of :colour, :in => %(Red Green Blue)
|
38
|
+
validates_numericality_of :weight
|
39
|
+
end
|
40
|
+
|
41
|
+
class ActiveRecordTest < Test::Unit::TestCase
|
42
|
+
|
43
|
+
def setup
|
44
|
+
Fixtures.create_fixtures(File.dirname(__FILE__) + '/fixtures', ActiveRecord::Base.connection.tables)
|
45
|
+
end
|
46
|
+
|
47
|
+
def teardown
|
48
|
+
Fixtures.reset_cache
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_should_fetch_belongs_to_assocation
|
52
|
+
assert_equal Manufacturer.find(1), Product.find(1).manufacturer
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_should_assign_belongs_to_assocation
|
56
|
+
product = Product.find(1)
|
57
|
+
manufacturer_1 = Manufacturer.find(1)
|
58
|
+
manufacturer_2 = Manufacturer.find(2)
|
59
|
+
assert_equal product.manufacturer, manufacturer_1
|
60
|
+
product.manufacturer = manufacturer_2
|
61
|
+
assert product.save
|
62
|
+
product.reload
|
63
|
+
assert_equal product.manufacturer, manufacturer_2
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_should_pass_validations
|
67
|
+
product = Product.find(1)
|
68
|
+
manufacturer_3 = Manufacturer.find(3)
|
69
|
+
assert product.valid?
|
70
|
+
product.name = "Widget D"
|
71
|
+
product.manufacturer = manufacturer_3
|
72
|
+
product.colour = "Blue"
|
73
|
+
product.weight = 10.0
|
74
|
+
assert product.valid?
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_should_fail_validations
|
78
|
+
product = Product.find(1)
|
79
|
+
assert product.valid?
|
80
|
+
product.name = "This is a very long name that needs to be over 30 characters"
|
81
|
+
assert !product.valid?
|
82
|
+
assert product.errors.on(:name).include?("too long")
|
83
|
+
product.reload
|
84
|
+
product.manufacturer_id = nil
|
85
|
+
assert !product.valid?
|
86
|
+
assert product.errors.on(:manufacturer_id).include?("can't be blank")
|
87
|
+
product.reload
|
88
|
+
product.colour = "Black"
|
89
|
+
assert !product.valid?
|
90
|
+
assert product.errors.on(:colour).include?("is not included in the list")
|
91
|
+
product.reload
|
92
|
+
product.weight = "Not a number"
|
93
|
+
assert !product.valid?
|
94
|
+
assert product.errors.on(:weight).include?("is not a number")
|
95
|
+
product.reload
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
widget_a:
|
2
|
+
id: 1
|
3
|
+
properties: "---\n :name: \"Widget A\"\n :manufacturer_id: 1\n :colour: Red\n :weight: 1.0\n"
|
4
|
+
|
5
|
+
widget_b:
|
6
|
+
id: 2
|
7
|
+
properties: "---\n :name: \"Widget B\"\n :manufacturer_id: 2\n :colour: Green\n :weight: 2.0\n"
|
8
|
+
|
9
|
+
widget_c:
|
10
|
+
id: 3
|
11
|
+
properties: "---\n :name: \"Widget C\"\n :manufacturer_id: 3\n :colour: Blue\n :weight: 3.0\n"
|
@@ -0,0 +1,286 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/hattr_accessor'
|
3
|
+
|
4
|
+
class CustomField
|
5
|
+
hattr_accessor :name, :type => :string, :attribute => :configuration
|
6
|
+
hattr_accessor :type, :type => :symbol, :attribute => :configuration
|
7
|
+
hattr_accessor :unit, :reference, :attribute => :configuration
|
8
|
+
hattr_accessor :offset, :type => :integer, :attribute => :configuration
|
9
|
+
hattr_accessor :amount, :type => :float, :allow_nil => true, :attribute => :configuration
|
10
|
+
hattr_accessor :price, :type => :decimal, :default => '5.0', :attribute => :configuration
|
11
|
+
hattr_accessor :sale_price, :type => :decimal, :attribute => :configuration,
|
12
|
+
:default => lambda { |custom_field| custom_field.price / 2 }
|
13
|
+
hattr_accessor :required, :type => :boolean, :attribute => :configuration2
|
14
|
+
hattr_accessor :sizes, :type => :array, :attribute => :configuration
|
15
|
+
|
16
|
+
def configuration2
|
17
|
+
@configuration2 ||= { :some_default_reader_value => true }
|
18
|
+
end
|
19
|
+
|
20
|
+
def configuration2=(value)
|
21
|
+
@configuration2 = value.merge(:some_default_writer_value => true)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class HattrAccessorTest < Test::Unit::TestCase
|
26
|
+
|
27
|
+
def setup
|
28
|
+
@custom_field = CustomField.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_should_alias_method_chain_configuration
|
32
|
+
assert CustomField.method_defined?(:configuration_with_hattr_accessor)
|
33
|
+
assert CustomField.method_defined?(:configuration_without_hattr_accessor)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_configuration_should_be_a_hash_by_default
|
37
|
+
assert_equal({}, @custom_field.configuration)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_should_set_name
|
41
|
+
@custom_field.name = 'Date'
|
42
|
+
assert_equal({ :name => 'Date' }, @custom_field.configuration)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_should_get_name
|
46
|
+
@custom_field.name = 'Date'
|
47
|
+
assert_equal 'Date', @custom_field.name
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_should_get_name_predicate
|
51
|
+
@custom_field.name = 'Date'
|
52
|
+
assert_equal true, @custom_field.name?
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_should_type_cast_name_as_string
|
56
|
+
assert_equal '', @custom_field.name
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_should_set_type
|
60
|
+
@custom_field.type = :date
|
61
|
+
assert_equal({ :type => :date }, @custom_field.configuration)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_should_get_type
|
65
|
+
@custom_field.type = :date
|
66
|
+
assert_equal :date, @custom_field.type
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_should_get_type_predicate
|
70
|
+
@custom_field.type = :date
|
71
|
+
assert_equal true, @custom_field.type?
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_should_type_cast_type_as_symbol
|
75
|
+
@custom_field.type = 'date'
|
76
|
+
assert_equal :date, @custom_field.type
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_should_set_unit
|
80
|
+
@custom_field.unit = 'days'
|
81
|
+
assert_equal({ :unit => 'days' }, @custom_field.configuration)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_should_get_unit
|
85
|
+
@custom_field.unit = 'days'
|
86
|
+
assert_equal 'days', @custom_field.unit
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_should_get_unit_predicate
|
90
|
+
@custom_field.unit = 'days'
|
91
|
+
assert_equal true, @custom_field.unit?
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_should_set_reference
|
95
|
+
@custom_field.reference = 'from_now'
|
96
|
+
assert_equal({ :reference => 'from_now' }, @custom_field.configuration)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_should_get_reference
|
100
|
+
@custom_field.reference = 'from_now'
|
101
|
+
assert_equal 'from_now', @custom_field.reference
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_should_get_reference_predicate
|
105
|
+
@custom_field.reference = 'from_now'
|
106
|
+
assert_equal true, @custom_field.reference?
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_should_set_offset
|
110
|
+
@custom_field.offset = 1
|
111
|
+
assert_equal({ :offset => 1 }, @custom_field.configuration)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_should_get_offset
|
115
|
+
@custom_field.offset = 1
|
116
|
+
assert_equal 1, @custom_field.offset
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_should_get_offset_predicate
|
120
|
+
@custom_field.offset = 1
|
121
|
+
assert_equal true, @custom_field.offset?
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_should_type_cast_offset_as_integer
|
125
|
+
@custom_field.offset = '1'
|
126
|
+
assert_equal 1, @custom_field.offset
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_should_set_amount
|
130
|
+
@custom_field.amount = 1.0
|
131
|
+
assert_equal({ :amount => 1.0 }, @custom_field.configuration)
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_should_get_amount
|
135
|
+
@custom_field.amount = 1.0
|
136
|
+
assert_equal 1.0, @custom_field.amount
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_should_get_amount_predicate
|
140
|
+
@custom_field.amount = 1.0
|
141
|
+
assert_equal true, @custom_field.amount?
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_should_get_nil_amount
|
145
|
+
assert_equal nil, @custom_field.amount
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_should_type_cast_amount_as_float
|
149
|
+
@custom_field.amount = '1'
|
150
|
+
assert_equal 1.0, @custom_field.amount
|
151
|
+
|
152
|
+
@custom_field.amount = 1
|
153
|
+
assert_equal 1.0, @custom_field.amount
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_should_set_required_in_configuration2
|
157
|
+
@custom_field.required = true
|
158
|
+
assert_equal true, @custom_field.configuration2[:required]
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_should_get_required
|
162
|
+
@custom_field.required = true
|
163
|
+
assert_equal true, @custom_field.required
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_should_get_required_predicate
|
167
|
+
@custom_field.required = false
|
168
|
+
assert_equal false, @custom_field.required?
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_should_type_cast_required_as_boolean
|
172
|
+
assert_equal false, @custom_field.required
|
173
|
+
|
174
|
+
@custom_field.required = false
|
175
|
+
assert_equal false, @custom_field.required
|
176
|
+
|
177
|
+
@custom_field.required = 0
|
178
|
+
assert_equal false, @custom_field.required
|
179
|
+
|
180
|
+
@custom_field.required = '0'
|
181
|
+
assert_equal false, @custom_field.required
|
182
|
+
|
183
|
+
@custom_field.required = true
|
184
|
+
assert_equal true, @custom_field.required
|
185
|
+
|
186
|
+
@custom_field.required = 1
|
187
|
+
assert_equal true, @custom_field.required
|
188
|
+
|
189
|
+
@custom_field.required = '1'
|
190
|
+
assert_equal true, @custom_field.required
|
191
|
+
|
192
|
+
@custom_field.required = ''
|
193
|
+
assert_equal false, @custom_field.required
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_should_set_price
|
197
|
+
@custom_field.price = BigDecimal.new('1.0')
|
198
|
+
assert_equal({ :price => BigDecimal.new('1.0') }, @custom_field.configuration)
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_should_get_price
|
202
|
+
@custom_field.price = BigDecimal.new('1.0')
|
203
|
+
assert_equal BigDecimal.new('1.0'), @custom_field.price
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_should_get_price_predicate
|
207
|
+
@custom_field.price = BigDecimal.new('1.0')
|
208
|
+
assert_equal true, @custom_field.price?
|
209
|
+
end
|
210
|
+
|
211
|
+
def test_should_type_cast_price_as_decimal
|
212
|
+
@custom_field.price = '1.0'
|
213
|
+
assert_equal BigDecimal.new('1.0'), @custom_field.price
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_should_get_default_price
|
217
|
+
assert_equal BigDecimal.new('5.0'), @custom_field.price
|
218
|
+
@custom_field.price = '1.0'
|
219
|
+
assert_equal BigDecimal.new('1.0'), @custom_field.price
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_should_get_sale_price
|
223
|
+
assert_equal BigDecimal.new('2.5'), @custom_field.sale_price
|
224
|
+
@custom_field.price = '1.0'
|
225
|
+
assert_equal BigDecimal.new('0.5'), @custom_field.sale_price
|
226
|
+
@custom_field.sale_price = '0.8'
|
227
|
+
assert_equal BigDecimal.new('0.8'), @custom_field.sale_price
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_should_get_sale_price_predicate
|
231
|
+
assert_equal true, @custom_field.sale_price?
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_should_set_sizes
|
235
|
+
@custom_field.sizes = %w(XS S M L XL XXL)
|
236
|
+
assert_equal({ :sizes => %w(XS S M L XL XXL) }, @custom_field.configuration)
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_should_get_sizes
|
240
|
+
@custom_field.sizes = %w(XS S M L XL XXL)
|
241
|
+
assert_equal %w(XS S M L XL XXL), @custom_field.sizes
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_should_get_sizes_predicate?
|
245
|
+
@custom_field.sizes = %w(XS S M L XL XXL)
|
246
|
+
assert_equal true, @custom_field.sizes?
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_should_type_cast_sizes_as_array
|
250
|
+
@custom_field.sizes = 'XXL'
|
251
|
+
assert_equal %w(XXL), @custom_field.sizes
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_should_edit_sizes_in_place
|
255
|
+
@custom_field.sizes = %w(XS S M L XL XXL)
|
256
|
+
@custom_field.sizes.delete("XXL")
|
257
|
+
assert_equal %w(XS S M L XL), @custom_field.sizes
|
258
|
+
@custom_field.sizes << "XXL"
|
259
|
+
assert_equal %w(XS S M L XL XXL), @custom_field.sizes
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_should_raise_exception_if_attribute_option_is_not_passed
|
263
|
+
assert_raises Huberry::HattrAccessor::MissingAttributeError do
|
264
|
+
CustomField.hattr_accessor :test
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_should_not_overwrite_existing_reader
|
269
|
+
assert_equal true, @custom_field.configuration2[:some_default_reader_value]
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_should_not_overwrite_existing_writer
|
273
|
+
@custom_field.configuration2 = {}
|
274
|
+
assert_equal true, @custom_field.configuration2[:some_default_writer_value]
|
275
|
+
end
|
276
|
+
|
277
|
+
def test_should_add_before_type_cast_reader
|
278
|
+
assert CustomField.method_defined?(:name_before_type_cast)
|
279
|
+
assert CustomField.method_defined?(:unit_before_type_cast)
|
280
|
+
assert CustomField.method_defined?(:offset_before_type_cast)
|
281
|
+
assert CustomField.method_defined?(:amount_before_type_cast)
|
282
|
+
assert CustomField.method_defined?(:price_before_type_cast)
|
283
|
+
assert CustomField.method_defined?(:required_before_type_cast)
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hattr_accessor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sean Huber
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-06 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Allows you to define attr_accessors that reference members of a hash
|
17
|
+
email: shuber@huberry.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.markdown
|
24
|
+
files:
|
25
|
+
- CHANGELOG
|
26
|
+
- MIT-LICENSE
|
27
|
+
- README.markdown
|
28
|
+
- Rakefile
|
29
|
+
- VERSION
|
30
|
+
- hattr_accessor.gemspec
|
31
|
+
- lib/hattr_accessor.rb
|
32
|
+
- test/active_record_test.rb
|
33
|
+
- test/fixtures/manufacturers.yml
|
34
|
+
- test/fixtures/products.yml
|
35
|
+
- test/hattr_accessor_test.rb
|
36
|
+
has_rdoc: true
|
37
|
+
homepage: http://github.com/shuber/hattr_accessor
|
38
|
+
licenses: []
|
39
|
+
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options:
|
42
|
+
- --charset=UTF-8
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.3.5
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Allows you to define attr_accessors that reference members of a hash
|
64
|
+
test_files:
|
65
|
+
- test/active_record_test.rb
|
66
|
+
- test/hattr_accessor_test.rb
|