htransform 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in htransform.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,149 @@
1
+ HTransform
2
+ ======
3
+
4
+ HTransform provides a simple DSL to transform a supplied hash with an arbitrary structure to another hash with an even more arbitrary structure.
5
+
6
+ ***Note:*** *HTransform does not simply modify the hash you have passed in. It actually creates a new hash from scratch. It only copies or does transformations on the fields you specify in the transform block.*
7
+
8
+ Support
9
+ =======
10
+
11
+ Should work for both 1.8 and 1.9 rubies.
12
+
13
+ Install
14
+ =======
15
+
16
+ Standard gem install.
17
+
18
+ gem install htransform
19
+
20
+
21
+ Put this in your Gemfile.
22
+
23
+ gem "htransform"
24
+
25
+ or
26
+
27
+ gem "htransform", :git =>
28
+ "git://github.com/joshkrueger/htransform.git"
29
+
30
+ Basic Usage
31
+ ===========
32
+
33
+ All you have to do is create a new class and inherit from HTransform.
34
+
35
+ class ExampleHTransform < HTransform
36
+ end
37
+
38
+ Inside your new class you just use the hopefully not-convoluted DSL.
39
+
40
+ class ExampleHTransform < HTransform
41
+ transform do
42
+ input :from => "foo", :to => :bar
43
+ end
44
+ end
45
+
46
+ Theres also shorthand for you lazy folks.
47
+
48
+ input "foo" => :bar
49
+
50
+ But how do I do more than just re-name my keys?
51
+
52
+ Let's start simple. Lets take the following hash
53
+
54
+ { "foo" => "party" }
55
+
56
+ and have HTransform change it to
57
+
58
+ { :foo => "Party" }
59
+
60
+ Its pretty easy!
61
+
62
+ input "foo" => :foo, :via => :capitalize
63
+
64
+ See! Nice and simple!
65
+
66
+ HTransform with a block!
67
+
68
+ input "foo" => :foo, :via => lambda { |x| x.capitalize }
69
+
70
+ Actually Using HTransform
71
+ ================
72
+
73
+ So we have our ExampleHTransform we created above (with our capitalize transform), saved somewhere. Lets say its in your "lib" folder. Anywhere it's loadable via require should be fine.
74
+
75
+ require 'htransform'
76
+ require 'example_htransform'
77
+
78
+ contrived_input_hash = { "foo" => "bar" }
79
+
80
+ output_hash = ExampleHTransform.convert(contrived_input_hash)
81
+
82
+ and now output_hash should look like
83
+
84
+ { :foo => "Bar" }
85
+
86
+ Slightly Less Basic Usage
87
+ =================
88
+
89
+ Want to combine multiple input keys? **HTransform CAN DO THAT**
90
+
91
+ input_multiple ["foo", "bar"] => :foo_bar
92
+
93
+ By default HTransform will just join the multiple elements with a space. i.e. "foo" and "bar" become "foo bar". Need something more complicated?
94
+
95
+ input_multiple ["foo", "bar"] => :foo_bar, :via => lambda { |x| x.map{ |v| v.capitalize }.join(" ") }
96
+
97
+ Here, "foo" and "bar" become "Foo Bar". Easy right?
98
+
99
+ Hrmm. What about nested hashes? No problem. Just define the nesting order in an array.
100
+
101
+ Lets start with nested inputs. Given the hash
102
+
103
+ { "foo" => { "bar" => "sample text" } }
104
+
105
+ and a desired hash of
106
+
107
+ { "example" => "sample text" }
108
+
109
+ we would give HTransform the following operation
110
+
111
+ input ["foo", "bar"] => "example"
112
+
113
+ If we want the opposite, all we do is reverse it.
114
+
115
+ input "example" => ["foo", "bar"]
116
+
117
+ Nested to nested?
118
+
119
+ input ["foo", "bar"] => ["baz", "qux"]
120
+
121
+ this turns
122
+
123
+ { "foo" => { "bar" => "hello world" } }
124
+
125
+ into
126
+
127
+ { "baz" => { "qux" => "hello world" } }
128
+
129
+ Want to combine multiple inputs when one or more are an array?
130
+
131
+ input [ [ "foo", "bar" ], "baz" ] => "combined nested hash"
132
+
133
+ Shorthand Limitations
134
+ ==============
135
+
136
+ Hey, the shorthand isn't perfect and theres really only a couple scenarios I can think of. If you have a key on your input hash of :via, you can't use the shorthand.
137
+
138
+ input :via => :not_via
139
+
140
+ That won't work. Why? **I'm lazy** So just use the long format.
141
+
142
+ input :from => :via, :to => :not_via, :via => :capitalize
143
+
144
+ However, :from or :to will work just fine in the shorthand. The longhand syntax only works if both the :from and :to symbols are passed in.
145
+
146
+ input :from => "from"
147
+
148
+ That will work.
149
+
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require "rspec"
6
+ require "rspec/core/rake_task"
7
+
8
+ RSpec::Core::RakeTask.new do |t|
9
+ t.rspec_opts = %w(--format documentation --color)
10
+ t.pattern = 'spec/**/*_spec.rb'
11
+ end
12
+
13
+ task :default => ["spec"]
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'htransform'
4
+ require 'example_transform'
5
+
6
+ in_hash = {
7
+ "fname" => "JOHN",
8
+ "lname" => "doe",
9
+ "phonenum" => "555-123-1122",
10
+ "email" => "j.doe@example.com",
11
+ "state_id" => {
12
+ "number" => "d123456",
13
+ "state" => "il",
14
+ "type" => "drivers"
15
+ }
16
+ }
17
+
18
+ desired_hash = {
19
+ :first_name => "John",
20
+ :last_name => "Doe",
21
+ :full_name => "John Doe",
22
+ :contact_info => {
23
+ :phone_number => "5551231122",
24
+ :email_address => "j.doe@example.com"
25
+ },
26
+ :state_identification => {
27
+ :issuing_state => "IL",
28
+ :type => "drivers",
29
+ :number => "D123456"
30
+ }
31
+ }
32
+
33
+ result_hash = ExampleTransform.convert(in_hash)
34
+
35
+ puts "The Hash Transformation worked correctly!" if result_hash == desired_hash
@@ -0,0 +1,18 @@
1
+ require 'htransform'
2
+
3
+ class ExampleTransform < HTransform
4
+ transform do
5
+
6
+ input "fname" => :first_name, :via => :capitalize
7
+ input :from => "lname", :to => :last_name, :via => lambda { |x| x.capitalize }
8
+ input "phonenum" => [:contact_info, :phone_number], :via => lambda { |p| p.gsub("-", "") }
9
+ input "email" => [:contact_info, :email_address]
10
+
11
+ input_multiple :from => ["fname", "lname"], :to => :full_name, :via => lambda { |x| x.map{ |v| v.capitalize }.join(" ") }
12
+
13
+ input ["state_id", "state"] => [:state_identification, :issuing_state], :via => :upcase
14
+ input ["state_id", "number"] => [:state_identification, :number], :via => :upcase
15
+ input ["state_id", "type"] => [:state_identification, :type]
16
+
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "htransform/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "htransform"
7
+ s.version = HTransform::VERSION
8
+ s.authors = ["Josh Krueger"]
9
+ s.email = ["joshsinbox@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{HTransform transforms your hashes}
12
+ s.description = %q{HTransform provides a simple DSL to transform arbitrary hashes into another, more arbitrary hash. Yay.}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_development_dependency("rspec", "~> 2.5.0")
20
+ s.add_development_dependency("rake")
21
+ end
@@ -0,0 +1,3 @@
1
+ class HTransform
2
+ VERSION = "0.8.0"
3
+ end
data/lib/htransform.rb ADDED
@@ -0,0 +1,198 @@
1
+ require "htransform/version"
2
+
3
+ class HTransform
4
+
5
+ ValueNotPresentError = Class.new(StandardError)
6
+
7
+ class << self
8
+ attr_reader :transform_block
9
+
10
+ attr_reader :input_nest
11
+ attr_reader :output_nest
12
+
13
+ def convert(input_hash)
14
+ new.convert(input_hash)
15
+ end
16
+
17
+ def transform(&trans_block)
18
+ @transform_block = trans_block
19
+ end
20
+
21
+ end
22
+
23
+ def initialize
24
+ @output_hash = Hash.new { |h,k| h[k] = Hash.new(&h.default_proc) }
25
+ @input_nest = []
26
+ @output_nest = []
27
+ end
28
+
29
+ def convert(input)
30
+
31
+ unless input.is_a? Hash
32
+ if input.respond_to? :to_hash
33
+ input = input.to_hash
34
+ else
35
+ raise ArgumentError "object is not a Hash or does not respond to to_hash"
36
+ end
37
+ end
38
+
39
+ @input_hash = input
40
+ instance_exec(&self.class.transform_block)
41
+ Hash[@output_hash]
42
+ end
43
+
44
+ private
45
+
46
+ def set_val(dst, val)
47
+
48
+ dst = (@output_nest + [dst]).flatten unless @output_nest.empty?
49
+
50
+ if dst.is_a? Array
51
+ last_key = dst.pop
52
+ dst.reduce(@output_hash) { |h,k| h[k] }[last_key] = val
53
+ else
54
+ @output_hash[dst] = val
55
+ end
56
+ end
57
+
58
+ def get_val(src)
59
+ return get_val!(src)
60
+ rescue ValueNotPresentError
61
+ return nil
62
+ end
63
+
64
+ def get_val!(src)
65
+
66
+ src = (@input_nest + [src]).flatten unless @input_nest.empty?
67
+
68
+ if src.is_a? Array
69
+ src.reduce(@input_hash) { |h,k| h[k] }
70
+ else
71
+ if @input_hash.key?(src)
72
+ @input_hash[src]
73
+ else
74
+ raise ValueNotPresentError
75
+ end
76
+ end
77
+ end
78
+
79
+ def key_present?(src)
80
+ src = (@input_nest + [src]).flatten unless @input_nest.empty?
81
+ if src.is_a? Array
82
+ src.reduce(@input_hash) do |h, k|
83
+ h[k].is_a?(Hash) ? h[k] : h.has_key?(k)
84
+ end
85
+ else
86
+ @input_hash.key?(src)
87
+ end
88
+ end
89
+
90
+ def parse_options(options)
91
+ from, to, via, default = nil
92
+
93
+ via = options.delete :via
94
+ default = options.delete :default
95
+
96
+ from, to = if options.key? :from and options.key? :to
97
+ [options[:from], options[:to]]
98
+ else
99
+ options.delete :from
100
+ options.delete :to
101
+
102
+ lazy_args = options.shift
103
+ if lazy_args.is_a? Array and lazy_args.length == 2
104
+ lazy_args
105
+ end
106
+ end
107
+
108
+ [from, to, via, default]
109
+ end
110
+
111
+ def passthrough(*options)
112
+ options.each do |k|
113
+ begin
114
+ input_value = get_val!(k)
115
+ set_val(k, input_value)
116
+ rescue ValueNotPresentError
117
+ end
118
+ end
119
+ end
120
+
121
+ def input_multiple(options)
122
+ from, to, via, default = parse_options(options)
123
+ via = lambda { |x| x.join(" ") } if via.nil?
124
+
125
+
126
+ old_values = from.map{ |k| get_val(k) }
127
+ new_value = if via.is_a? Proc
128
+ via.call old_values
129
+ elsif via.is_a? Symbol
130
+ if methods.include? via.to_s or methods.include? via
131
+ via_method = method(via)
132
+ if via_method.arity != 1
133
+ via_method.call(*old_values)
134
+ else
135
+ via_method.call(old_values)
136
+ end
137
+ else
138
+ old_values.send(via)
139
+ end
140
+ end
141
+
142
+
143
+ set_val(to, new_value)
144
+ end
145
+
146
+ def input(options)
147
+ from, to, via, default = parse_options(options)
148
+ return if !key_present?(from) && default.nil?
149
+
150
+ new_value = if get_val(from).nil? && !default.nil?
151
+ default.respond_to?(:call) ? default.call : default
152
+ elsif via.is_a? Proc
153
+ via.call get_val(from)
154
+ elsif via.is_a? Symbol
155
+ if methods.include? via.to_s or methods.include? via
156
+ via_method = method(via)
157
+ if via_method.arity != 1
158
+ method(via).call(*get_val(from))
159
+ else
160
+ method(via).call(get_val(from))
161
+ end
162
+ else
163
+ get_val(from).send(via)
164
+ end
165
+ else
166
+ get_val(from)
167
+ end
168
+
169
+ set_val(to, new_value)
170
+ end
171
+
172
+ def insert(options)
173
+ options.each { |key, value| set_val(key, value) }
174
+ end
175
+
176
+ def nested(options, &trans_block)
177
+
178
+ if options.is_a? Symbol
179
+ options = {:key => options, :type => "output"}
180
+ else
181
+ options[:type] = "output" unless options.key? :type
182
+ end
183
+
184
+ if options[:type] == "output" or options[:type] == "input"
185
+ type = options[:type]
186
+ if options[:key]
187
+ key = options[:key]
188
+ nest_var = "@#{type}_nest"
189
+ self.instance_variable_set(:"#{nest_var}", self.instance_variable_get(:"#{nest_var}") + [key].flatten)
190
+
191
+ instance_exec(&trans_block)
192
+
193
+ self.instance_variable_set(:"#{nest_var}", self.instance_variable_get(:"#{nest_var}") - [key].flatten)
194
+ end
195
+ end
196
+ end
197
+
198
+ end
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+ require 'date'
3
+
4
+ describe HTransform do
5
+ describe "convert" do
6
+ context "using the nested block" do
7
+
8
+ it "defaults to output nesting" do
9
+ class TestHTransform < HTransform
10
+ transform do
11
+ nested :key => :foo_nest do
12
+ input "foo" => :foo
13
+ end
14
+ end
15
+ end
16
+
17
+ input_hash = { "foo" => "bar" }
18
+ desired_hash = { :foo_nest => { :foo => "bar" } }
19
+
20
+ result = TestHTransform.convert(input_hash)
21
+
22
+ result.should == desired_hash
23
+ end
24
+
25
+ it "uses output nested when specified" do
26
+ class TestHTransform < HTransform
27
+ transform do
28
+ nested :type => "output", :key => :foo_nest do
29
+ input "foo" => :foo
30
+ end
31
+ end
32
+ end
33
+
34
+ input_hash = { "foo" => "bar" }
35
+ desired_hash = { :foo_nest => { :foo => "bar" } }
36
+
37
+ result = TestHTransform.convert(input_hash)
38
+
39
+ result.should == desired_hash
40
+ end
41
+
42
+ it "uses input nesting when specified" do
43
+ class TestHTransform < HTransform
44
+ transform do
45
+ nested :type => "input", :key => :nested_data do
46
+ input "foo" => :foo
47
+ end
48
+ end
49
+ end
50
+
51
+ input_hash = { :nested_data => { "foo" => "bar" } }
52
+ desired_hash = { :foo => "bar" }
53
+
54
+ result = TestHTransform.convert(input_hash)
55
+
56
+ result.should == desired_hash
57
+ end
58
+
59
+ it "can nest multiple nested blocks of different types" do
60
+ class TestHTransform < HTransform
61
+ transform do
62
+ nested :type => "input", :key => :nested_input do
63
+ input :foo_nest => :foo
64
+ input :bar_nest => :bar
65
+
66
+ nested :type => "input", :key => :more_nesting do
67
+ nested :type => "output", :key => :nested do
68
+ input :foobar => :foo_bar
69
+ end
70
+ end
71
+ end
72
+
73
+ nested :key => :animal_sounds do
74
+ passthrough :cat, :dog
75
+ end
76
+ end
77
+ end
78
+
79
+ input_hash = { :nested_input => { :foo_nest => "foo!", :bar_nest => "bar!", :more_nesting => { :foobar => "meh"} },
80
+ :cat => "meow!", :dog => "woof!" }
81
+
82
+ desired_hash = { :foo => "foo!", :bar => "bar!", :nested => { :foo_bar => "meh"} , :animal_sounds => { :cat => "meow!", :dog => "woof!" } }
83
+
84
+ result = TestHTransform.convert(input_hash)
85
+
86
+ result.should == desired_hash
87
+ end
88
+
89
+ it "can use the shorthand method and default to output nesting" do
90
+ class TestHTransform < HTransform
91
+ transform do
92
+ nested :out_nest do
93
+ input "foo" => :foo
94
+ end
95
+ end
96
+ end
97
+
98
+ input_hash = { "foo" => "bar" }
99
+ desired_hash = { :out_nest => { :foo => "bar" } }
100
+
101
+ result = TestHTransform.convert(input_hash)
102
+
103
+ result.should == desired_hash
104
+ end
105
+
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,392 @@
1
+ require 'spec_helper'
2
+ require 'date'
3
+
4
+ describe HTransform do
5
+ describe "convert" do
6
+ it "returns real hashes" do
7
+ class TestHTransform < HTransform
8
+ transform do
9
+ input "foo" => :baz
10
+ end
11
+ end
12
+
13
+ input_hash = { "foo" => "bar" }
14
+ result = TestHTransform.convert(input_hash)
15
+ result[:missing].should be_nil
16
+ end
17
+
18
+ it "can call instance methods from :via" do
19
+ class TestHTransform < HTransform
20
+ transform do
21
+ input "foo" => :foo, :via => lambda { |value| my_method(value) }
22
+ end
23
+ def my_method(value); value.reverse; end
24
+ end
25
+
26
+ input_hash = { "foo" => "bar" }
27
+ TestHTransform.convert(input_hash).should == { :foo => "rab" }
28
+ end
29
+
30
+ context "via is a symbol" do
31
+ it "can call instance methods via a symbol" do
32
+ class TestHTransform < HTransform
33
+ transform do
34
+ input "foo" => :foo, :via => :single_arg_method
35
+ input_multiple ["num1", "num2"] => :diff, :via => :multi_arg_method
36
+ input :nums => :diff2, :via => :multi_arg_method
37
+ input :nums => :splat_1, :via => :splat_method
38
+ input "foo" => :foo_splat, :via => :splat_method
39
+ end
40
+
41
+ def single_arg_method(value); value.reverse; end
42
+ def multi_arg_method(value1, value2); value1 - value2; end
43
+ def splat_method(*arg); arg.join(", "); end
44
+ end
45
+
46
+ input_hash = { "foo" => "bar", "num1" => 100, "num2" => 50, :nums => [100, 50] }
47
+
48
+ desired_hash = {:foo => "rab", :diff => 50, :diff2 => 50, :splat_1 => "100, 50", :foo_splat => "bar" }
49
+ TestHTransform.convert(input_hash).should == desired_hash
50
+ end
51
+ end
52
+
53
+ context "the input key is missing" do
54
+ it "does not set it in the output" do
55
+ class TestHTransform < HTransform
56
+ transform do
57
+ input "birth_date" => :birthday, :via => lambda { |d| d.strftime('%F') }
58
+ end
59
+ end
60
+
61
+ input_hash = { "foo" => "bar" }
62
+ TestHTransform.convert(input_hash).should == {}
63
+ end
64
+
65
+ context "the input key is present but the value is nil" do
66
+ it "does set it in the input" do
67
+ class TestHTransform < HTransform
68
+ transform do
69
+ input "foo" => :bar
70
+ end
71
+ end
72
+
73
+ input_hash = { "foo" => nil }
74
+ desired_hash = { :bar => nil }
75
+
76
+ TestHTransform.convert(input_hash).should == desired_hash
77
+ end
78
+ end
79
+
80
+ it "can set a default value" do
81
+ class TestHTransform < HTransform
82
+ transform do
83
+ input "birth_date" => :birthday, :default => Date.today
84
+ input "death_date" => :deathday, :default => lambda { Date.today + 5 }
85
+ end
86
+ end
87
+
88
+ input_hash = { "foo" => "bar" }
89
+ desired_hash = { :birthday => Date.today, :deathday => Date.today + 5 }
90
+
91
+ TestHTransform.convert(input_hash).should == desired_hash
92
+ end
93
+ end
94
+
95
+ context "inserting keys" do
96
+ it "should insert the key regardless of the input hash" do
97
+ class TestHTransform < HTransform
98
+ transform do
99
+ insert :bar => 'bar', :quux => 'quux'
100
+ end
101
+ end
102
+
103
+ input_hash = { "foo" => "foo" }
104
+ desired_hash = { :bar => 'bar', :quux => 'quux' }
105
+ TestHTransform.convert(input_hash).should == desired_hash
106
+ end
107
+ end
108
+
109
+ context "the value has no transform" do
110
+ it "should produce the same key (string)" do
111
+ class TestHTransform < HTransform
112
+ transform do
113
+ input :from => "foo", :to => "foo"
114
+ end
115
+ end
116
+
117
+ input_hash = { "foo" => "bar" }
118
+
119
+ TestHTransform.convert(input_hash).should == input_hash
120
+ end
121
+
122
+ it "should produce the same key via shorthand (symbol)" do
123
+ class TestHTransform < HTransform
124
+ transform do
125
+ input :foo => :foo
126
+ end
127
+ end
128
+
129
+ input_hash = { :foo => :bar }
130
+
131
+ TestHTransform.convert(input_hash).should == input_hash
132
+ end
133
+
134
+ it "should change the key (string to symbol)" do
135
+ class TestHTransform < HTransform
136
+ transform do
137
+ input "foo" => :foo
138
+ end
139
+ end
140
+
141
+ input_hash = { "foo" => "bar" }
142
+ desired_hash = { :foo => "bar" }
143
+
144
+ TestHTransform.convert(input_hash).should == desired_hash
145
+ end
146
+
147
+ it "should change the key (symbox to key)" do
148
+ class TestHTransform < HTransform
149
+ transform do
150
+ input :foo => "foo"
151
+ end
152
+ end
153
+
154
+ desired_hash = { "foo" => "bar" }
155
+ input_hash = { :foo => "bar" }
156
+
157
+ TestHTransform.convert(input_hash).should == desired_hash
158
+ end
159
+
160
+ end
161
+
162
+ context "the value should be capitalized" do
163
+ it "should produce the same key" do
164
+ class TestHTransform < HTransform
165
+ transform do
166
+ input "foo" => "foo", :via => :capitalize
167
+ end
168
+ end
169
+
170
+ input_hash = { "foo" => "bar" }
171
+ desired_hash = { "foo" => "Bar" }
172
+
173
+ TestHTransform.convert(input_hash).should == desired_hash
174
+ end
175
+
176
+ it "should produce a different key" do
177
+ class TestHTransform < HTransform
178
+ transform do
179
+ input "foo" => :foo, :via => lambda { |x| x.capitalize }
180
+ end
181
+ end
182
+
183
+ input_hash = { "foo" => "bar" }
184
+ desired_hash = { :foo => "Bar" }
185
+
186
+ TestHTransform.convert(input_hash).should == desired_hash
187
+ end
188
+
189
+ end
190
+
191
+ context "use nested input" do
192
+ it "should produce the same key without nesting" do
193
+ class TestHTransform < HTransform
194
+ transform do
195
+ input ["foo", "bar"] => "bar"
196
+ end
197
+ end
198
+
199
+ input_hash = { "foo" => { "bar" => "FOOBAR!" } }
200
+ desired_hash = { "bar" => "FOOBAR!" }
201
+
202
+ TestHTransform.convert(input_hash).should == desired_hash
203
+ end
204
+
205
+ it "should produce a different key without nesting" do
206
+ class TestHTransform < HTransform
207
+ transform do
208
+ input ["foo", "bar"] => :foo_bar
209
+ end
210
+ end
211
+
212
+ input_hash = { "foo" => { "bar" => "FOOBAR!" } }
213
+ desired_hash = { :foo_bar => "FOOBAR!" }
214
+
215
+ TestHTransform.convert(input_hash).should == desired_hash
216
+ end
217
+ end
218
+
219
+ context "use nested output" do
220
+ it "should produce the same key, but nested" do
221
+ class TestHTransform < HTransform
222
+ transform do
223
+ input :foo => [:bar, :foo]
224
+ end
225
+ end
226
+
227
+ input_hash = { :foo => "FOO!" }
228
+ desired_hash = { :bar => { :foo => "FOO!" } }
229
+
230
+ TestHTransform.convert(input_hash).should == desired_hash
231
+ end
232
+
233
+ it "should produce a different key, but nested" do
234
+ class TestHTransform < HTransform
235
+ transform do
236
+ input "foo" => [:bar, :foo]
237
+ end
238
+ end
239
+
240
+ input_hash = { "foo" => "FOO!" }
241
+ desired_hash = { :bar => { :foo => "FOO!" } }
242
+
243
+ TestHTransform.convert(input_hash).should == desired_hash
244
+ end
245
+
246
+ it "should produce a different key, but nested (with string keys)" do
247
+ class TestHTransform < HTransform
248
+ transform do
249
+ input "foo" => ["bar", "foo"]
250
+ end
251
+ end
252
+
253
+ input_hash = { "foo" => "FOO!" }
254
+ desired_hash = { "bar" => { "foo" => "FOO!" } }
255
+
256
+ TestHTransform.convert(input_hash).should == desired_hash
257
+ end
258
+
259
+ it "should use nested input and produce the same key, but nested differently" do
260
+ class TestHTransform < HTransform
261
+ transform do
262
+ input ["foo", "bar"] => ["baz", "bar"]
263
+ end
264
+ end
265
+
266
+ input_hash = { "foo" => { "bar" => "BAR!" } }
267
+ desired_hash = { "baz" => { "bar" => "BAR!" } }
268
+
269
+ TestHTransform.convert(input_hash).should == desired_hash
270
+ end
271
+
272
+ it "should use nested input and produce a different key, but nested differently" do
273
+ class TestHTransform < HTransform
274
+ transform do
275
+ input ["foo", "bar"] => [:baz, :bar]
276
+ end
277
+ end
278
+
279
+ input_hash = { "foo" => { "bar" => "BAR!" } }
280
+ desired_hash = { :baz => { :bar => "BAR!" } }
281
+
282
+ TestHTransform.convert(input_hash).should == desired_hash
283
+ end
284
+ end
285
+
286
+ context "use multiple inputs" do
287
+ it "should join two elements with a space and produce a single key" do
288
+ class TestHTransform < HTransform
289
+ transform do
290
+ input_multiple [:foo, :bar] => :foo_bar
291
+ end
292
+ end
293
+
294
+ input_hash = { :foo => "Foo", :bar => "Bar" }
295
+ desired_hash = { :foo_bar => "Foo Bar" }
296
+
297
+ TestHTransform.convert(input_hash).should == desired_hash
298
+ end
299
+
300
+ it "should join two nested elements with a dash, via a lambda and produce a single key" do
301
+ class TestHTransform < HTransform
302
+ transform do
303
+ input_multiple [[:foo, :bar], [:baz, :qux]] => :bar_qux, :via => lambda { |x| x.join("-") }
304
+ end
305
+ end
306
+
307
+ input_hash = { :foo => { :bar => "fbar" }, :baz => { :qux => "bqux" } }
308
+ desired_hash = { :bar_qux => "fbar-bqux" }
309
+
310
+ TestHTransform.convert(input_hash).should == desired_hash
311
+ end
312
+ end
313
+
314
+ end
315
+
316
+ context "multiple htransforms" do
317
+ it "does not share transformations between unrelated subclasses" do
318
+ class HTransformA < HTransform
319
+ transform do
320
+ input "foo" => :foo
321
+ end
322
+ end
323
+
324
+ class HTransformB < HTransform
325
+ transform do
326
+ input "bar" => :bar
327
+ end
328
+ end
329
+
330
+ input_hash = { "foo" => "fooval", "bar" => "barval" }
331
+ HTransformA.convert(input_hash).should == { :foo => "fooval" }
332
+ HTransformB.convert(input_hash).should == { :bar => "barval" }
333
+ end
334
+ end
335
+
336
+ context "passthrough" do
337
+
338
+ it "ignores a passthrough'd key if it is absent" do
339
+ class TestHTransform < HTransform
340
+ transform do
341
+ passthrough :foo
342
+ input :bar => :bar_key
343
+ end
344
+ end
345
+
346
+ input_hash = { :bar => 'one' }
347
+ desired_hash = { :bar_key => 'one' }
348
+
349
+ result = TestHTransform.convert(input_hash)
350
+ result.should == desired_hash
351
+ end
352
+
353
+ it "does not ignore the passthrough key if it is present but nil" do
354
+ class TestHTransform < HTransform
355
+ transform do
356
+ passthrough :foo
357
+ input :bar => :bar_key
358
+ end
359
+ end
360
+
361
+ input_hash = { :bar => 'one', :foo => nil }
362
+ desired_hash = { :bar_key => 'one', :foo => nil }
363
+
364
+ result = TestHTransform.convert(input_hash)
365
+ result.should == desired_hash
366
+ end
367
+
368
+ end
369
+
370
+ context "non-hashes" do
371
+ it "works with objects that respond to to_hash" do
372
+ class TestHTransform < HTransform
373
+ transform do
374
+ passthrough :foo
375
+ input :bar => :bar_key
376
+ end
377
+ end
378
+
379
+ class NonHash
380
+ def to_hash
381
+ { :foo => "FOO", :bar => 'BAR' }
382
+ end
383
+ end
384
+
385
+ input_object = NonHash.new
386
+ desired_hash = { :bar_key => 'BAR', :foo => "FOO" }
387
+
388
+ result = TestHTransform.convert(input_object)
389
+ result.should == desired_hash
390
+ end
391
+ end
392
+ end
@@ -0,0 +1 @@
1
+ require 'htransform'
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: htransform
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josh Krueger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-08-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.5.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.5.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: HTransform provides a simple DSL to transform arbitrary hashes into another,
47
+ more arbitrary hash. Yay.
48
+ email:
49
+ - joshsinbox@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - README.md
57
+ - Rakefile
58
+ - examples/example.rb
59
+ - examples/example_htransform.rb
60
+ - htransform.gemspec
61
+ - lib/htransform.rb
62
+ - lib/htransform/version.rb
63
+ - spec/htransform_nested_spec.rb
64
+ - spec/htransform_spec.rb
65
+ - spec/spec_helper.rb
66
+ homepage: ''
67
+ licenses: []
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 1.8.23
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: HTransform transforms your hashes
90
+ test_files:
91
+ - spec/htransform_nested_spec.rb
92
+ - spec/htransform_spec.rb
93
+ - spec/spec_helper.rb
94
+ has_rdoc: