sundbp-extlib 0.9.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/.autotest +21 -0
  2. data/.document +5 -0
  3. data/.gitignore +22 -0
  4. data/LICENSE +47 -0
  5. data/README.rdoc +17 -0
  6. data/Rakefile +28 -0
  7. data/VERSION +1 -0
  8. data/extlib.gemspec +146 -0
  9. data/lib/extlib.rb +50 -0
  10. data/lib/extlib/array.rb +36 -0
  11. data/lib/extlib/assertions.rb +8 -0
  12. data/lib/extlib/blank.rb +89 -0
  13. data/lib/extlib/boolean.rb +11 -0
  14. data/lib/extlib/byte_array.rb +6 -0
  15. data/lib/extlib/class.rb +177 -0
  16. data/lib/extlib/datetime.rb +29 -0
  17. data/lib/extlib/dictionary.rb +433 -0
  18. data/lib/extlib/hash.rb +442 -0
  19. data/lib/extlib/hook.rb +403 -0
  20. data/lib/extlib/inflection.rb +440 -0
  21. data/lib/extlib/lazy_array.rb +451 -0
  22. data/lib/extlib/lazy_module.rb +18 -0
  23. data/lib/extlib/logger.rb +198 -0
  24. data/lib/extlib/mash.rb +155 -0
  25. data/lib/extlib/module.rb +47 -0
  26. data/lib/extlib/nil.rb +5 -0
  27. data/lib/extlib/numeric.rb +5 -0
  28. data/lib/extlib/object.rb +175 -0
  29. data/lib/extlib/object_space.rb +13 -0
  30. data/lib/extlib/pathname.rb +20 -0
  31. data/lib/extlib/pooling.rb +235 -0
  32. data/lib/extlib/rubygems.rb +38 -0
  33. data/lib/extlib/simple_set.rb +66 -0
  34. data/lib/extlib/string.rb +176 -0
  35. data/lib/extlib/struct.rb +17 -0
  36. data/lib/extlib/symbol.rb +21 -0
  37. data/lib/extlib/time.rb +43 -0
  38. data/lib/extlib/virtual_file.rb +10 -0
  39. data/spec/array_spec.rb +39 -0
  40. data/spec/blank_spec.rb +85 -0
  41. data/spec/byte_array_spec.rb +7 -0
  42. data/spec/class_spec.rb +157 -0
  43. data/spec/datetime_spec.rb +22 -0
  44. data/spec/hash_spec.rb +537 -0
  45. data/spec/hook_spec.rb +1234 -0
  46. data/spec/inflection/plural_spec.rb +564 -0
  47. data/spec/inflection/singular_spec.rb +497 -0
  48. data/spec/inflection_extras_spec.rb +110 -0
  49. data/spec/lazy_array_spec.rb +1957 -0
  50. data/spec/lazy_module_spec.rb +38 -0
  51. data/spec/mash_spec.rb +311 -0
  52. data/spec/module_spec.rb +70 -0
  53. data/spec/object_space_spec.rb +9 -0
  54. data/spec/object_spec.rb +114 -0
  55. data/spec/pooling_spec.rb +511 -0
  56. data/spec/rcov.opts +6 -0
  57. data/spec/simple_set_spec.rb +57 -0
  58. data/spec/spec.opts +4 -0
  59. data/spec/spec_helper.rb +10 -0
  60. data/spec/string_spec.rb +221 -0
  61. data/spec/struct_spec.rb +12 -0
  62. data/spec/symbol_spec.rb +8 -0
  63. data/spec/time_spec.rb +29 -0
  64. data/spec/try_call_spec.rb +73 -0
  65. data/spec/try_dup_spec.rb +45 -0
  66. data/spec/virtual_file_spec.rb +21 -0
  67. data/tasks/ci.rake +1 -0
  68. data/tasks/metrics.rake +36 -0
  69. data/tasks/spec.rake +25 -0
  70. data/tasks/yard.rake +9 -0
  71. data/tasks/yardstick.rake +19 -0
  72. metadata +180 -0
