shuber-hattr_accessor 1.0.5 → 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 CHANGED
@@ -1,3 +1,9 @@
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
+
1
7
  2009-01-18 - Sean Huber (shuber@huberry.com)
2
8
  * Boolean type casted empty strings should return false
3
9
 
@@ -57,14 +57,53 @@ The current options (email me for suggestions for others) for `:type` are:
57
57
  :integer
58
58
  :float
59
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
60
99
 
61
100
  NOTE: Make sure your call `define_attribute_methods` before calling `hattr_accessor` when you're using ActiveRecord and your `:attribute` is a
62
- database field.
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.
63
103
 
64
- class CustomField < ActiveRecord::Base
65
- define_attribute_methods
66
-
104
+ class CustomField < ActiveRecord::Base
67
105
  serialize :configuration, Hash
106
+ define_attribute_methods
68
107
 
69
108
  hattr_accessor :testing, :attribute => :configuration
70
109
  end
@@ -1,46 +1,77 @@
1
+ require 'bigdecimal'
2
+
1
3
  module Huberry
2
4
  module HattrAccessor
3
5
  class MissingAttributeError < StandardError; self; end
4
-
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
+
5
15
  def hattr_accessor(*attrs)
6
16
  options = attrs.last.is_a?(Hash) ? attrs.pop : {}
7
-
17
+
8
18
  raise MissingAttributeError, 'Must specify the :attribute option with the name of an attribute which will store the hash' if options[:attribute].nil?
9
19
 
10
20
  attrs.each do |name|
21
+ hattr_attributes << name
22
+
11
23
  # Defines a type casting getter method for each attribute
12
24
  #
13
25
  define_method name do
14
- value = send(options[:attribute])[name]
26
+ value = send("#{name}_before_type_cast".to_sym)
27
+ return value if options[:allow_nil] && value.nil?
15
28
  case options[:type]
16
29
  when :string
17
30
  value.to_s
31
+ when :symbol
32
+ value.to_s.to_sym
18
33
  when :integer
19
34
  value.to_i
20
35
  when :float
21
36
  value.to_f
22
37
  when :boolean
23
38
  ![false, nil, 0, '0', ''].include?(value)
39
+ when :decimal
40
+ BigDecimal.new(value.to_s)
41
+ when :array
42
+ Array(value)
24
43
  else
25
44
  value
26
45
  end
27
46
  end
28
-
47
+
48
+ # Defines a predicate method for each attribute
49
+ #
50
+ define_method "#{name}?" do
51
+ send(name) ? true : false
52
+ end
53
+
29
54
  # Defines a setter method for each attribute
30
55
  #
31
56
  define_method "#{name}=" do |value|
32
57
  send(options[:attribute])[name] = value
33
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
34
65
  end
35
-
66
+
36
67
  # Create the reader for #{options[:attribute]} unless it exists already
37
68
  #
38
69
  attr_reader options[:attribute] unless instance_methods.include?(options[:attribute].to_s)
39
-
70
+
40
71
  # Create the writer for #{options[:attribute]} unless it exists already
41
72
  #
42
73
  attr_writer options[:attribute] unless instance_methods.include?("#{options[:attribute]}=")
43
-
74
+
44
75
  # Overwrites the method passed as the :attribute option to ensure that it is a hash by default
45
76
  #
46
77
  unless instance_methods.include?("#{options[:attribute]}_with_hattr_accessor")
@@ -54,7 +85,31 @@ module Huberry
54
85
  EOF
55
86
  end
56
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
57
111
  end
58
112
  end
59
113
 
60
- Class.send :include, Huberry::HattrAccessor
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
+ acme:
2
+ id: 1
3
+ name: Acme
4
+
5
+ bodgit:
6
+ id: 2
7
+ name: Bodgit
8
+
9
+ scarper:
10
+ id: 3
11
+ name: Scarper
@@ -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"
@@ -2,100 +2,149 @@ require 'test/unit'
2
2
  require File.dirname(__FILE__) + '/../lib/hattr_accessor'
3
3
 
4
4
  class CustomField
