defined_hash 0.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/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.rdoc +42 -0
- data/Rakefile +27 -0
- data/VERSION +1 -0
- data/lib/defined_hash.rb +168 -0
- data/test/setup.rb +2 -0
- data/test/suite/lib/hash.rb +197 -0
- metadata +118 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Jonathan Stott
|
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.rdoc
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
= Defined Hash
|
2
|
+
|
3
|
+
A hash with defined keys
|
4
|
+
|
5
|
+
This is somewhat similar to the 'Dash' provided by Hashie, but it has
|
6
|
+
hashidator validations baked in, and in addition, supports the idea of
|
7
|
+
optional columns. It also allows for sensible merging of such values.
|
8
|
+
Property definition is based on the hashidator validation syntax.
|
9
|
+
|
10
|
+
class Person < DefinedHash
|
11
|
+
property :name, String # name is a string
|
12
|
+
property :emails, [String] # Array of strings for email addresses
|
13
|
+
property :addresses, [Address] # Supports nesting of defined hashes
|
14
|
+
end
|
15
|
+
|
16
|
+
It was written with mongodb in mind, as all the existing mappers were massively
|
17
|
+
complex for what I wanted.
|
18
|
+
|
19
|
+
== Who should use DefinedHash?
|
20
|
+
|
21
|
+
* People who want a simple hash schema with validations
|
22
|
+
* People who want something lightweight and fast
|
23
|
+
|
24
|
+
== Who shouldn't use DefinedHash?
|
25
|
+
|
26
|
+
* People who want error messages
|
27
|
+
* People with a very complex schema
|
28
|
+
* People who want an ORM/ODM.
|
29
|
+
|
30
|
+
== Note on Patches/Pull Requests
|
31
|
+
|
32
|
+
* Fork the project.
|
33
|
+
* Make your feature addition or bug fix.
|
34
|
+
* Add tests for it. This is important so I don't break it in a
|
35
|
+
future version unintentionally.
|
36
|
+
* Commit, do not mess with rakefile, version, or history.
|
37
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
38
|
+
* Send me a pull request. Bonus points for topic branches.
|
39
|
+
|
40
|
+
== Copyright
|
41
|
+
|
42
|
+
Copyright (c) 2010 Jonathan Stott. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "defined_hash"
|
5
|
+
gem.summary = %Q{Defined hashlike objects, with validations}
|
6
|
+
gem.description = %Q{Defined hashlike objects, with validations. For simple schema definitions and checking they're followed}
|
7
|
+
gem.email = "jonathan.stott@gmail.com"
|
8
|
+
gem.homepage = "http://github.com/namelessjon/defined_hash"
|
9
|
+
gem.authors = ["Jonathan Stott"]
|
10
|
+
gem.add_dependency "hashidator"
|
11
|
+
gem.add_development_dependency "baretest", ">= 0"
|
12
|
+
gem.add_development_dependency "yard", ">= 0"
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'yard'
|
22
|
+
YARD::Rake::YardocTask.new
|
23
|
+
rescue LoadError
|
24
|
+
task :yardoc do
|
25
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
26
|
+
end
|
27
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/defined_hash.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'hashidator'
|
2
|
+
|
3
|
+
# A hash with defined keys
|
4
|
+
#
|
5
|
+
# This is somewhat similar to the 'Dash' provided by Hashie, but it has
|
6
|
+
# hashidator validations baked in, and in addition, supports the idea of
|
7
|
+
# optional columns. It also allows for sensible merging of such values.
|
8
|
+
# Property definition is based on the hashidator validation syntax.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# class Person < DefinedHash
|
12
|
+
# property :name, String # name is a string
|
13
|
+
# property :emails, [String] # Array of strings for email addresses
|
14
|
+
# property :addresses, [Address] # Supports nesting of defined hashes
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
class DefinedHash < Hash
|
18
|
+
|
19
|
+
def initialize(attributes={})
|
20
|
+
attributes.each do |name, value|
|
21
|
+
self[name] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# Assign a value to the hash
|
27
|
+
#
|
28
|
+
# Assigns a value to the defined hash. This does a certain amount of
|
29
|
+
# typecasting, for example converting properties into arrays, or DefinedHashes
|
30
|
+
#
|
31
|
+
# @param name [Symbol,String] Property name to assign
|
32
|
+
# @param value [Object] Value to assign
|
33
|
+
#
|
34
|
+
# @return [Object] The value just assigned.
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def []=(name, value)
|
38
|
+
if (property_name = self.class.property_names.detect { |pn| pn == name.to_s })
|
39
|
+
property_name = property_name.to_sym # we know it's a defined name, so this isn't a leak.
|
40
|
+
property = self.class.properties[property_name]
|
41
|
+
case property
|
42
|
+
when Array
|
43
|
+
value = self.class.typecast_value_to_array(property, value)
|
44
|
+
if has_key?(property_name)
|
45
|
+
self[property_name].concat( value )
|
46
|
+
else
|
47
|
+
super(property_name, value)
|
48
|
+
end
|
49
|
+
when Class
|
50
|
+
super(property_name, self.class.typecast_value_to_class(property, value))
|
51
|
+
else
|
52
|
+
super(property_name, value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
# ignore properties without names
|
56
|
+
value
|
57
|
+
end
|
58
|
+
|
59
|
+
# Define a property for the hash
|
60
|
+
#
|
61
|
+
# This is used to define the hash schema. Property definitions look somewhat
|
62
|
+
# similar to DataMapper ones, but the types actually define hashidator
|
63
|
+
# validation classes, as well as defining the schema.
|
64
|
+
#
|
65
|
+
# @param name [Symbol] The name of the property
|
66
|
+
# @param type [Object] The type of the property
|
67
|
+
#
|
68
|
+
# @option opts [Boolean] :optional (false) If true, this property won't be validated if it isn't there.
|
69
|
+
#
|
70
|
+
# @return nil
|
71
|
+
#
|
72
|
+
# @api public
|
73
|
+
def self.property(name, type, opts={})
|
74
|
+
# use a DefinedHash's inbuilt validations if we have a defined hash
|
75
|
+
klass_validator = class_validator_for(type)
|
76
|
+
klass_validator = (Array === klass_validator and
|
77
|
+
Class === klass_validator.first and
|
78
|
+
klass_validator.first < DefinedHash) ? [class_validator_for(klass_validator.first)] : klass_validator
|
79
|
+
|
80
|
+
# optional validations done via proc
|
81
|
+
validator = opts.fetch(:optional, false) ? proc { |v| v.nil? ? true : klass_validator } : klass_validator
|
82
|
+
|
83
|
+
(@properties ||= {})[name] = type
|
84
|
+
(@validations ||= {})[name] = validator
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.property_names
|
89
|
+
properties.keys.map { |p| p.to_s }
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.properties
|
93
|
+
(@properties || {})
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.validations
|
97
|
+
(@validations || {})
|
98
|
+
end
|
99
|
+
|
100
|
+
# validates the hash with hashidator
|
101
|
+
#
|
102
|
+
# @return [Boolean] true/false depending on validation
|
103
|
+
#
|
104
|
+
# @api public
|
105
|
+
def valid?
|
106
|
+
Hashidator.validate(self.class.validations, self)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Merges in another hash, destroying original values
|
110
|
+
#
|
111
|
+
# @param hash [Hash] The hash to merge
|
112
|
+
#
|
113
|
+
# @return [self] The new hash
|
114
|
+
def merge!(hash)
|
115
|
+
hash.each do |key, value|
|
116
|
+
self[key] = value
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
def to_hash
|
122
|
+
out = {}
|
123
|
+
keys.each do |k|
|
124
|
+
out[k] = self[k]
|
125
|
+
end
|
126
|
+
out
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
private
|
131
|
+
def self.class_validator_for(type)
|
132
|
+
(Class === type and type < DefinedHash) ? proc { |v| v.valid? } : type
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def self.typecast_value_to_class(klass, value)
|
137
|
+
if klass < DefinedHash # if we have a defined hash, make it new!
|
138
|
+
value = klass.new(value) unless klass === value
|
139
|
+
end
|
140
|
+
value
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
|
145
|
+
def self.typecast_value_to_array(property, value)
|
146
|
+
value = (Array === value) ? value : [value]
|
147
|
+
case property.first
|
148
|
+
when Hash
|
149
|
+
return value.map { |v| self.typecast_value_to_hash(property.first, v) }
|
150
|
+
when Class
|
151
|
+
return value.map { |v| self.typecast_value_to_class(property.first, v) }
|
152
|
+
end
|
153
|
+
value
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.typecast_value_to_hash(property, values)
|
157
|
+
hash = {}
|
158
|
+
keys = property.keys.map { |k| k.to_s }
|
159
|
+
values.each do |name, value|
|
160
|
+
name = name.to_s
|
161
|
+
if keys.include?(name)
|
162
|
+
hash[name.to_sym] = value
|
163
|
+
end
|
164
|
+
end
|
165
|
+
hash
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
data/test/setup.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# Jonathan D. Stott <jonathan.stott@gmail.com>
|
3
|
+
require 'defined_hash'
|
4
|
+
BareTest.suite "DefinedHash" do
|
5
|
+
setup do
|
6
|
+
class ::Person < DefinedHash
|
7
|
+
property :name, String
|
8
|
+
property :page, String
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
teardown do
|
13
|
+
Object.send(:remove_const, :Person)
|
14
|
+
end
|
15
|
+
|
16
|
+
suite ".properties" do
|
17
|
+
assert "properties list is correct" do
|
18
|
+
equal(Person.properties, {:name => String, :page => String})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
suite ".new", :provides => :new do
|
23
|
+
assert "Basic .new works" do
|
24
|
+
Person.new
|
25
|
+
end
|
26
|
+
|
27
|
+
assert ".new with properties works" do
|
28
|
+
Person.new(:name => 'foo')
|
29
|
+
end
|
30
|
+
|
31
|
+
assert ".new with invalid properties works" do
|
32
|
+
Person.new(:name => 'foo', :num => 1)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
suite "A DefinedHash", :depends_on => :new do
|
37
|
+
setup do
|
38
|
+
@person = Person.new(:name => 'Bob', 'page' => 'bob', :num => 1)
|
39
|
+
end
|
40
|
+
|
41
|
+
assert "has valid properties set" do
|
42
|
+
@person[:name] == 'Bob'
|
43
|
+
end
|
44
|
+
|
45
|
+
assert "has strings set" do
|
46
|
+
@person[:page] == 'bob'
|
47
|
+
end
|
48
|
+
|
49
|
+
assert "Doesn't have non-valid keys set" do
|
50
|
+
@person.has_key?(:num) == false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
suite "DefinedHash properties", :depends_on => :new do
|
55
|
+
setup do
|
56
|
+
class ::Address < DefinedHash
|
57
|
+
property :name, String
|
58
|
+
property :postcode, String
|
59
|
+
end
|
60
|
+
|
61
|
+
class ::Email < DefinedHash
|
62
|
+
property :name, String
|
63
|
+
property :email, String
|
64
|
+
end
|
65
|
+
|
66
|
+
class ::Person
|
67
|
+
property :address, ::Address
|
68
|
+
property :emails, [::Email]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
teardown do
|
73
|
+
Object.send(:remove_const, :Address)
|
74
|
+
end
|
75
|
+
|
76
|
+
suite do
|
77
|
+
setup :person, [
|
78
|
+
{:name => 'name', :address => { :name => 'home', :postcode => 'code' }},
|
79
|
+
{:name => 'name', :address => { 'name' => 'home', 'postcode' => 'code' }}
|
80
|
+
# {:name => 'name', :address => Address.new('name' => 'home', 'postcode' => 'code' )}
|
81
|
+
] do |person|
|
82
|
+
@person = Person.new(person)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
assert ":person has address name set correctly" do
|
87
|
+
@person[:address][:name] == 'home'
|
88
|
+
end
|
89
|
+
|
90
|
+
assert ":person has address postcode set correctly" do
|
91
|
+
@person[:address][:postcode] == 'code'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
suite "Array properties", :depends_on => :new do
|
97
|
+
setup do
|
98
|
+
class ::Person
|
99
|
+
property :numbers, [String]
|
100
|
+
property :emails, [{:name => String}]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
assert "An array property is assigned as an array" do
|
105
|
+
@person = Person.new(:numbers => %w{5551234})
|
106
|
+
equal_unordered(%w{5551234}, @person[:numbers])
|
107
|
+
end
|
108
|
+
|
109
|
+
assert "A non-array property is assigned as an array" do
|
110
|
+
@person = Person.new(:numbers => "5551234")
|
111
|
+
equal_unordered(%w{5551234}, @person[:numbers])
|
112
|
+
end
|
113
|
+
|
114
|
+
assert "A hash in array is added properly" do
|
115
|
+
@person = Person.new(:emails => { 'name' => 'gmail', :foo => 'bar'})
|
116
|
+
equal_unordered([{:name => 'gmail'}], @person[:emails])
|
117
|
+
end
|
118
|
+
assert "A hash in array is added properly" do
|
119
|
+
@person = Person.new(:emails => [{ 'name' => 'gmail', :foo => 'bar'}])
|
120
|
+
equal_unordered([{:name => 'gmail'}], @person[:emails])
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
suite "Testing validity", :depends_on => :new do
|
129
|
+
setup do
|
130
|
+
class ::Email < DefinedHash
|
131
|
+
property :name, String, :optional => true
|
132
|
+
property :email, String
|
133
|
+
end
|
134
|
+
|
135
|
+
class ::Number < DefinedHash
|
136
|
+
property :name, String, :optional => true
|
137
|
+
property :number, String
|
138
|
+
end
|
139
|
+
|
140
|
+
class ::Person
|
141
|
+
property :nick, String, :optional => true
|
142
|
+
property :email, Email, :optional => true
|
143
|
+
property :numbers, [Number], :optional => true
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
suite "invalid" do
|
148
|
+
setup :invalid_person, [
|
149
|
+
{:name => 'name'},
|
150
|
+
{:page => 'page'},
|
151
|
+
{ :name => 'name', :page => 1 },
|
152
|
+
{ :name => 1, :page => 'page' },
|
153
|
+
{:name => 'name', :page => 'page', :email => { :name => 'home'}},
|
154
|
+
{:name => 'name', :page => 'page', :email => { :email => 'a@example.com'}, :numbers => [{ :number => "555-1234" }, { }]},
|
155
|
+
{:name => 'name', :page => 'page', :nick => 2 }
|
156
|
+
] do |invalid_person|
|
157
|
+
@person = Person.new(invalid_person)
|
158
|
+
end
|
159
|
+
|
160
|
+
assert ":invalid_person is invalid" do
|
161
|
+
@person.valid? == false
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
suite "valid" do
|
166
|
+
setup :valid_person, [
|
167
|
+
{:name => 'name', :page => 'page'},
|
168
|
+
{:name => 'name', :page => 'page', :email => { :name => 'home', :email => 'a@example.com'}},
|
169
|
+
{:name => 'name', :page => 'page', :email => { :email => 'a@example.com'}},
|
170
|
+
{:name => 'name', :page => 'page', :email => { :email => 'a@example.com'}, :numbers => [{ :number => "555-1234" }]},
|
171
|
+
{:name => 'name', :page => 'page', :nick => 'namz' }
|
172
|
+
] do |valid_person|
|
173
|
+
@person = Person.new(valid_person)
|
174
|
+
end
|
175
|
+
|
176
|
+
assert ":valid_person is valid" do
|
177
|
+
@person.valid? == true
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
suite "merge!", :depends_on => :new do
|
183
|
+
setup do
|
184
|
+
@person = Person.new(:name => 'name', :page => 'p')
|
185
|
+
end
|
186
|
+
|
187
|
+
assert "properties are merged correctly from a hash" do
|
188
|
+
@person.merge!(:page => 'page', :foo => 'bar')
|
189
|
+
@person == Person.new(:name => 'name', :page => 'page')
|
190
|
+
end
|
191
|
+
|
192
|
+
assert "properties are merged correctly from a person" do
|
193
|
+
@person.merge!(Person.new(:page => 'page', :foo => 'bar'))
|
194
|
+
@person == Person.new(:name => 'name', :page => 'page')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: defined_hash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jonathan Stott
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-07-05 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: hashidator
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: baretest
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: yard
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description: Defined hashlike objects, with validations. For simple schema definitions and checking they're followed
|
64
|
+
email: jonathan.stott@gmail.com
|
65
|
+
executables: []
|
66
|
+
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files:
|
70
|
+
- LICENSE
|
71
|
+
- README.rdoc
|
72
|
+
files:
|
73
|
+
- .document
|
74
|
+
- .gitignore
|
75
|
+
- LICENSE
|
76
|
+
- README.rdoc
|
77
|
+
- Rakefile
|
78
|
+
- VERSION
|
79
|
+
- lib/defined_hash.rb
|
80
|
+
- test/setup.rb
|
81
|
+
- test/suite/lib/hash.rb
|
82
|
+
has_rdoc: true
|
83
|
+
homepage: http://github.com/namelessjon/defined_hash
|
84
|
+
licenses: []
|
85
|
+
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options:
|
88
|
+
- --charset=UTF-8
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
hash: 3
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
version: "0"
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
requirements: []
|
110
|
+
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 1.3.7
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: Defined hashlike objects, with validations
|
116
|
+
test_files:
|
117
|
+
- test/suite/lib/hash.rb
|
118
|
+
- test/setup.rb
|