@@ -0,0 +1,17 @@
1
+ class Struct
2
+ ##
3
+ # Get a hash with names and values of all instance variables.
4
+ #
5
+ # class Foo < Struct.new(:name, :age, :gender); end
6
+ # f = Foo.new("Jill", 50, :female)
7
+ # f.attributes #=> {:name => "Jill", :age => 50, :gender => :female}
8
+ #
9
+ # @return [Hash] Hash of instance variables in receiver, keyed by ivar name
10
+ #
11
+ # @api public
12
+ def attributes
13
+ h = {}
14
+ each_pair { |k,v| h[k] = v }
15
+ h
16
+ end
17
+ end # class Struct
@@ -0,0 +1,21 @@
1
+ class Symbol
2
+
3
+ def try_dup
4
+ self
5
+ end
6
+
7
+ ##
8
+ # Join with _o_ as a file path
9
+ #
10
+ # :merb/"core_ext" #=> "merb/core_ext"
11
+ # :merb / :core_ext / :string #=> "merb/core_ext/string"
12
+ #
13
+ # @param [#to_s] o The path component(s) to append.
14
+ #
15
+ # @return [String] The receiver (as path string), concatenated with _o_.
16
+ #
17
+ # @api public
18
+ def /(o)
19
+ File.join(self.to_s, o.to_s)
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ require "date"
2
+
3
+ class Time
4
+
5
+ ##
6
+ # Convert to ISO 8601 representation
7
+ #
8
+ # Time.now.to_json #=> "\"2008-03-28T17:54:20-05:00\""
9
+ #
10
+ # @return [String]
11
+ # ISO 8601 compatible representation of the Time object.
12
+ #
13
+ # @api public
14
+ def to_json(*)
15
+ self.xmlschema.to_json
16
+ end
17
+
18
+ ##
19
+ # Return receiver (for DateTime/Time conversion protocol).
20
+ #
21
+ # Time.now.to_time #=> Wed Nov 19 20:08:28 -0800 2008
22
+ #
23
+ # @return [Time] Receiver
24
+ #
25
+ # @api public
26
+ remove_method :to_time if instance_methods(false).any? { |m| m.to_sym == :to_time }
27
+ def to_time
28
+ self
29
+ end
30
+
31
+ ##
32
+ # Convert to DateTime (for DateTime/Time conversion protocol).
33
+ #
34
+ # Time.now.to_datetime #=> #<DateTime: 106046956823/43200,-1/3,2299161>
35
+ #
36
+ # @return [DateTime] DateTime object representing the same moment as receiver
37
+ #
38
+ # @api public
39
+ remove_method :to_datetime if instance_methods(false).any? { |m| m.to_sym == :to_datetime }
40
+ def to_datetime
41
+ DateTime.new(year, month, day, hour, min, sec, Rational(gmt_offset, 24 * 3600))
42
+ end
43
+ end
@@ -0,0 +1,10 @@
1
+ require "stringio"
2
+
3
+ # To use as a parameter to Merb::Template.inline_template
4
+ class VirtualFile < StringIO
5
+ attr_accessor :path
6
+ def initialize(string, path)
7
+ super(string)
8
+ @path = path
9
+ end
10
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe Array do
4
+ before :all do
5
+ @array = [ [ :a, [ 1 ] ], [ :b, [ 2 ] ], [ :c, [ 3 ] ] ].freeze
6
+ end
7
+
8
+ it { @array.should respond_to(:to_hash) }
9
+
10
+ describe '#to_hash' do
11
+ before :all do
12
+ @return = @array.to_hash
13
+ end
14
+
15
+ it 'should return a Hash' do
16
+ @return.should be_kind_of(Hash)
17
+ end
18
+
19
+ it 'should return expected value' do
20
+ @return.should == { :a => [ 1 ], :b => [ 2 ], :c => [ 3 ] }
21
+ end
22
+ end
23
+
24
+ it { @array.should respond_to(:to_mash) }
25
+
26
+ describe '#to_mash' do
27
+ before :all do
28
+ @return = @array.to_mash
29
+ end
30
+
31
+ it 'should return a Mash' do
32
+ @return.should be_kind_of(Mash)
33
+ end
34
+
35
+ it 'should return expected value' do
36
+ @return.should == { 'a' => [ 1 ], 'b' => [ 2 ], 'c' => [ 3 ] }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,85 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe Object do
4
+ it 'should provide blank?' do
5
+ Object.new.should respond_to(:blank?)
6
+ end
7
+
8
+ it 'should be blank if it is nil' do
9
+ object = Object.new
10
+ class << object
11
+ def nil?; true end
12
+ end
13
+ object.should be_blank
14
+ end
15
+
16
+ it 'should be blank if it is empty' do
17
+ {}.should be_blank
18
+ [].should be_blank
19
+ end
20
+
21
+ it 'should not be blank if not nil or empty' do
22
+ Object.new.should_not be_blank
23
+ [nil].should_not be_blank
24
+ { nil => 0 }.should_not be_blank
25
+ end
26
+ end
27
+
28
+ describe Numeric do
29
+ it 'should provide blank?' do
30
+ 1.should respond_to(:blank?)
31
+ end
32
+
33
+ it 'should never be blank' do
34
+ 1.should_not be_blank
35
+ end
36
+ end
37
+
38
+ describe NilClass do
39
+ it 'should provide blank?' do
40
+ nil.should respond_to(:blank?)
41
+ end
42
+
43
+ it 'should always be blank' do
44
+ nil.should be_blank
45
+ end
46
+ end
47
+
48
+ describe TrueClass do
49
+ it 'should provide blank?' do
50
+ true.should respond_to(:blank?)
51
+ end
52
+
53
+ it 'should never be blank' do
54
+ true.should_not be_blank
55
+ end
56
+ end
57
+
58
+ describe FalseClass do
59
+ it 'should provide blank?' do
60
+ false.should respond_to(:blank?)
61
+ end
62
+
63
+ it 'should always be blank' do
64
+ false.should be_blank
65
+ end
66
+ end
67
+
68
+ describe String do
69
+ it 'should provide blank?' do
70
+ 'string'.should respond_to(:blank?)
71
+ end
72
+
73
+ it 'should be blank if empty' do
74
+ ''.should be_blank
75
+ end
76
+
77
+ it 'should be blank if it only contains whitespace' do
78
+ ' '.should be_blank
79
+ " \r \n \t ".should be_blank
80
+ end
81
+
82
+ it 'should not be blank if it contains non-whitespace' do
83
+ ' a '.should_not be_blank
84
+ end
85
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe Extlib::ByteArray do
4
+ it 'should be a String' do
5
+ Extlib::ByteArray.new.should be_kind_of(String)
6
+ end
7
+ end
@@ -0,0 +1,157 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ class Grandparent
4
+ end
5
+
6
+ class Parent < Grandparent
7
+ end
8
+
9
+ class Child < Parent
10
+ end
11
+
12
+ class Parent
13
+ end
14
+
15
+ class Grandparent
16
+ class_inheritable_accessor :last_name, :_attribute
17
+
18
+ self._attribute = 1900
19
+ end
20
+
21
+ class ClassWithInheritableSymbolAccessor
22
+ class_inheritable_accessor :symbol
23
+ self.symbol = :foo
24
+ end
25
+
26
+ class ClassInheritingSymbolAccessor < ClassWithInheritableSymbolAccessor
27
+ end
28
+
29
+ describe Class, "#inheritable_accessor" do
30
+
31
+ after :each do
32
+ Grandparent.send(:remove_instance_variable, "@last_name") rescue nil
33
+ Parent.send(:remove_instance_variable, "@last_name") rescue nil
34
+ Child.send(:remove_instance_variable, "@last_name") rescue nil
35
+ end
36
+
37
+ it 'inherits from parent unless overriden' do
38
+ Parent._attribute.should == 1900
39
+ Child._attribute.should == 1900
40
+ end
41
+
42
+ it 'inherits from grandparent unless overriden' do
43
+ Child._attribute.should == 1900
44
+ end
45
+
46
+ it "inherits even if the accessor is made after the inheritance" do
47
+ Grandparent.last_name = "Merb"
48
+ Parent.last_name.should == "Merb"
49
+ Child.last_name.should == "Merb"
50
+ end
51
+
52
+ it "supports ||= to change a child" do
53
+ Parent.last_name ||= "Merb"
54
+ Grandparent.last_name.should == nil
55
+ Parent.last_name.should == "Merb"
56
+ Child.last_name.should == "Merb"
57
+ end
58
+
59
+ it "supports << to change a child when the parent is an Array" do
60
+ Grandparent.last_name = ["Merb"]
61
+ Parent.last_name << "Core"
62
+ Grandparent.last_name.should == ["Merb"]
63
+ Parent.last_name.should == ["Merb", "Core"]
64
+ end
65
+
66
+ it "supports ! methods on an Array" do
67
+ Grandparent.last_name = %w(Merb Core)
68
+ Parent.last_name.reverse!
69
+ Grandparent.last_name.should == %w(Merb Core)
70
+ Parent.last_name.should == %w(Core Merb)
71
+ end
72
+
73
+ it "support modifying a parent Hash" do
74
+ Grandparent.last_name = {"Merb" => "name"}
75
+ Parent.last_name["Core"] = "name"
76
+ Parent.last_name.should == {"Merb" => "name", "Core" => "name"}
77
+ Grandparent.last_name.should == {"Merb" => "name"}
78
+ end
79
+
80
+ it "supports hard-merging a parent Hash" do
81
+ Grandparent.last_name = {"Merb" => "name"}
82
+ Parent.last_name.merge!("Core" => "name")
83
+ Parent.last_name.should == {"Merb" => "name", "Core" => "name"}
84
+ Grandparent.last_name.should == {"Merb" => "name"}
85
+ end
86
+
87
+ it "supports changes to the parent even if the child has already been read" do
88
+ Child.last_name
89
+ Grandparent.last_name = "Merb"
90
+ Child.last_name.should == "Merb"
91
+ end
92
+
93
+ it "handles nil being set midstream" do
94
+ Child.last_name
95
+ Parent.last_name = nil
96
+ Grandparent.last_name = "Merb"
97
+ Child.last_name.should == nil
98
+ end
99
+
100
+ it "handles false being used in Parent" do
101
+ Child.last_name
102
+ Parent.last_name = false
103
+ Grandparent.last_name = "Merb"
104
+ Child.last_name.should == false
105
+ end
106
+
107
+ it "handles the grandparent changing the value (as long as the child isn't read first)" do
108
+ Grandparent.last_name = "Merb"
109
+ Grandparent.last_name = "Core"
110
+ Child.last_name.should == "Core"
111
+ end
112
+
113
+ end
114
+
115
+ describe Class, "#inheritable_accessor (of type Symbol)" do
116
+
117
+ it "should not raise" do
118
+ lambda { ClassInheritingSymbolAccessor.symbol }.should_not raise_error(TypeError)
119
+ end
120
+
121
+ end
122
+
123
+ #
124
+ # The bug that prompted this estoric spec was found in
125
+ # the wild when using dm-is-versioned with c_i_w.
126
+ #
127
+
128
+ module Plugin
129
+ def self.included(base)
130
+ base.class_eval do
131
+ class_inheritable_writer :plugin_options
132
+ class_inheritable_reader :plugin_options
133
+ self.plugin_options = :foo
134
+ end
135
+ end
136
+ end
137
+
138
+ class Model
139
+ def self.new
140
+ model = Class.new
141
+ model.send(:include, Plugin)
142
+ model
143
+ end
144
+
145
+ include Plugin
146
+ self.const_set("Version", Model.new)
147
+ end
148
+
149
+ describe Class, "#inheritable_accessor" do
150
+ it "uses object_id for comparison" do
151
+ Model.methods.map { |m| m.to_sym }.should be_include(:plugin_options)
152
+ Model.plugin_options.should == :foo
153
+
154
+ Model::Version.methods.map { |m| m.to_sym }.should be_include(:plugin_options)
155
+ Model::Version.plugin_options.should == :foo
156
+ end
157
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+ require 'json'
3
+
4
+ describe DateTime, "#to_time" do
5
+ before do
6
+ @expected = Time.now.to_s
7
+ @datetime = DateTime.parse(@expected)
8
+ end
9
+
10
+ it "should return a copy of time" do
11
+ time = @datetime.to_time
12
+ time.class.should == Time
13
+ time.to_s.should == @expected
14
+ end
15
+ end
16
+
17
+ describe Time, "#to_datetime" do
18
+ it "should return a copy of its self" do
19
+ datetime = DateTime.now
20
+ datetime.to_datetime.should == datetime
21
+ end
22
+ end
@@ -0,0 +1,537 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+ require "date"
3
+ require 'bigdecimal'
4
+
5
+ describe Hash, "environmentize_keys!" do
6
+ it "should transform keys to uppercase text" do
7
+ { :test_1 => 'test', 'test_2' => 'test', 1 => 'test' }.environmentize_keys!.should ==
8
+ { 'TEST_1' => 'test', 'TEST_2' => 'test', '1' => 'test' }
9
+ end
10
+
11
+ it "should only transform one level of keys" do
12
+ { :test_1 => { :test2 => 'test'} }.environmentize_keys!.should ==
13
+ { 'TEST_1' => { :test2 => 'test'} }
14
+ end
15
+ end
16
+
17
+
18
+ describe Hash, "only" do
19
+ before do
20
+ @hash = { :one => 'ONE', 'two' => 'TWO', 3 => 'THREE', 4 => nil }
21
+ end
22
+
23
+ it "should return a hash with only the given key(s)" do
24
+ @hash.only(:not_in_there).should == {}
25
+ @hash.only(4).should == {4 => nil}
26
+ @hash.only(:one).should == { :one => 'ONE' }
27
+ @hash.only(:one, 3).should == { :one => 'ONE', 3 => 'THREE' }
28
+ end
29
+ end
30
+
31
+
32
+ describe Hash, "except" do
33
+ before do
34
+ @hash = { :one => 'ONE', 'two' => 'TWO', 3 => 'THREE' }
35
+ end
36
+
37
+ it "should return a hash without only the given key(s)" do
38
+ @hash.except(:one).should == { 'two' => 'TWO', 3 => 'THREE' }
39
+ @hash.except(:one, 3).should == { 'two' => 'TWO' }
40
+ end
41
+ end
42
+
43
+
44
+ describe Hash, "to_xml_attributes" do
45
+ before do
46
+ @hash = { :one => "ONE", "two" => "TWO" }
47
+ end
48
+
49
+ it "should turn the hash into xml attributes" do
50
+ attrs = @hash.to_xml_attributes
51
+ attrs.should match(/one="ONE"/m)
52
+ attrs.should match(/two="TWO"/m)
53
+ end
54
+
55
+ it 'should preserve _ in hash keys' do
56
+ attrs = {
57
+ :some_long_attribute => "with short value",
58
+ :crash => :burn,
59
+ :merb => "uses extlib"
60
+ }.to_xml_attributes
61
+
62
+ attrs.should =~ /some_long_attribute="with short value"/
63
+ attrs.should =~ /merb="uses extlib"/
64
+ attrs.should =~ /crash="burn"/
65
+ end
66
+ end
67
+
68
+
69
+ describe Hash, "from_xml" do
70
+ it "should transform a simple tag with content" do
71
+ xml = "<tag>This is the contents</tag>"
72
+ Hash.from_xml(xml).should == { 'tag' => 'This is the contents' }
73
+ end
74
+
75
+ it "should work with cdata tags" do
76
+ xml = <<-END
77
+ <tag>
78
+ <![CDATA[
79
+ text inside cdata
80
+ ]]>
81
+ </tag>
82
+ END
83
+ Hash.from_xml(xml)["tag"].strip.should == "text inside cdata"
84
+ end
85
+
86
+ it "should transform a simple tag with attributes" do
87
+ xml = "<tag attr1='1' attr2='2'></tag>"
88
+ hash = { 'tag' => { 'attr1' => '1', 'attr2' => '2' } }
89
+ Hash.from_xml(xml).should == hash
90
+ end
91
+
92
+ it "should transform repeating siblings into an array" do
93
+ xml =<<-XML
94
+ <opt>
95
+ <user login="grep" fullname="Gary R Epstein" />
96
+ <user login="stty" fullname="Simon T Tyson" />
97
+ </opt>
98
+ XML
99
+
100
+ Hash.from_xml(xml)['opt']['user'].should be_an_instance_of(Array)
101
+
102
+ hash = {
103
+ 'opt' => {
104
+ 'user' => [{
105
+ 'login' => 'grep',
106
+ 'fullname' => 'Gary R Epstein'
107
+ },{
108
+ 'login' => 'stty',
109
+ 'fullname' => 'Simon T Tyson'
110
+ }]
111
+ }
112
+ }
113
+
114
+ Hash.from_xml(xml).should == hash
115
+ end
116
+
117
+ it "should not transform non-repeating siblings into an array" do
118
+ xml =<<-XML
119
+ <opt>
120
+ <user login="grep" fullname="Gary R Epstein" />
121
+ </opt>
122
+ XML
123
+
124
+ Hash.from_xml(xml)['opt']['user'].should be_an_instance_of(Hash)
125
+
126
+ hash = {
127
+ 'opt' => {
128
+ 'user' => {
129
+ 'login' => 'grep',
130
+ 'fullname' => 'Gary R Epstein'
131
+ }
132
+ }
133
+ }
134
+
135
+ Hash.from_xml(xml).should == hash
136
+ end
137
+
138
+ it "should typecast an integer" do
139
+ xml = "<tag type='integer'>10</tag>"
140
+ Hash.from_xml(xml)['tag'].should == 10
141
+ end
142
+
143
+ it "should typecast a true boolean" do
144
+ xml = "<tag type='boolean'>true</tag>"
145
+ Hash.from_xml(xml)['tag'].should be_true
146
+ end
147
+
148
+ it "should typecast a false boolean" do
149
+ ["false"].each do |w|
150
+ Hash.from_xml("<tag type='boolean'>#{w}</tag>")['tag'].should be_false
151
+ end
152
+ end
153
+
154
+ it "should typecast a datetime" do
155
+ xml = "<tag type='datetime'>2007-12-31 10:32</tag>"
156
+ Hash.from_xml(xml)['tag'].should == Time.parse( '2007-12-31 10:32' ).utc
157
+ end
158
+
159
+ it "should typecast a date" do
160
+ xml = "<tag type='date'>2007-12-31</tag>"
161
+ Hash.from_xml(xml)['tag'].should == Date.parse('2007-12-31')
162
+ end
163
+
164
+ it "should unescape html entities" do
165
+ values = {
166
+ "<" => "&lt;",
167
+ ">" => "&gt;",
168
+ '"' => "&quot;",
169
+ "'" => "&apos;",
170
+ "&" => "&amp;"
171
+ }
172
+ values.each do |k,v|
173
+ xml = "<tag>Some content #{v}</tag>"
174
+ Hash.from_xml(xml)['tag'].should match(Regexp.new(k))
175
+ end
176
+ end
177
+
178
+ it "should undasherize keys as tags" do
179
+ xml = "<tag-1>Stuff</tag-1>"
180
+ Hash.from_xml(xml).should have_key('tag_1')
181
+ end
182
+
183
+ it "should undasherize keys as attributes" do
184
+ xml = "<tag1 attr-1='1'></tag1>"
185
+ Hash.from_xml(xml)['tag1'].should have_key('attr_1')
186
+ end
187
+
188
+ it "should undasherize keys as tags and attributes" do
189
+ xml = "<tag-1 attr-1='1'></tag-1>"
190
+ Hash.from_xml(xml).should have_key('tag_1' )
191
+ Hash.from_xml(xml)['tag_1'].should have_key('attr_1')
192
+ end
193
+
194
+ it "should render nested content correctly" do
195
+ xml = "<root><tag1>Tag1 Content <em><strong>This is strong</strong></em></tag1></root>"
196
+ Hash.from_xml(xml)['root']['tag1'].should == "Tag1 Content <em><strong>This is strong</strong></em>"
197
+ end
198
+
199
+ it "should render nested content with split text nodes correctly" do
200
+ xml = "<root>Tag1 Content<em>Stuff</em> Hi There</root>"
201
+ Hash.from_xml(xml)['root'].should == "Tag1 Content<em>Stuff</em> Hi There"
202
+ end
203
+
204
+ it "should ignore attributes when a child is a text node" do
205
+ xml = "<root attr1='1'>Stuff</root>"
206
+ Hash.from_xml(xml).should == { "root" => "Stuff" }
207
+ end
208
+
209
+ it "should ignore attributes when any child is a text node" do
210
+ xml = "<root attr1='1'>Stuff <em>in italics</em></root>"
211
+ Hash.from_xml(xml).should == { "root" => "Stuff <em>in italics</em>" }
212
+ end
213
+
214
+ it "should correctly transform multiple children" do
215
+ xml = <<-XML
216
+ <user gender='m'>
217
+ <age type='integer'>35</age>
218
+ <name>Home Simpson</name>
219
+ <dob type='date'>1988-01-01</dob>
220
+ <joined-at type='datetime'>2000-04-28 23:01</joined-at>
221
+ <is-cool type='boolean'>true</is-cool>
222
+ </user>
223
+ XML
224
+
225
+ hash = {
226
+ "user" => {
227
+ "gender" => "m",
228
+ "age" => 35,
229
+ "name" => "Home Simpson",
230
+ "dob" => Date.parse('1988-01-01'),
231
+ "joined_at" => Time.parse("2000-04-28 23:01"),
232
+ "is_cool" => true
233
+ }
234
+ }
235
+
236
+ Hash.from_xml(xml).should == hash
237
+ end
238
+
239
+ it "should properly handle nil values (ActiveSupport Compatible)" do
240
+ topic_xml = <<-EOT
241
+ <topic>
242
+ <title></title>
243
+ <id type="integer"></id>
244
+ <approved type="boolean"></approved>
245
+ <written-on type="date"></written-on>
246
+ <viewed-at type="datetime"></viewed-at>
247
+ <content type="yaml"></content>
248
+ <parent-id></parent-id>
249
+ </topic>
250
+ EOT
251
+
252
+ expected_topic_hash = {
253
+ 'title' => nil,
254
+ 'id' => nil,
255
+ 'approved' => nil,
256
+ 'written_on' => nil,
257
+ 'viewed_at' => nil,
258
+ 'content' => nil,
259
+ 'parent_id' => nil
260
+ }
261
+ Hash.from_xml(topic_xml)["topic"].should == expected_topic_hash
262
+ end
263
+
264
+ it "should handle a single record from xml (ActiveSupport Compatible)" do
265
+ topic_xml = <<-EOT
266
+ <topic>
267
+ <title>The First Topic</title>
268
+ <author-name>David</author-name>
269
+ <id type="integer">1</id>
270
+ <approved type="boolean"> true </approved>
271
+ <replies-count type="integer">0</replies-count>
272
+ <replies-close-in type="integer">2592000000</replies-close-in>
273
+ <written-on type="date">2003-07-16</written-on>
274
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
275
+ <content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</content>
276
+ <author-email-address>david@loudthinking.com</author-email-address>
277
+ <parent-id></parent-id>
278
+ <ad-revenue type="decimal">1.5</ad-revenue>
279
+ <optimum-viewing-angle type="float">135</optimum-viewing-angle>
280
+ <resident type="symbol">yes</resident>
281
+ </topic>
282
+ EOT
283
+
284
+ expected_topic_hash = {
285
+ 'title' => "The First Topic",
286
+ 'author_name' => "David",
287
+ 'id' => 1,
288
+ 'approved' => true,
289
+ 'replies_count' => 0,
290
+ 'replies_close_in' => 2592000000,
291
+ 'written_on' => Date.new(2003, 7, 16),
292
+ 'viewed_at' => Time.utc(2003, 7, 16, 9, 28),
293
+ # Changed this line where the key is :message. The yaml specifies this as a symbol, and who am I to change what you specify
294
+ # The line in ActiveSupport is
295
+ # 'content' => { 'message' => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
296
+ 'content' => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
297
+ 'author_email_address' => "david@loudthinking.com",
298
+ 'parent_id' => nil,
299
+ 'ad_revenue' => BigDecimal("1.50"),
300
+ 'optimum_viewing_angle' => 135.0,
301
+ 'resident' => :yes
302
+ }
303
+
304
+ Hash.from_xml(topic_xml)["topic"].each do |k,v|
305
+ v.should == expected_topic_hash[k]
306
+ end
307
+ end
308
+
309
+ it "should handle multiple records (ActiveSupport Compatible)" do
310
+ topics_xml = <<-EOT
311
+ <topics type="array">
312
+ <topic>
313
+ <title>The First Topic</title>
314
+ <author-name>David</author-name>
315
+ <id type="integer">1</id>
316
+ <approved type="boolean">false</approved>
317
+ <replies-count type="integer">0</replies-count>
318
+ <replies-close-in type="integer">2592000000</replies-close-in>
319
+ <written-on type="date">2003-07-16</written-on>
320
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
321
+ <content>Have a nice day</content>
322
+ <author-email-address>david@loudthinking.com</author-email-address>
323
+ <parent-id nil="true"></parent-id>
324
+ </topic>
325
+ <topic>
326
+ <title>The Second Topic</title>
327
+ <author-name>Jason</author-name>
328
+ <id type="integer">1</id>
329
+ <approved type="boolean">false</approved>
330
+ <replies-count type="integer">0</replies-count>
331
+ <replies-close-in type="integer">2592000000</replies-close-in>
332
+ <written-on type="date">2003-07-16</written-on>
333
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
334
+ <content>Have a nice day</content>
335
+ <author-email-address>david@loudthinking.com</author-email-address>
336
+ <parent-id></parent-id>
337
+ </topic>
338
+ </topics>
339
+ EOT
340
+
341
+ expected_topic_hash = {
342
+ 'title' => "The First Topic",
343
+ 'author_name' => "David",
344
+ 'id' => 1,
345
+ 'approved' => false,
346
+ 'replies_count' => 0,
347
+ 'replies_close_in' => 2592000000,
348
+ 'written_on' => Date.new(2003, 7, 16),
349
+ 'viewed_at' => Time.utc(2003, 7, 16, 9, 28),
350
+ 'content' => "Have a nice day",
351
+ 'author_email_address' => "david@loudthinking.com",
352
+ 'parent_id' => nil
353
+ }
354
+ # puts Hash.from_xml(topics_xml)['topics'].first.inspect
355
+ Hash.from_xml(topics_xml)["topics"].first.each do |k,v|
356
+ v.should == expected_topic_hash[k]
357
+ end
358
+ end
359
+
360
+ it "should handle a single record from_xml with attributes other than type (ActiveSupport Compatible)" do
361
+ topic_xml = <<-EOT
362
+ <rsp stat="ok">
363
+ <photos page="1" pages="1" perpage="100" total="16">
364
+ <photo id="175756086" owner="55569174@N00" secret="0279bf37a1" server="76" title="Colored Pencil PhotoBooth Fun" ispublic="1" isfriend="0" isfamily="0"/>
365
+ </photos>
366
+ </rsp>
367
+ EOT
368
+
369
+ expected_topic_hash = {
370
+ 'id' => "175756086",
371
+ 'owner' => "55569174@N00",
372
+ 'secret' => "0279bf37a1",
373
+ 'server' => "76",
374
+ 'title' => "Colored Pencil PhotoBooth Fun",
375
+ 'ispublic' => "1",
376
+ 'isfriend' => "0",
377
+ 'isfamily' => "0",
378
+ }
379
+ Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"].each do |k,v|
380
+ v.should == expected_topic_hash[k]
381
+ end
382
+ end
383
+
384
+ it "should handle an emtpy array (ActiveSupport Compatible)" do
385
+ blog_xml = <<-XML
386
+ <blog>
387
+ <posts type="array"></posts>
388
+ </blog>
389
+ XML
390
+ expected_blog_hash = {"blog" => {"posts" => []}}
391
+ Hash.from_xml(blog_xml).should == expected_blog_hash
392
+ end
393
+
394
+ it "should handle empty array with whitespace from xml (ActiveSupport Compatible)" do
395
+ blog_xml = <<-XML
396
+ <blog>
397
+ <posts type="array">
398
+ </posts>
399
+ </blog>
400
+ XML
401
+ expected_blog_hash = {"blog" => {"posts" => []}}
402
+ Hash.from_xml(blog_xml).should == expected_blog_hash
403
+ end
404
+
405
+ it "should handle array with one entry from_xml (ActiveSupport Compatible)" do
406
+ blog_xml = <<-XML
407
+ <blog>
408
+ <posts type="array">
409
+ <post>a post</post>
410
+ </posts>
411
+ </blog>
412
+ XML
413
+ expected_blog_hash = {"blog" => {"posts" => ["a post"]}}
414
+ Hash.from_xml(blog_xml).should == expected_blog_hash
415
+ end
416
+
417
+ it "should handle array with multiple entries from xml (ActiveSupport Compatible)" do
418
+ blog_xml = <<-XML
419
+ <blog>
420
+ <posts type="array">
421
+ <post>a post</post>
422
+ <post>another post</post>
423
+ </posts>
424
+ </blog>
425
+ XML
426
+ expected_blog_hash = {"blog" => {"posts" => ["a post", "another post"]}}
427
+ Hash.from_xml(blog_xml).should == expected_blog_hash
428
+ end
429
+
430
+ it "should handle file types (ActiveSupport Compatible)" do
431
+ blog_xml = <<-XML
432
+ <blog>
433
+ <logo type="file" name="logo.png" content_type="image/png">
434
+ </logo>
435
+ </blog>
436
+ XML
437
+ hash = Hash.from_xml(blog_xml)
438
+ hash.should have_key('blog')
439
+ hash['blog'].should have_key('logo')
440
+
441
+ file = hash['blog']['logo']
442
+ file.original_filename.should == 'logo.png'
443
+ file.content_type.should == 'image/png'
444
+ end
445
+
446
+ it "should handle file from xml with defaults (ActiveSupport Compatible)" do
447
+ blog_xml = <<-XML
448
+ <blog>
449
+ <logo type="file">
450
+ </logo>
451
+ </blog>
452
+ XML
453
+ file = Hash.from_xml(blog_xml)['blog']['logo']
454
+ file.original_filename.should == 'untitled'
455
+ file.content_type.should == 'application/octet-stream'
456
+ end
457
+
458
+ it "should handle xsd like types from xml (ActiveSupport Compatible)" do
459
+ bacon_xml = <<-EOT
460
+ <bacon>
461
+ <weight type="double">0.5</weight>
462
+ <price type="decimal">12.50</price>
463
+ <chunky type="boolean"> 1 </chunky>
464
+ <expires-at type="dateTime">2007-12-25T12:34:56+0000</expires-at>
465
+ <notes type="string"></notes>
466
+ <illustration type="base64Binary">YmFiZS5wbmc=</illustration>
467
+ </bacon>
468
+ EOT
469
+
470
+ expected_bacon_hash = {
471
+ 'weight' => 0.5,
472
+ 'chunky' => true,
473
+ 'price' => BigDecimal("12.50"),
474
+ 'expires_at' => Time.utc(2007,12,25,12,34,56),
475
+ 'notes' => "",
476
+ 'illustration' => "babe.png"
477
+ }
478
+
479
+ Hash.from_xml(bacon_xml)["bacon"].should == expected_bacon_hash
480
+ end
481
+
482
+ it "should let type trickle through when unknown (ActiveSupport Compatible)" do
483
+ product_xml = <<-EOT
484
+ <product>
485
+ <weight type="double">0.5</weight>
486
+ <image type="ProductImage"><filename>image.gif</filename></image>
487
+
488
+ </product>
489
+ EOT
490
+
491
+ expected_product_hash = {
492
+ 'weight' => 0.5,
493
+ 'image' => {'type' => 'ProductImage', 'filename' => 'image.gif' },
494
+ }
495
+
496
+ Hash.from_xml(product_xml)["product"].should == expected_product_hash
497
+ end
498
+
499
+ it "should handle unescaping from xml (ActiveResource Compatible)" do
500
+ xml_string = '<person><bare-string>First &amp; Last Name</bare-string><pre-escaped-string>First &amp;amp; Last Name</pre-escaped-string></person>'
501
+ expected_hash = {
502
+ 'bare_string' => 'First & Last Name',
503
+ 'pre_escaped_string' => 'First &amp; Last Name'
504
+ }
505
+
506
+ Hash.from_xml(xml_string)['person'].should == expected_hash
507
+ end
508
+ end
509
+
510
+ describe Hash, 'to_params' do
511
+ {
512
+ { "foo" => "bar", "baz" => "bat" } => "foo=bar&baz=bat",
513
+ { "foo" => [ "bar", "baz" ] } => "foo[]=bar&foo[]=baz",
514
+ { "foo" => [ {"bar" => "1"}, {"bar" => 2} ] } => "foo[][bar]=1&foo[][bar]=2",
515
+ { "foo" => { "bar" => [ {"baz" => 1}, {"baz" => "2"} ] } } => "foo[bar][][baz]=1&foo[bar][][baz]=2",
516
+ { "foo" => {"1" => "bar", "2" => "baz"} } => "foo[1]=bar&foo[2]=baz"
517
+ }.each do |hash, params|
518
+ it "should covert hash: #{hash.inspect} to params: #{params.inspect}" do
519
+ hash.to_params.split('&').sort.should == params.split('&').sort
520
+ end
521
+ end
522
+
523
+ it 'should not leave a trailing &' do
524
+ { :name => 'Bob', :address => { :street => '111 Ruby Ave.', :city => 'Ruby Central', :phones => ['111-111-1111', '222-222-2222'] } }.to_params.should_not match(/&$/)
525
+ end
526
+ end
527
+
528
+ describe Hash, 'to_mash' do
529
+ before :each do
530
+ @hash = Hash.new(10)
531
+ end
532
+
533
+ it "copies default Hash value to Mash" do
534
+ @mash = @hash.to_mash
535
+ @mash[:merb].should == 10
536
+ end
537
+ end