5
- hattr_accessor :name, :type, :type => :string, :attribute => :configuration
5
+ hattr_accessor :name, :type => :string, :attribute => :configuration
6
+ hattr_accessor :type, :type => :symbol, :attribute => :configuration
6
7
  hattr_accessor :unit, :reference, :attribute => :configuration
7
8
  hattr_accessor :offset, :type => :integer, :attribute => :configuration
8
- hattr_accessor :amount, :type => :float, :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 }
9
13
  hattr_accessor :required, :type => :boolean, :attribute => :configuration2
10
-
14
+ hattr_accessor :sizes, :type => :array, :attribute => :configuration
15
+
11
16
  def configuration2
12
17
  @configuration2 ||= { :some_default_reader_value => true }
13
18
  end
14
-
19
+
15
20
  def configuration2=(value)
16
21
  @configuration2 = value.merge(:some_default_writer_value => true)
17
22
  end
18
23
  end
19
24
 
20
25
  class HattrAccessorTest < Test::Unit::TestCase
21
-
26
+
22
27
  def setup
23
28
  @custom_field = CustomField.new
24
29
  end
25
-
30
+
26
31
  def test_should_alias_method_chain_configuration
27
32
  assert CustomField.method_defined?(:configuration_with_hattr_accessor)
28
33
  assert CustomField.method_defined?(:configuration_without_hattr_accessor)
29
34
  end
30
-
35
+
31
36
  def test_configuration_should_be_a_hash_by_default
32
37
  assert_equal({}, @custom_field.configuration)
33
38
  end
34
-
39
+
35
40
  def test_should_set_name
36
41
  @custom_field.name = 'Date'
37
42
  assert_equal({ :name => 'Date' }, @custom_field.configuration)
38
43
  end
39
-
44
+
40
45
  def test_should_get_name
41
46
  @custom_field.name = 'Date'
42
47
  assert_equal 'Date', @custom_field.name
43
48
  end
44
-
49
+
50
+ def test_should_get_name_predicate
51
+ @custom_field.name = 'Date'
52
+ assert_equal true, @custom_field.name?
53
+ end
54
+
45
55
  def test_should_type_cast_name_as_string
46
56
  assert_equal '', @custom_field.name
47
57
  end
48
-
49
- def test_should_set_and_get_an_attribute_named_type
50
- @custom_field.type = 'Date'
51
- assert_equal 'Date', @custom_field.type
58
+
59
+ def test_should_set_type
60
+ @custom_field.type = :date
61
+ assert_equal({ :type => :date }, @custom_field.configuration)
52
62
  end
53
-
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
+
54
79
  def test_should_set_unit
55
80
  @custom_field.unit = 'days'
56
81
  assert_equal({ :unit => 'days' }, @custom_field.configuration)
57
82
  end
58
-
83
+
59
84
  def test_should_get_unit
60
85
  @custom_field.unit = 'days'
61
86
  assert_equal 'days', @custom_field.unit
62
87
  end
63
-
88
+
89
+ def test_should_get_unit_predicate
90
+ @custom_field.unit = 'days'
91
+ assert_equal true, @custom_field.unit?
92
+ end
93
+
64
94
  def test_should_set_reference
65
95
  @custom_field.reference = 'from_now'
66
96
  assert_equal({ :reference => 'from_now' }, @custom_field.configuration)
67
97
  end
68
-
98
+
69
99
  def test_should_get_reference
70
100
  @custom_field.reference = 'from_now'
71
101
  assert_equal 'from_now', @custom_field.reference
72
102
  end
73
-
103
+
104
+ def test_should_get_reference_predicate
105
+ @custom_field.reference = 'from_now'
106
+ assert_equal true, @custom_field.reference?
107
+ end
108
+
74
109
  def test_should_set_offset
75
110
  @custom_field.offset = 1
76
111
  assert_equal({ :offset => 1 }, @custom_field.configuration)
77
112
  end
78
-
113
+
79
114
  def test_should_get_offset
80
115
  @custom_field.offset = 1
81
116
  assert_equal 1, @custom_field.offset
82
117
  end
83
-
118
+
119
+ def test_should_get_offset_predicate
120
+ @custom_field.offset = 1
121
+ assert_equal true, @custom_field.offset?
122
+ end
123
+
84
124
  def test_should_type_cast_offset_as_integer
85
125
  @custom_field.offset = '1'
86
126
  assert_equal 1, @custom_field.offset
87
127
  end
88
-
128
+
89
129
  def test_should_set_amount
90
130
  @custom_field.amount = 1.0
91
131
  assert_equal({ :amount => 1.0 }, @custom_field.configuration)
92
132
  end
93
-
133
+
94
134
  def test_should_get_amount
95
135
  @custom_field.amount = 1.0
96
136
  assert_equal 1.0, @custom_field.amount
97
137
  end
98
-
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
+
99
148
  def test_should_type_cast_amount_as_float
100
149
  @custom_field.amount = '1'
101
150
  assert_equal 1.0, @custom_field.amount
@@ -103,55 +152,135 @@ class HattrAccessorTest < Test::Unit::TestCase
103
152
  @custom_field.amount = 1
104
153
  assert_equal 1.0, @custom_field.amount
105
154
  end
106
-
155
+
107
156
  def test_should_set_required_in_configuration2
108
157
  @custom_field.required = true
109
158
  assert_equal true, @custom_field.configuration2[:required]
110
159
  end
111
-
160
+
112
161
  def test_should_get_required
113
162
  @custom_field.required = true
114
163
  assert_equal true, @custom_field.required
115
164
  end
116
-
165
+
166
+ def test_should_get_required_predicate
167
+ @custom_field.required = false
168
+ assert_equal false, @custom_field.required?
169
+ end
170
+
117
171
  def test_should_type_cast_required_as_boolean
118
172
  assert_equal false, @custom_field.required
119
-
173
+
120
174
  @custom_field.required = false
121
175
  assert_equal false, @custom_field.required
122
-
176
+
123
177
  @custom_field.required = 0
124
178
  assert_equal false, @custom_field.required
125
-
179
+
126
180
  @custom_field.required = '0'
127
181
  assert_equal false, @custom_field.required
128
-
182
+
129
183
  @custom_field.required = true
130
184
  assert_equal true, @custom_field.required
131
-
185
+
132
186
  @custom_field.required = 1
133
187
  assert_equal true, @custom_field.required
134
-
188
+
135
189
  @custom_field.required = '1'
136
190
  assert_equal true, @custom_field.required
137
-
191
+
138
192
  @custom_field.required = ''
139
193
  assert_equal false, @custom_field.required
140
194
  end
141
-
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
+
142
262
  def test_should_raise_exception_if_attribute_option_is_not_passed
143
263
  assert_raises Huberry::HattrAccessor::MissingAttributeError do
144
264
  CustomField.hattr_accessor :test
145
265
  end
146
266
  end
147
-
267
+
148
268
  def test_should_not_overwrite_existing_reader
149
269
  assert_equal true, @custom_field.configuration2[:some_default_reader_value]
150
270
  end
151
-
271
+
152
272
  def test_should_not_overwrite_existing_writer
153
273
  @custom_field.configuration2 = {}
154
274
  assert_equal true, @custom_field.configuration2[:some_default_writer_value]
155
275
  end
156
-
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
+
157
286
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shuber-hattr_accessor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Huber
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-18 00:00:00 -08:00
12
+ date: 2009-03-05 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -27,6 +27,8 @@ files:
27
27
  - MIT-LICENSE
28
28
  - Rakefile
29
29
  - README.markdown
30
+ - test/fixtures/manufacturers.yml
31
+ - test/fixtures/products.yml
30
32
  has_rdoc: false
31
33
  homepage: http://github.com/shuber/hattr_accessor
32
34
  post_install_message:
@@ -54,4 +56,5 @@ signing_key:
54
56
  specification_version: 2
55
57
  summary: Allows you to define attr_accessors that reference members of a hash
56
58
  test_files:
59
+ - test/active_record_test.rb
57
60
  - test/hattr_accessor_test.rb