ismasan-hash_mapper 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2009-01-29
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,13 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/hash_mapper.rb
7
+ script/console
8
+ script/destroy
9
+ script/generate
10
+ spec/hash_mapper_spec.rb
11
+ spec/spec.opts
12
+ spec/spec_helper.rb
13
+ tasks/rspec.rake
@@ -0,0 +1,7 @@
1
+
2
+ For more information on hash_mapper, see http://hash_mapper.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
@@ -0,0 +1,206 @@
1
+ = hash_mapper
2
+
3
+ * http://github.com/ismasan/hash_mapper
4
+
5
+ == DESCRIPTION:
6
+
7
+ Maps values from hashes with different structures and/or key names. Ideal for normalizing arbitrary data to be consumed by your applications, or to prepare your data for different display formats (ie. json).
8
+
9
+ Tiny module that allows you to easily adapt from one hash structure to another with a simple declarative DSL.
10
+
11
+ == FEATURES/PROBLEMS:
12
+
13
+ It is a module so it doesn't get in the way of your inheritance tree.
14
+
15
+ == SYNOPSIS:
16
+
17
+ class ManyLevels
18
+ extend HashMapper
19
+ map from('/name'), to('/tag_attributes/name')
20
+ map from('/properties/type'), to('/tag_attributes/type')
21
+ map from('/tagid'), to('/tag_id')
22
+ map from('/properties/egg'), to('/chicken')
23
+ end
24
+
25
+ input = {
26
+ :name => 'ismael',
27
+ :tagid => 1,
28
+ :properties => {
29
+ :type => 'BLAH',
30
+ :egg => 33
31
+ }
32
+ }
33
+
34
+ ManyLevels.translate(input)
35
+
36
+ # outputs:
37
+ {
38
+ :tag_id => 1,
39
+ :chicken => 33,
40
+ :tag_attributes => {
41
+ :name => 'ismael',
42
+ :type => 'BLAH'
43
+ }
44
+ }
45
+
46
+ === Uses:
47
+
48
+ HashMapper was primarily written as a way of mapping data structure in json requests to hashes with structures friendlier to our ActiveRecord models:
49
+
50
+ @article = Article.create( ArticleParams.translate(params[:weird_article_data]) )
51
+
52
+ You can use HashMapper in your own little hash-like objects:
53
+
54
+ class NiceHash
55
+ include Enumerable
56
+ extend HashMap
57
+
58
+ map from('/names/first'), to('/first_name')
59
+ map from('/names/last'), to('/last_name')
60
+
61
+ def initialize(input_hash)
62
+ @hash = self.class.translate(input_hash)
63
+ end
64
+
65
+ def [](k)
66
+ @hash[k]
67
+ end
68
+
69
+ def []=(k,v)
70
+ @hash[k] = v
71
+ end
72
+
73
+ def each(&block)
74
+ @hash.each(&block)
75
+ end
76
+ end
77
+
78
+ @user = User.new(NiceHash.new(params))
79
+
80
+ === Options:
81
+
82
+ ==== Coercing values
83
+
84
+ You want to make sure an incoming value get converted to a certain type, so
85
+
86
+ {'one' => '1', 'two' => '2'} gets translated to {:one => 1, :two => 2}
87
+
88
+ Do this:
89
+
90
+ map from('/one'), to('/one', :to_i)
91
+ map from('/two'), to('/two', :to_i)
92
+
93
+ You can pass :to_i, :to_s or anything available method that makes sense.
94
+
95
+ ==== Custom value filtering
96
+
97
+ You want to pass the final value of a key through a custom filter:
98
+
99
+ {:names => {:first => 'Ismael', :last => 'Celis'}} gets translated to {:user => 'Mr. Celis, Ismael'}
100
+
101
+ Do this:
102
+
103
+ map from('/names'), to('/user') do |names|
104
+ "Mr. #{names[1]}, #{names[0]}"
105
+ end
106
+
107
+ ==== Array access
108
+
109
+ You want:
110
+
111
+ {:names => ['Ismael', 'Celis']} converted to {:first_name => 'Ismael', :last_name => 'Celis'}
112
+
113
+ Do this:
114
+
115
+ map from('/names[0]'), to('/first_name')
116
+ map from('/names[1]'), to('/last_name')
117
+
118
+ === Advanced usage
119
+ ==== Nested mappers
120
+
121
+ You want to map nested structures delegating to different mappers:
122
+
123
+ From this:
124
+ input = {
125
+ :project => 'HashMapper',
126
+ :url => 'http://github.com/ismasan/hash_mapper',
127
+ :author_names => {:first => 'Ismael', :last => 'Celis'}
128
+ }
129
+ To this:
130
+ output = {
131
+ :project_name => 'HashMapper',
132
+ :url => 'http://github.com/ismasan/hash_mapper',
133
+ :author => {:first_name => 'Ismael', :last_name => 'Celis'}
134
+ }
135
+
136
+ Define an UserMapper separate from your ProjectMapper, so you reuse them combined or standalone
137
+
138
+ class UserMapper
139
+ extend HashMapper
140
+ map from('/first'), to('/first_name')
141
+ map from('/last'), to('/lastt_name')
142
+ end
143
+
144
+ class ProjectMapper
145
+ extend HashMapper
146
+ map from('/project'), to('/project_name')
147
+ map from('/url'), to('/url')
148
+ map from('/author_names'), to('/author'), &UserMapper
149
+ end
150
+
151
+ Now ProjectMapper will delegate parsing of :author_names to UserMapper
152
+
153
+ ProjectMapper.translate( input ) # => output
154
+
155
+ * Note the ampersand in &UserMapper. This is important if you are passing custom classes instead of procs.
156
+ * If you want to implement your own filter class just define to_proc in it.
157
+
158
+ Let's say you have a CompanyMapper which maps a hash with an array of employees, and you want to reuse UserMapper to map each employee:
159
+
160
+ class CompanyMapper
161
+ map from('/info/name'), to('/company_name')
162
+ map form('/info/address'), to('/company_address')
163
+ map from('/info/year_founded'), to('year_founded', :to_i)
164
+
165
+ map from('/employees'), to('employees') do |employees_array|
166
+ employees_array.collect {|emp_hash| UserMapper.translate(emp_hash)}
167
+ end
168
+ end
169
+
170
+ == REQUIREMENTS:
171
+
172
+
173
+ == INSTALL:
174
+
175
+ # If you haven't done this already:
176
+
177
+ gem sources -a http://gems.github.com
178
+
179
+ # Now install
180
+
181
+ sudo gem install ismasan-hash_mapper
182
+
183
+ == LICENSE:
184
+
185
+ (The MIT License)
186
+
187
+ Copyright (c) 2009 Ismael Celis
188
+
189
+ Permission is hereby granted, free of charge, to any person obtaining
190
+ a copy of this software and associated documentation files (the
191
+ 'Software'), to deal in the Software without restriction, including
192
+ without limitation the rights to use, copy, modify, merge, publish,
193
+ distribute, sublicense, and/or sell copies of the Software, and to
194
+ permit persons to whom the Software is furnished to do so, subject to
195
+ the following conditions:
196
+
197
+ The above copyright notice and this permission notice shall be
198
+ included in all copies or substantial portions of the Software.
199
+
200
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
201
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
202
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
203
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
204
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
205
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
206
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/hash_mapper'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('hash_mapper', HashMapper::VERSION) do |p|
7
+ p.developer('Ismael Celis', 'ismaelct@gmail.com')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
10
+ p.rubyforge_name = p.name # TODO this is default value
11
+ # p.extra_deps = [
12
+ # ['activesupport','>= 2.0.2'],
13
+ # ]
14
+ p.extra_dev_deps = [
15
+ ['newgem', ">= #{::Newgem::VERSION}"]
16
+ ]
17
+
18
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
19
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
20
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
21
+ p.rsync_args = '-av --delete --ignore-errors'
22
+ end
23
+
24
+ require 'newgem/tasks' # load /tasks/*.rake
25
+ Dir['tasks/**/*.rake'].each { |t| load t }
26
+
27
+ # TODO - want other tests/tasks run by default? Add them to the list
28
+ # task :default => [:spec, :features]
@@ -0,0 +1,111 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module HashMapper
5
+ VERSION = '0.0.2'
6
+
7
+ def maps
8
+ @maps ||= []
9
+ end
10
+
11
+ def map(from, to, &blk)
12
+ to.filter = blk if block_given?
13
+ self.maps << [from, to]
14
+ end
15
+
16
+ def from(path, coerce_method = nil)
17
+ PathMap.new(path, coerce_method)
18
+ end
19
+
20
+ alias :to :from
21
+
22
+ def translate(incoming_hash)
23
+ output = {}
24
+ incoming_hash = simbolize_keys(incoming_hash)
25
+ maps.each do |path_from, path_to|
26
+ path_to.inject(output){|h,e|
27
+ if h[e]
28
+ h[e]
29
+ else
30
+ h[e] = (e == path_to.last ? path_to.resolve_value(path_from, incoming_hash) : {})
31
+ end
32
+ }
33
+ end
34
+ output
35
+ end
36
+
37
+ # from http://www.geekmade.co.uk/2008/09/ruby-tip-normalizing-hash-keys-as-symbols/
38
+ #
39
+ def simbolize_keys(hash)
40
+ hash.inject({}) do |options, (key, value)|
41
+ options[(key.to_sym rescue key) || key] = value
42
+ options
43
+ end
44
+ end
45
+
46
+ # This allows us to pass mapper classes as block arguments
47
+ #
48
+ def to_proc
49
+ Proc.new{|*args| self.translate(*args)}
50
+ end
51
+
52
+ class PathMap
53
+
54
+ include Enumerable
55
+
56
+ attr_reader :segments
57
+
58
+ attr_writer :filter
59
+
60
+ def initialize(path, coerce_method = nil)
61
+ @path = path.dup
62
+ @coerce_method = coerce_method
63
+ @index = extract_array_index!(path)
64
+ @segments = parse(path)
65
+ @filter = lambda{|value| value}# default filter does nothing
66
+ end
67
+
68
+ def resolve_value(another_path, incoming_hash)
69
+ coerce another_path.extract_from(incoming_hash)
70
+ end
71
+
72
+ def coerce(value)
73
+ value = @filter.call(value)
74
+ return value unless @coerce_method
75
+ value.send(@coerce_method) rescue value
76
+ end
77
+
78
+ def extract_from(incoming_hash)
79
+ value = inject(incoming_hash){|hh,ee| hh[ee]}
80
+ return value unless @index
81
+ value.to_a[@index]
82
+ end
83
+
84
+ def each(&blk)
85
+ @segments.each(&blk)
86
+ end
87
+
88
+ def last
89
+ @segments.last
90
+ end
91
+
92
+ private
93
+
94
+ def extract_array_index!(path)
95
+ path.gsub! /(\[[0-9]+\])/, ''
96
+ if idx = $1
97
+ idx.gsub(/(\[|\])/, '').to_i
98
+ else
99
+ nil
100
+ end
101
+ end
102
+
103
+ def parse(path)
104
+ p = path.split('/')
105
+ p.shift
106
+ p.collect{|e| e.to_sym}
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/hash_mapper.rb'}"
9
+ puts "Loading hash_mapper gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,224 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ class OneLevel
4
+ extend HashMapper
5
+ map from('/name'), to('/nombre')
6
+ end
7
+
8
+ describe 'mapping a hash wit one level' do
9
+
10
+ before :each do
11
+ @from = {:name => 'ismael'}
12
+ @to = {:nombre => 'ismael'}
13
+ end
14
+
15
+ it "should map to" do
16
+ OneLevel.translate(@from).should == @to
17
+ end
18
+
19
+ it "should have indifferent access" do
20
+ OneLevel.translate({'name' => 'ismael'}).should == @to
21
+ end
22
+
23
+ end
24
+
25
+ class ManyLevels
26
+ extend HashMapper
27
+ map from('/name'), to('/tag_attributes/name')
28
+ map from('/properties/type'), to('/tag_attributes/type')
29
+ map from('/tagid'), to('/tag_id')
30
+ map from('/properties/egg'), to('/chicken')
31
+ end
32
+
33
+ describe 'mapping from one nested hash to another' do
34
+
35
+ before :each do
36
+ @from = {
37
+ :name => 'ismael',
38
+ :tagid => 1,
39
+ :properties => {
40
+ :type => 'BLAH',
41
+ :egg => 33
42
+ }
43
+ }
44
+
45
+ @to = {
46
+ :tag_id => 1,
47
+ :chicken => 33,
48
+ :tag_attributes => {
49
+ :name => 'ismael',
50
+ :type => 'BLAH'
51
+ }
52
+ }
53
+ end
54
+
55
+ it "should map from and to different depths" do
56
+ ManyLevels.translate(@from).should == @to
57
+ end
58
+
59
+ end
60
+
61
+ class DifferentTypes
62
+ extend HashMapper
63
+ map from('/strings/a'), to('/integers/a',:to_i)
64
+ map from('/integers/b'), to('/strings/b',:to_s)
65
+ end
66
+
67
+ describe 'coercing types' do
68
+
69
+ before :each do
70
+ @from = {
71
+ :strings => {:a => '10'},
72
+ :integers =>{:b => 20}
73
+ }
74
+
75
+ @to = {
76
+ :integers => {:a => 10},
77
+ :strings => {:b => '20'}
78
+ }
79
+ end
80
+
81
+ it "should coerce values to specified types" do
82
+ DifferentTypes.translate(@from).should == @to
83
+ end
84
+
85
+ end
86
+
87
+
88
+ describe 'arrays in hashes' do
89
+ before :each do
90
+ @from = {
91
+ :name => ['ismael','sachiyo'],
92
+ :tagid => 1,
93
+ :properties => {
94
+ :type => 'BLAH',
95
+ :egg => 33
96
+ }
97
+ }
98
+
99
+ @to = {
100
+ :tag_id => 1,
101
+ :chicken => 33,
102
+ :tag_attributes => {
103
+ :name => ['ismael','sachiyo'],
104
+ :type => 'BLAH'
105
+ }
106
+ }
107
+ end
108
+
109
+ it "should map array values as normal" do
110
+ ManyLevels.translate(@from).should == @to
111
+ end
112
+ end
113
+
114
+ class WithArrays
115
+ extend HashMapper
116
+ map from('/arrays/names[0]'), to('/first_name')
117
+ map from('/arrays/names[1]'), to('/last_name')
118
+ map from('/arrays/company'), to('/work/company')
119
+ end
120
+
121
+ describe "array indexes" do
122
+ before :each do
123
+ @from = {
124
+ :arrays => {
125
+ :names => ['ismael','celis'],
126
+ :company => 'New Bamboo'
127
+ }
128
+ }
129
+ @to ={
130
+ :first_name => 'ismael',
131
+ :last_name => 'celis',
132
+ :work => {:company => 'New Bamboo'}
133
+ }
134
+ end
135
+
136
+ it "should extract defined array values" do
137
+ WithArrays.translate(@from).should == @to
138
+ end
139
+ end
140
+
141
+ class PersonWithBlock
142
+ extend HashMapper
143
+
144
+ map from('/names/first'), to('/first_name') do |name|
145
+ "+++ #{name} +++"
146
+ end
147
+ end
148
+
149
+ describe "with blocks filters" do
150
+ before :each do
151
+ @from = {
152
+ :names => {:first => 'Ismael'}
153
+ }
154
+ @to = {
155
+ :first_name => '+++ Ismael +++'
156
+ }
157
+ end
158
+
159
+ it "should pass final value through given block" do
160
+ PersonWithBlock.translate(@from).should == @to
161
+ end
162
+ end
163
+
164
+ class ProjectMapper
165
+ extend HashMapper
166
+
167
+ map from('/name'), to('/project_name')
168
+ map from('/author_hash'), to('/author'), &PersonWithBlock
169
+ end
170
+
171
+ describe "with nested mapper" do
172
+ before :each do
173
+ @from ={
174
+ :name => 'HashMapper',
175
+ :author_hash => {
176
+ :names => {:first => 'Ismael'}
177
+ }
178
+ }
179
+ @to = {
180
+ :project_name => 'HashMapper',
181
+ :author => {:first_name => '+++ Ismael +++'}
182
+ }
183
+ end
184
+
185
+ it "should delegate nested hashes to another mapper" do
186
+ ProjectMapper.translate(@from).should == @to
187
+ end
188
+ end
189
+
190
+ class CompanyMapper
191
+ extend HashMapper
192
+
193
+ map from('/name'), to('/company_name')
194
+ map from('/employees'), to('/employees') do |employees_array|
195
+ employees_array.collect{|emp_hash| PersonWithBlock.translate(emp_hash)}
196
+ end
197
+ end
198
+
199
+ describe "with arrays of nested hashes" do
200
+ before :each do
201
+ @from = {
202
+ :name => 'New Bamboo',
203
+ :employees => [
204
+ {:names => {:first => 'Ismael'}},
205
+ {:names => {:first => 'Sachiyo'}},
206
+ {:names => {:first => 'Pedro'}}
207
+ ]
208
+ }
209
+ @to = {
210
+ :company_name => 'New Bamboo',
211
+ :employees => [
212
+ {:first_name => '+++ Ismael +++'},
213
+ {:first_name => '+++ Sachiyo +++'},
214
+ {:first_name => '+++ Pedro +++'}
215
+ ]
216
+ }
217
+ end
218
+
219
+ it "should pass array value though given block mapper" do
220
+ CompanyMapper.translate(@from).should == @to
221
+ end
222
+ end
223
+
224
+
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'hash_mapper'
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'spec'
6
+ end
7
+ begin
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ desc "Run the specs under spec/models"
18
+ Spec::Rake::SpecTask.new do |t|
19
+ t.spec_opts = ['--options', "spec/spec.opts"]
20
+ t.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ismasan-hash_mapper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Ismael Celis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-31 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: newgem
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.3
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: hoe
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 1.8.0
32
+ version:
33
+ description: Maps values from hashes with different structures and/or key names. Ideal for normalizing arbitrary data to be consumed by your applications, or to prepare your data for different display formats (ie. json). Tiny module that allows you to easily adapt from one hash structure to another with a simple declarative DSL.
34
+ email:
35
+ - ismaelct@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - History.txt
42
+ - Manifest.txt
43
+ - PostInstall.txt
44
+ - README.rdoc
45
+ files:
46
+ - History.txt
47
+ - Manifest.txt
48
+ - PostInstall.txt
49
+ - README.rdoc
50
+ - Rakefile
51
+ - lib/hash_mapper.rb
52
+ - script/console
53
+ - script/destroy
54
+ - script/generate
55
+ - spec/hash_mapper_spec.rb
56
+ - spec/spec.opts
57
+ - spec/spec_helper.rb
58
+ - tasks/rspec.rake
59
+ has_rdoc: true
60
+ homepage: http://github.com/ismasan/hash_mapper
61
+ post_install_message: PostInstall.txt
62
+ rdoc_options:
63
+ - --main
64
+ - README.rdoc
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project: hash_mapper
82
+ rubygems_version: 1.2.0
83
+ signing_key:
84
+ specification_version: 2
85
+ summary: Maps values from hashes with different structures and/or key names
86
+ test_files: []
87
+