angry_hash 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/angry_hash.gemspec +5 -2
- data/examples/accessors_eg.rb +35 -0
- data/examples/creation_eg.rb +9 -5
- data/examples/dup_eg.rb +68 -0
- data/examples/eg_helper.rb +12 -2
- data/examples/merge_eg.rb +134 -0
- data/lib/angry_hash.rb +96 -16
- metadata +6 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
data/angry_hash.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{angry_hash}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Lachie Cox"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-05-19}
|
13
13
|
s.description = %q{A stabler mash with different emphases. Used in plus2 projects AngryMob and Igor.}
|
14
14
|
s.email = %q{lachie@plus2.com.au}
|
15
15
|
s.files = [
|
@@ -17,8 +17,11 @@ Gem::Specification.new do |s|
|
|
17
17
|
"Rakefile",
|
18
18
|
"VERSION",
|
19
19
|
"angry_hash.gemspec",
|
20
|
+
"examples/accessors_eg.rb",
|
20
21
|
"examples/creation_eg.rb",
|
22
|
+
"examples/dup_eg.rb",
|
21
23
|
"examples/eg_helper.rb",
|
24
|
+
"examples/merge_eg.rb",
|
22
25
|
"lib/angry_hash.rb",
|
23
26
|
"lib/angry_hash/merge_string.rb"
|
24
27
|
]
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'eg_helper'
|
2
|
+
|
3
|
+
eg.setup do
|
4
|
+
@a = AngryHash[
|
5
|
+
:b => {
|
6
|
+
:c => 'c',
|
7
|
+
:d => 'd'
|
8
|
+
}
|
9
|
+
]
|
10
|
+
end
|
11
|
+
|
12
|
+
def same_obj(a,b)
|
13
|
+
a.__id__ == b.__id__
|
14
|
+
end
|
15
|
+
|
16
|
+
eg 'accessor! with existing subhash' do
|
17
|
+
Assert( same_obj( @a.b!, @a.b ) )
|
18
|
+
end
|
19
|
+
|
20
|
+
eg 'accessor! creating and returning' do
|
21
|
+
d = @a.d!
|
22
|
+
Assert( same_obj( d, @a.d ) )
|
23
|
+
end
|
24
|
+
|
25
|
+
eg 'accessor= AngryHash' do
|
26
|
+
d = AngryHash.new
|
27
|
+
@a.d = d
|
28
|
+
Assert( same_obj( d, @a.d ) )
|
29
|
+
end
|
30
|
+
|
31
|
+
eg 'accessor= Hash' do
|
32
|
+
d = {}
|
33
|
+
@a.d = d
|
34
|
+
Assert( ! same_obj( d, @a.d ) )
|
35
|
+
end
|
data/examples/creation_eg.rb
CHANGED
@@ -24,15 +24,19 @@ eg.helpers do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
eg 'creation duplicates' do
|
27
|
-
a1 = AngryHash[
|
27
|
+
a1 = AngryHash[
|
28
|
+
:a => { :b => 1 },
|
29
|
+
:c => [ 1, :d, {:e => :f}, nil, true, false, 1.0, [ 'x', 'y' ] ]
|
30
|
+
]
|
28
31
|
a2 = AngryHash[ a1 ]
|
29
32
|
|
30
33
|
pp oids(a1)
|
31
34
|
pp oids(a2)
|
32
35
|
|
33
|
-
|
34
|
-
|
36
|
+
Assert(a1.a.__id__ != a2.a.__id__)
|
37
|
+
Assert(a1.c[2].__id__ != a2.c[2].__id__)
|
38
|
+
Assert(a1.c[7].__id__ != a2.c[7].__id__)
|
35
39
|
end
|
36
40
|
|
37
|
-
eg 'cycle detection' do
|
38
|
-
end
|
41
|
+
#eg 'cycle detection' do
|
42
|
+
#end
|
data/examples/dup_eg.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'eg_helper'
|
2
|
+
|
3
|
+
eg.setup do
|
4
|
+
@original = { 'database' => {
|
5
|
+
"micropower_micropower_sandbox"=>
|
6
|
+
{"app_owner"=>"micropower_micropower_sandbox", "server"=>:db},
|
7
|
+
"amc_accred_staging"=>
|
8
|
+
{"app_owner"=>"amc_accred", "server"=>:plus2staging_local_mysql},
|
9
|
+
"post_froggy"=>
|
10
|
+
{"app_owner"=>"froggy_owner",
|
11
|
+
"backup"=>{"to_s3"=>true},
|
12
|
+
"server"=>:postfrog},
|
13
|
+
"isg_url_shortener"=>
|
14
|
+
{"app_owner"=>"isg_sandbox_v5", "server"=>:isg_forums_mysql},
|
15
|
+
"isg_forums"=>{"app_owner"=>"isg_forums", "server"=>:isg_forums_mysql},
|
16
|
+
"eggs_eggs"=>
|
17
|
+
{"app_owner"=>"eggs_eggs",
|
18
|
+
"backup"=>{"to_s3"=>true},
|
19
|
+
"server"=>:db,
|
20
|
+
"admin_owner"=>"eggs_admin"},
|
21
|
+
"myfrogdb"=>{"app_owner"=>:myfroggy, "server"=>:myfrog},
|
22
|
+
"amc_exam_staging"=>
|
23
|
+
{"app_owner"=>"amc_exam", "server"=>:plus2staging_local_mysql},
|
24
|
+
"micropower_golfo"=>{"app_owner"=>:golfo, "server"=>:linode_golfo_mysql},
|
25
|
+
"linode_golfo"=>{"app_owner"=>:golfo, "server"=>:linode_golfo_mysql},
|
26
|
+
"halal_staging"=>
|
27
|
+
{"app_owner"=>"halal", "server"=>:plus2staging_local_postgres},
|
28
|
+
"eggs_enag"=>
|
29
|
+
{"app_owner"=>"eggs_enag", "backup"=>{"to_s3"=>true}, "server"=>:db},
|
30
|
+
"amc_store"=>
|
31
|
+
{"app_owner"=>"amc_store", "backup"=>{"to_s3"=>true}, "server"=>:amc},
|
32
|
+
"weatherzone_wengine"=>
|
33
|
+
{"app_owner"=>"weatherzone_wengine", "server"=>:linode_postgres},
|
34
|
+
"weatherzone_wzfb_staging"=>
|
35
|
+
{"app_owner"=>"weatherzone_wzfb_staging", "server"=>:db},
|
36
|
+
"amc_ncmr"=>
|
37
|
+
{"app_owner"=>"amc_ncmr", "backup"=>{"to_s3"=>true}, "server"=>:amc},
|
38
|
+
"westpac_thinkbank_staging"=>
|
39
|
+
{"app_owner"=>"westpac_thinkbank_staging", "server"=>:db},
|
40
|
+
"micropower_micropower_staging"=>
|
41
|
+
{"app_owner"=>"micropower", "server"=>:staging_mysql},
|
42
|
+
"weatherzone_wx_staging"=>
|
43
|
+
{"app_owner"=>"weatherzone_wx_staging", "server"=>:db},
|
44
|
+
"isg_sandbox_v4"=>
|
45
|
+
{"app_owner"=>"isg_sandbox_v5", "server"=>:isg_forums_mysql},
|
46
|
+
"eggs_aecl"=>
|
47
|
+
{"app_owner"=>"eggs_aecl", "backup"=>{"to_s3"=>true}, "server"=>:db},
|
48
|
+
"eggs_hwag"=>
|
49
|
+
{"app_owner"=>"eggs_hwag", "backup"=>{"to_s3"=>true}, "server"=>:db},
|
50
|
+
"plus2_gemcutter"=>{"app_owner"=>"plus2_gemcutter", "server"=>:db},
|
51
|
+
"isg_sandbox_v5"=>
|
52
|
+
{"app_owner"=>"isg_sandbox_v5", "server"=>:isg_forums_mysql},
|
53
|
+
"amc_exam"=>
|
54
|
+
{"app_owner"=>"amc_exam", "backup"=>{"to_s3"=>true}, "server"=>:amc},
|
55
|
+
"weatherzone_wzfb"=>
|
56
|
+
{"app_owner"=>"weatherzone_wzfb", "backup"=>{"to_s3"=>true}, "server"=>:db},
|
57
|
+
"amc_store_staging"=>
|
58
|
+
{"app_owner"=>"amc_store", "server"=>:plus2staging_local_mysql}
|
59
|
+
}}
|
60
|
+
end
|
61
|
+
|
62
|
+
eg 'duping copies symbols' do
|
63
|
+
ah = AngryHash[ @original ]
|
64
|
+
ah2 = AngryHash[ ah ]
|
65
|
+
|
66
|
+
Assert( ! ah2.database.isg_sandbox_v5.server.nil? )
|
67
|
+
Assert( ah2.database.isg_sandbox_v5.server == @original['database']['isg_sandbox_v5']['server'] )
|
68
|
+
end
|
data/examples/eg_helper.rb
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
root = Pathname('../..').expand_path(__FILE__)
|
3
3
|
require 'pp'
|
4
|
+
|
5
|
+
UseGem = false
|
6
|
+
|
7
|
+
if UseGem
|
8
|
+
require 'rubygems'
|
9
|
+
require 'angry_hash'
|
10
|
+
else
|
11
|
+
$:.unshift root+'lib'
|
12
|
+
require root+'lib/angry_hash'
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'rubygems'
|
4
16
|
require 'exemplor'
|
5
|
-
$:.unshift root+'lib'
|
6
|
-
require 'angry_hash'
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'eg_helper'
|
2
|
+
require 'yajl'
|
3
|
+
|
4
|
+
eg.setup do
|
5
|
+
@a = AngryHash[
|
6
|
+
:b => {
|
7
|
+
:c => 'c',
|
8
|
+
:d => 'd'
|
9
|
+
}
|
10
|
+
]
|
11
|
+
|
12
|
+
@b = AngryHash[
|
13
|
+
:b => {
|
14
|
+
:d => 'dd',
|
15
|
+
:e => 'e'
|
16
|
+
}
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
def same_obj(a,b)
|
21
|
+
a.__id__ == b.__id__
|
22
|
+
end
|
23
|
+
|
24
|
+
eg 'merges' do
|
25
|
+
merged = @a.merge(@b)
|
26
|
+
|
27
|
+
Assert( ! same_obj( merged, @a ) )
|
28
|
+
Assert( ! same_obj( merged, @b ) )
|
29
|
+
|
30
|
+
Assert( ! merged.b.c? )
|
31
|
+
Assert( merged.b.d == 'dd' )
|
32
|
+
Assert( merged.b.e == 'e' )
|
33
|
+
end
|
34
|
+
|
35
|
+
eg 'merge dups' do
|
36
|
+
a = AngryHash[
|
37
|
+
:a => {:b => "cows"}
|
38
|
+
]
|
39
|
+
b = AngryHash[
|
40
|
+
:a => {:c => "bows"}
|
41
|
+
]
|
42
|
+
|
43
|
+
merged = a.merge(b)
|
44
|
+
|
45
|
+
Assert( ! same_obj( merged.a.b, a.a.b ) )
|
46
|
+
Assert( same_obj( merged.a.c, b.a.c ) )
|
47
|
+
end
|
48
|
+
|
49
|
+
eg 'updates' do
|
50
|
+
merged = @a.update(@b)
|
51
|
+
|
52
|
+
Assert( same_obj( merged, @a ) )
|
53
|
+
Assert( ! same_obj( merged, @b ) )
|
54
|
+
|
55
|
+
Assert( ! merged.b.c? )
|
56
|
+
Assert( merged.b.d == 'dd' )
|
57
|
+
Assert( merged.b.e == 'e' )
|
58
|
+
end
|
59
|
+
|
60
|
+
eg 'deep_merge' do
|
61
|
+
merged = @a.deep_merge(@b)
|
62
|
+
|
63
|
+
Show(merged)
|
64
|
+
|
65
|
+
Assert( ! same_obj( merged, @a ) )
|
66
|
+
Assert( ! same_obj( merged, @b ) )
|
67
|
+
|
68
|
+
Assert( merged.b.c == 'c')
|
69
|
+
Assert( merged.b.d == 'dd')
|
70
|
+
Assert( merged.b.e == 'e')
|
71
|
+
end
|
72
|
+
|
73
|
+
eg 'deep_update' do
|
74
|
+
rv = @a.deep_update(@b)
|
75
|
+
|
76
|
+
Show(@a)
|
77
|
+
Assert( same_obj( rv, @a ) )
|
78
|
+
Assert( ! same_obj( @a, @b ) )
|
79
|
+
|
80
|
+
Assert( ! rv.nil? )
|
81
|
+
Assert( @a.b.c == 'c')
|
82
|
+
Assert( @a.b.d == 'dd')
|
83
|
+
Assert( @a.b.e == 'e')
|
84
|
+
end
|
85
|
+
|
86
|
+
eg 'reverse_deep_merge' do
|
87
|
+
merged = @a.reverse_deep_merge(@b)
|
88
|
+
|
89
|
+
Show(merged)
|
90
|
+
Assert( ! merged.nil? )
|
91
|
+
Assert( ! same_obj( merged, @a ) )
|
92
|
+
Assert( ! same_obj( merged, @b ) )
|
93
|
+
|
94
|
+
Assert( merged.b.c == 'c')
|
95
|
+
Assert( merged.b.d == 'd')
|
96
|
+
Assert( merged.b.e == 'e')
|
97
|
+
end
|
98
|
+
|
99
|
+
eg 'deep regression' do
|
100
|
+
provider = AngryHash[
|
101
|
+
:network => {
|
102
|
+
:public => {
|
103
|
+
:gateway => '69.164.204.1',
|
104
|
+
:netmask => '255.255.255.0'
|
105
|
+
},
|
106
|
+
:private => {
|
107
|
+
:netmask => '255.255.128.0'
|
108
|
+
}
|
109
|
+
}]
|
110
|
+
|
111
|
+
server = AngryHash[
|
112
|
+
:public_ip => '72.14.191.135',
|
113
|
+
:private_ip => '192.168.146.105',
|
114
|
+
:network => {
|
115
|
+
:public => {:gateway => '72.14.191.1'}
|
116
|
+
}]
|
117
|
+
|
118
|
+
server.provider = provider
|
119
|
+
server.network!.reverse_deep_merge!(server.provider.network || {})
|
120
|
+
|
121
|
+
Show( Yajl::Encoder.encode( server ) )
|
122
|
+
end
|
123
|
+
|
124
|
+
eg 'merge with symbol key' do
|
125
|
+
orig = AngryHash[{
|
126
|
+
"hosts_db"=>:db,
|
127
|
+
"run_list"=>["role[database]"],
|
128
|
+
"provider"=>"octopus"
|
129
|
+
}]
|
130
|
+
|
131
|
+
merged = orig.merge(:key=>"db")
|
132
|
+
|
133
|
+
Assert( merged.keys[1].is_a?(String) )
|
134
|
+
end
|
data/lib/angry_hash.rb
CHANGED
@@ -4,25 +4,39 @@ class AngryHash < Hash
|
|
4
4
|
super(__convert(other))
|
5
5
|
end
|
6
6
|
|
7
|
-
alias_method :regular_writer, :[]=
|
8
|
-
alias_method :regular_reader, :[]
|
7
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
8
|
+
alias_method :regular_reader, :[] unless method_defined?(:regular_reader)
|
9
9
|
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
10
10
|
|
11
11
|
def []=(key, value)
|
12
|
-
regular_writer(__convert_key(key),
|
12
|
+
regular_writer(__convert_key(key), self.class.__convert_value_without_dup(value))
|
13
13
|
end
|
14
|
+
|
14
15
|
def [](key)
|
15
16
|
regular_reader(__convert_key(key))
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
|
19
|
+
def dup_and_store(key,value)
|
20
|
+
regular_writer(__convert_key(key), self.class.__convert_value(value))
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :regular_merge, :merge unless method_defined?(:regular_merge)
|
24
|
+
def merge(hash)
|
25
|
+
regular_merge(self.class.__convert_without_dup(hash))
|
26
|
+
end
|
27
|
+
|
28
|
+
def merge!(other_hash)
|
29
|
+
other_hash.each_pair { |key, value| dup_and_store(key,value) }
|
20
30
|
self
|
21
31
|
end
|
22
|
-
alias_method :
|
32
|
+
alias_method :update, :merge!
|
33
|
+
|
34
|
+
def dup
|
35
|
+
self.class[ self ]
|
36
|
+
end
|
23
37
|
|
24
38
|
def deep_merge(other_hash)
|
25
|
-
self.
|
39
|
+
self.regular_merge(other_hash) do |key, oldval, newval|
|
26
40
|
oldval = AngryHash.__convert_value(oldval)
|
27
41
|
newval = AngryHash.__convert_value(newval)
|
28
42
|
|
@@ -32,11 +46,20 @@ class AngryHash < Hash
|
|
32
46
|
|
33
47
|
def deep_merge!(other_hash)
|
34
48
|
replace(deep_merge(other_hash))
|
49
|
+
self
|
50
|
+
end
|
51
|
+
alias_method :deep_update, :deep_merge!
|
52
|
+
|
53
|
+
def reverse_deep_merge(other_hash)
|
54
|
+
self.class.__convert_value(other_hash).deep_merge(self)
|
35
55
|
end
|
36
56
|
|
37
57
|
def reverse_deep_merge!(other_hash)
|
38
|
-
replace(
|
58
|
+
replace(reverse_deep_merge(other_hash))
|
59
|
+
self
|
39
60
|
end
|
61
|
+
alias_method :reverse_deep_update, :reverse_deep_merge!
|
62
|
+
|
40
63
|
|
41
64
|
|
42
65
|
def key?(key)
|
@@ -93,13 +116,15 @@ class AngryHash < Hash
|
|
93
116
|
|
94
117
|
case method_s[-1]
|
95
118
|
when ?=
|
119
|
+
#regular_writer(key,args.first)
|
96
120
|
self[ key ] = args.first
|
97
121
|
|
98
122
|
when ??
|
99
123
|
!! self[key]
|
100
124
|
|
101
125
|
when ?!
|
102
|
-
self[key]
|
126
|
+
self[key] = AngryHash.new unless self.key?(key)
|
127
|
+
self[key]
|
103
128
|
|
104
129
|
else
|
105
130
|
self[method_s]
|
@@ -113,27 +138,82 @@ class AngryHash < Hash
|
|
113
138
|
Symbol === key ? key.to_s : key
|
114
139
|
end
|
115
140
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
141
|
+
|
142
|
+
# non-duplicating convert
|
143
|
+
def self.__convert_without_dup(hash)
|
144
|
+
hash.inject(AngryHash.new) do |newhash,(k,v)|
|
145
|
+
newhash[__convert_key(k)] = __convert_value_without_dup(v)
|
146
|
+
newhash
|
120
147
|
end
|
121
148
|
end
|
122
149
|
|
123
|
-
def self.
|
150
|
+
def self.__convert_value_without_dup(v)
|
124
151
|
v = v.to_hash if v.respond_to?(:to_hash)
|
125
152
|
|
126
153
|
case v
|
127
154
|
when AngryHash
|
128
155
|
v
|
129
156
|
when Hash
|
130
|
-
|
157
|
+
__convert_without_dup(v)
|
131
158
|
when Array
|
132
|
-
v.map {|
|
159
|
+
v.map {|vv| __convert_value_without_dup(vv)}
|
133
160
|
else
|
134
161
|
v
|
135
162
|
end
|
136
163
|
end
|
164
|
+
|
165
|
+
|
166
|
+
# duplicating convert
|
167
|
+
def self.__convert(hash,cycle_watch={})
|
168
|
+
new_hash = hash.inject(AngryHash.new) do |hash,(k,v)|
|
169
|
+
hash.regular_writer( __convert_key(k), __convert_value(v,cycle_watch) )
|
170
|
+
hash
|
171
|
+
end
|
172
|
+
|
173
|
+
#puts "re-extend? #{AngryHash === hash} #{hash.__extending_modules.inspect if (AngryHash === hash)}"
|
174
|
+
#if AngryHash === hash && hash.__extended?
|
175
|
+
#puts "rex"
|
176
|
+
#hash.__extending_modules.each {|mod| puts "rexmod=#{mod}"; new_hash.extend(mod)}
|
177
|
+
#end
|
178
|
+
|
179
|
+
new_hash
|
180
|
+
end
|
181
|
+
|
182
|
+
#def __extending_modules
|
183
|
+
#@extending_modules ||= []
|
184
|
+
#end
|
185
|
+
#def __extended?
|
186
|
+
#!__extending_modules.empty?
|
187
|
+
#end
|
188
|
+
#def extend(mod)
|
189
|
+
#puts "extending with #{mod}"
|
190
|
+
#__extending_modules << mod
|
191
|
+
#puts "extending mods: #{__extending_modules.inspect}"
|
192
|
+
#super
|
193
|
+
#end
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
def self.__convert_value(v,cycle_watch={})
|
198
|
+
return if cycle_watch.key?(v.__id__)
|
199
|
+
|
200
|
+
original_v = v
|
201
|
+
v = v.to_hash if v.respond_to?(:to_hash)
|
202
|
+
|
203
|
+
case v
|
204
|
+
when Hash
|
205
|
+
cycle_watch[original_v.__id__] = true
|
206
|
+
__convert(v,cycle_watch)
|
207
|
+
when Array
|
208
|
+
cycle_watch[original_v.__id__] = true
|
209
|
+
v.map {|vv| __convert_value(vv,cycle_watch)}
|
210
|
+
when Fixnum,Symbol,NilClass,TrueClass,FalseClass,Float
|
211
|
+
v
|
212
|
+
else
|
213
|
+
cycle_watch[original_v.__id__] = true
|
214
|
+
v.dup
|
215
|
+
end
|
216
|
+
end
|
137
217
|
|
138
218
|
end
|
139
219
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 4
|
9
|
+
version: 0.0.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Lachie Cox
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-05-19 00:00:00 +10:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -31,8 +31,11 @@ files:
|
|
31
31
|
- Rakefile
|
32
32
|
- VERSION
|
33
33
|
- angry_hash.gemspec
|
34
|
+
- examples/accessors_eg.rb
|
34
35
|
- examples/creation_eg.rb
|
36
|
+
- examples/dup_eg.rb
|
35
37
|
- examples/eg_helper.rb
|
38
|
+
- examples/merge_eg.rb
|
36
39
|
- lib/angry_hash.rb
|
37
40
|
- lib/angry_hash/merge_string.rb
|
38
41
|
has_rdoc: true
|