methodize 0.1
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/README.md +102 -0
- data/lib/methodize.rb +58 -0
- data/lib/methodize/hash.rb +6 -0
- data/test/hash_test.rb +64 -0
- data/test/methodize_test.rb +98 -0
- metadata +59 -0
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
Methodize
|
2
|
+
---------
|
3
|
+
|
4
|
+
Is a module to read from and write to the keys of a ruby Hash using methods. As simple as this:
|
5
|
+
|
6
|
+
require 'methodize/hash'
|
7
|
+
hash.methodize!
|
8
|
+
|
9
|
+
If you don't want to mess with your Hash class, just do this:
|
10
|
+
|
11
|
+
require 'methodize'
|
12
|
+
hash.extend(Methodize)
|
13
|
+
|
14
|
+
The advantage of using Methodize is that you can easily access the values of complex or big Hash objects, such as converted JSONs returned from Web Services, RESTful APIs, etc.
|
15
|
+
|
16
|
+
Instead of using:
|
17
|
+
|
18
|
+
hash["article"].last["info"][:category].first
|
19
|
+
|
20
|
+
You can use:
|
21
|
+
|
22
|
+
hash.article.last.info.category.first
|
23
|
+
|
24
|
+
### Interested? Let's see more examples ###
|
25
|
+
|
26
|
+
Let's suppose that we have the following hash object:
|
27
|
+
|
28
|
+
hash = {
|
29
|
+
:article => [
|
30
|
+
{
|
31
|
+
:title => "Article 1",
|
32
|
+
:author => "John Doe",
|
33
|
+
:url => "http://a.url.com"
|
34
|
+
},{
|
35
|
+
"title" => "Article 2",
|
36
|
+
"author" => "Foo Bar",
|
37
|
+
"url" => "http://another.url.com"
|
38
|
+
},{
|
39
|
+
:title => "Article 3",
|
40
|
+
:author => "Biff Tannen",
|
41
|
+
:url => ["http://yet.another.url.com", "http://localhost"],
|
42
|
+
:info => {
|
43
|
+
:published => "2010-05-31",
|
44
|
+
:category => [:sports, :entertainment]
|
45
|
+
}
|
46
|
+
}
|
47
|
+
],
|
48
|
+
"type" => :text,
|
49
|
+
:size => 3,
|
50
|
+
:id => 123456789
|
51
|
+
}
|
52
|
+
|
53
|
+
You can change the **title of the third article** using:
|
54
|
+
|
55
|
+
hash.article[2].title = "New title"
|
56
|
+
hash.article[2].title # => "New title"
|
57
|
+
|
58
|
+
Hash public methods that conflicts with keys will be automatically freed:
|
59
|
+
|
60
|
+
hash.type # => :text
|
61
|
+
hash.size # => 3
|
62
|
+
hash.id # => 123456789
|
63
|
+
|
64
|
+
But don't panic, you can still use the Hash methods just as Ruby does for id and send methods by default:
|
65
|
+
|
66
|
+
hash.__type__ # => Hash
|
67
|
+
hash.__size__ # => 4 (hash keys count)
|
68
|
+
|
69
|
+
### Another examples ###
|
70
|
+
|
71
|
+
Writing:
|
72
|
+
|
73
|
+
hash.article.last.title = "Article 3"
|
74
|
+
hash.article[1].info = {
|
75
|
+
:published => "2010-08-31",
|
76
|
+
:category => [:sports, :entertainment]
|
77
|
+
}
|
78
|
+
hash.article << {
|
79
|
+
:title => "A new title",
|
80
|
+
:author => "Marty Mcfly"
|
81
|
+
}
|
82
|
+
hash.shift = 12
|
83
|
+
hash["inspect"] = false
|
84
|
+
hash.size = 4
|
85
|
+
|
86
|
+
Accessing:
|
87
|
+
|
88
|
+
hash.article[2].title # => "Article 3"
|
89
|
+
hash.article[1].info.published # => "2010-08-31"
|
90
|
+
hash.article.last.author # => "Marty Mcfly"
|
91
|
+
hash.shift # => 12
|
92
|
+
hash.inspect # => false
|
93
|
+
hash.__inspect__.class # => String
|
94
|
+
hash.size # => 4
|
95
|
+
hash.__size__ # => 6
|
96
|
+
|
97
|
+
You can access the Hash as usual using [] or []=.
|
98
|
+
|
99
|
+
Check the tests for more examples.
|
100
|
+
|
101
|
+
*Created by Luis Cipriani*<br/>
|
102
|
+
*http://blog.talleye.com*
|
data/lib/methodize.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
module Methodize
|
2
|
+
def self.extended(base)
|
3
|
+
# if some of the Hash keys and public methods names conflict
|
4
|
+
# we free the existant method to enable the user to call it
|
5
|
+
base.keys.each do |k|
|
6
|
+
base.__free_method__(k.to_sym) if base.public_methods.include?(k.to_s)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](key)
|
11
|
+
__normalize__(super(key))
|
12
|
+
end
|
13
|
+
|
14
|
+
def []=(key, value)
|
15
|
+
__free_method__(key) if !self.keys.include?(key) && self.public_methods.include?(key.to_s)
|
16
|
+
super(key,value)
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(name, *args)
|
20
|
+
method_name = name.to_s
|
21
|
+
if method_name[-1,1] == '='
|
22
|
+
method_name = method_name.chop
|
23
|
+
self.key?(method_name) ? key = method_name : key = method_name.to_sym
|
24
|
+
self[key] = args[0]
|
25
|
+
else
|
26
|
+
self.key?(method_name) ? key = method_name : key = method_name.to_sym
|
27
|
+
self[key]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# if you have a key that is also a method (such as Array#size)
|
32
|
+
# you can use this to free the method and use the method obj.size
|
33
|
+
# to access the value of key "size".
|
34
|
+
# you still can access the old method with __[method_name]__
|
35
|
+
def __free_method__(sym)
|
36
|
+
self.__metaclass__.send(:alias_method, "__#{sym.to_s}__".to_sym, sym) unless self.respond_to?("__#{sym.to_s}__")
|
37
|
+
self.__metaclass__.send(:define_method, sym) { method_missing(sym.to_s) }
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def __metaclass__
|
42
|
+
class << self; self; end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def __normalize__(value)
|
48
|
+
case value
|
49
|
+
when Hash
|
50
|
+
value.extend(Methodize)
|
51
|
+
when Array
|
52
|
+
value.map { |v| __normalize__(v) }
|
53
|
+
else
|
54
|
+
value
|
55
|
+
end
|
56
|
+
value
|
57
|
+
end
|
58
|
+
end
|
data/test/hash_test.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'methodize/hash'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'ruby-debug'
|
6
|
+
|
7
|
+
class MethodizeTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@hash = {
|
11
|
+
:article => [
|
12
|
+
{
|
13
|
+
:title => "Article 1",
|
14
|
+
:author => "John Doe",
|
15
|
+
:url => "http://a.url.com"
|
16
|
+
},{
|
17
|
+
"title" => "Article 2",
|
18
|
+
"author" => "Foo Bar",
|
19
|
+
"url" => "http://another.url.com"
|
20
|
+
},{
|
21
|
+
:title => "Article 3",
|
22
|
+
:author => "Biff Tannen",
|
23
|
+
:url => ["http://yet.another.url.com", "http://localhost"],
|
24
|
+
:info => {
|
25
|
+
:published => "2010-05-31",
|
26
|
+
:category => [:sports, :entertainment]
|
27
|
+
}
|
28
|
+
}
|
29
|
+
],
|
30
|
+
"type" => :text,
|
31
|
+
:size => 3,
|
32
|
+
:id => 123456789
|
33
|
+
}
|
34
|
+
|
35
|
+
@hash.methodize!
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_methodize_should_still_work_as_expected
|
39
|
+
assert_equal @hash[:article].last[:title], "Article 3"
|
40
|
+
assert_equal @hash[:article][1]["author"], "Foo Bar"
|
41
|
+
assert_equal @hash["type"] , :text
|
42
|
+
assert_equal @hash[:size] , 3
|
43
|
+
assert_nil @hash[:wrong_key]
|
44
|
+
|
45
|
+
assert @hash.keys.include?(:size)
|
46
|
+
assert_equal @hash.article.size, 3
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_methodize_should_support_read_of_values_as_methods
|
50
|
+
assert_equal @hash.article.last.title , "Article 3"
|
51
|
+
assert_equal @hash.article[1].author , "Foo Bar"
|
52
|
+
assert_equal @hash.article.last.info.category.first, :sports
|
53
|
+
assert_nil @hash.wrong_key
|
54
|
+
|
55
|
+
assert_equal @hash.size, 3
|
56
|
+
assert_equal @hash.type, :text
|
57
|
+
assert_equal @hash.id , 123456789
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_double_methodize_call_does_not_affect_anything
|
61
|
+
assert_equal @hash.methodize!.article.last.title, "Article 3"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'methodize'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'ruby-debug'
|
6
|
+
|
7
|
+
class MethodizeTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@hash = {
|
11
|
+
:article => [
|
12
|
+
{
|
13
|
+
:title => "Article 1",
|
14
|
+
:author => "John Doe",
|
15
|
+
:url => "http://a.url.com"
|
16
|
+
},{
|
17
|
+
"title" => "Article 2",
|
18
|
+
"author" => "Foo Bar",
|
19
|
+
"url" => "http://another.url.com"
|
20
|
+
},{
|
21
|
+
:title => "Article 3",
|
22
|
+
:author => "Biff Tannen",
|
23
|
+
:url => ["http://yet.another.url.com", "http://localhost"],
|
24
|
+
:info => {
|
25
|
+
:published => "2010-05-31",
|
26
|
+
:category => [:sports, :entertainment]
|
27
|
+
}
|
28
|
+
}
|
29
|
+
],
|
30
|
+
"type" => :text,
|
31
|
+
:size => 3,
|
32
|
+
:id => 123456789
|
33
|
+
}
|
34
|
+
|
35
|
+
@hash.extend(Methodize)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_hash_should_still_work_as_expected
|
39
|
+
assert_equal @hash[:article].last[:title], "Article 3"
|
40
|
+
assert_equal @hash[:article][1]["author"], "Foo Bar"
|
41
|
+
assert_equal @hash["type"] , :text
|
42
|
+
assert_equal @hash[:size] , 3
|
43
|
+
assert_nil @hash[:wrong_key]
|
44
|
+
|
45
|
+
assert @hash.keys.include?(:size)
|
46
|
+
assert_equal @hash.article.size, 3
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_hash_should_support_read_of_values_as_methods
|
50
|
+
assert_equal @hash.article.last.title , "Article 3"
|
51
|
+
assert_equal @hash.article[1].author , "Foo Bar"
|
52
|
+
assert_equal @hash.article.last.info.category.first, :sports
|
53
|
+
assert_nil @hash.wrong_key
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_should_free_existant_methods_by_default
|
57
|
+
assert_equal @hash.size, 3
|
58
|
+
assert_equal @hash.type, :text
|
59
|
+
assert_equal @hash.id , 123456789
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_should_be_able_to_call_previously_freed_methods
|
63
|
+
assert_equal @hash.__size__, 4
|
64
|
+
begin #avoid showing deprecate Object#type warning on test log
|
65
|
+
$stderr = StringIO.new
|
66
|
+
assert_equal @hash.__type__, Hash
|
67
|
+
$stderr.rewind
|
68
|
+
ensure
|
69
|
+
$stderr = STDERR
|
70
|
+
end
|
71
|
+
assert_not_equal @hash.__id__ , 123456789
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_should_enable_write_operations
|
75
|
+
@hash.article.last.title = "Article 3"
|
76
|
+
@hash.article[1].info = {
|
77
|
+
:published => "2010-08-31",
|
78
|
+
:category => [:sports, :entertainment]
|
79
|
+
}
|
80
|
+
@hash.article << {
|
81
|
+
:title => "A new title",
|
82
|
+
:author => "Marty Mcfly"
|
83
|
+
}
|
84
|
+
@hash.shift = 12
|
85
|
+
@hash["inspect"] = false
|
86
|
+
@hash.size = 4
|
87
|
+
|
88
|
+
assert_equal @hash.article[2].title , "Article 3"
|
89
|
+
assert_equal @hash.article[1].info.published, "2010-08-31"
|
90
|
+
assert_equal @hash.article.last.author , "Marty Mcfly"
|
91
|
+
assert_equal @hash.shift , 12
|
92
|
+
assert_equal @hash.inspect , false
|
93
|
+
assert_equal @hash.__inspect__.class , String
|
94
|
+
assert_equal @hash.size , 4
|
95
|
+
assert_equal @hash.__size__ , 6
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: methodize
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Luis Cipriani
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-06-01 00:00:00 -03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: lfcipriani@talleye.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README.md
|
26
|
+
- lib/methodize.rb
|
27
|
+
- lib/methodize/hash.rb
|
28
|
+
- test/methodize_test.rb
|
29
|
+
- test/hash_test.rb
|
30
|
+
has_rdoc: true
|
31
|
+
homepage: http://blog.talleye.com
|
32
|
+
licenses: []
|
33
|
+
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
requirements: []
|
52
|
+
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 1.3.5
|
55
|
+
signing_key:
|
56
|
+
specification_version: 3
|
57
|
+
summary: Module to read from and write to the keys of a ruby Hash using methods
|
58
|
+
test_files: []
|
59
|
+
|