oha_extensions 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +60 -0
- data/Rakefile +2 -0
- data/lib/oha_extensions/array_extension.rb +111 -0
- data/lib/oha_extensions/hash_extension.rb +159 -0
- data/lib/oha_extensions/object_extension.rb +11 -0
- data/lib/oha_extensions/version.rb +3 -0
- data/lib/oha_extensions.rb +17 -0
- data/oha_extensions.gemspec +21 -0
- data/test/oha_extensions/array_extension_test.rb +373 -0
- data/test/oha_extensions/hash_extension_test.rb +211 -0
- data/test/test_helper.rb +19 -0
- metadata +110 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Bookrenter/Rafter
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# OhaExtensions
|
2
|
+
|
3
|
+
Gem adds convenience methods listed below to Object, Hash and Array classes.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'oha_extensions'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install oha_extensions
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
***
|
22
|
+
### Object extension methods:
|
23
|
+
|
24
|
+
* - has_additional_functionality_in(*files)
|
25
|
+
* - send_if_respond_to(method, *args)
|
26
|
+
|
27
|
+
***
|
28
|
+
### Hash extension methods:
|
29
|
+
|
30
|
+
* - sum(&block)
|
31
|
+
* - increment(key, amount=1, &block)
|
32
|
+
* - percent(key, &block)
|
33
|
+
* - assert_required_keys(*required_keys)
|
34
|
+
* - select_pairs(&block)
|
35
|
+
* - Hash.from_xml_string(s, options = {})
|
36
|
+
|
37
|
+
***
|
38
|
+
### Array extension methods:
|
39
|
+
|
40
|
+
* - stats
|
41
|
+
* - average
|
42
|
+
* - process_in_batches(batch_size)
|
43
|
+
* - to_hash_with_keys(options={}, &block)
|
44
|
+
* - to_lookup_hash()
|
45
|
+
* - to_identity_hash(id_proc = nil)
|
46
|
+
* - rand
|
47
|
+
* - next
|
48
|
+
* - shuffle
|
49
|
+
* - delete(first_element)
|
50
|
+
|
51
|
+
***
|
52
|
+
# Credits
|
53
|
+
|
54
|
+
[Oha_extensions](https://github.com/bkr/oha_extensions) is maintained by [Bookrenter/Rafter](http://github.com/bkr) and is funded by [BookRenter.com](http://www.bookrenter.com "BookRenter.com").
|
55
|
+
|
56
|
+
![BookRenter.com Logo](http://assets0.bookrenter.com/images/header/bookrenter_logo.gif "BookRenter.com")
|
57
|
+
|
58
|
+
# Copyright
|
59
|
+
|
60
|
+
Copyright (c) 2012 Bookrenter.com. See LICENSE.txt for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
#This class adds the stats methods to array which computes sum, min, max, med, mean, count, std deviation
|
2
|
+
module ArrayExtension
|
3
|
+
#Given an array of values it returns the mean, median, min, max in a hash
|
4
|
+
#*array*:: An array whose values are used to calculate the various statistics
|
5
|
+
#returns:: A hash with the following keys
|
6
|
+
#* sum
|
7
|
+
#* mean
|
8
|
+
#* min
|
9
|
+
#* max
|
10
|
+
#* median
|
11
|
+
#* count (number of items in the array)
|
12
|
+
#<b> All keys will => 0, if the array is empty</b>
|
13
|
+
#<b> The array is automatically sorted when you call stats</b>
|
14
|
+
def stats
|
15
|
+
return {:sum => 0, :mean => 0, :min => 0, :max => 0, :median => 0, :std_dev => 0, :count => 0} if empty?
|
16
|
+
out = Hash.new
|
17
|
+
sort!
|
18
|
+
out[:sum] = inject(0){|sum, element| sum + element}
|
19
|
+
out[:mean] = out[:sum]/size.to_f
|
20
|
+
out[:min] = min
|
21
|
+
out[:max] = max
|
22
|
+
out[:median] = size % 2 == 0 ? (self[size/2] + self[size/2 - 1]) / 2.0 : self[size/2]
|
23
|
+
out[:count] = size
|
24
|
+
if size > 1
|
25
|
+
out[:std_dev] = Math.sqrt((inject(0){|sum_square, element| sum_square += element * element} - out[:sum]*out[:mean]) / (size - 1))
|
26
|
+
else
|
27
|
+
out[:std_dev] = 0
|
28
|
+
end
|
29
|
+
return out
|
30
|
+
end
|
31
|
+
|
32
|
+
#returns:: The average value of the elements in the array
|
33
|
+
def average
|
34
|
+
return stats[:sum] / size.to_f
|
35
|
+
end
|
36
|
+
|
37
|
+
#Divides the calling array into +batch_size+ batches, (the last batch could be less than +batch_size+ if the calling array.size % batch_size != 0) and yields each batch
|
38
|
+
#*batch_size*:: The number elements to yield to the given block
|
39
|
+
#Example:
|
40
|
+
# (1..30).to_a.process_in_batches(10) do |batch|
|
41
|
+
# p batch
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
45
|
+
# [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
46
|
+
# [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
|
47
|
+
def process_in_batches(batch_size)
|
48
|
+
raise ArgumentError if batch_size.nil? or batch_size <= 0
|
49
|
+
|
50
|
+
index = 0
|
51
|
+
|
52
|
+
while index < self.size
|
53
|
+
yield self[index...index+batch_size]
|
54
|
+
index += batch_size
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_hash_with_keys(options={}, &block)
|
59
|
+
options = {:value_acts_as_array => false}.merge(options)
|
60
|
+
return self.inject(Hash.new) do |injection, var|
|
61
|
+
key = block.call(var)
|
62
|
+
if options[:value_acts_as_array]
|
63
|
+
if injection[key].nil?
|
64
|
+
injection[key] = [var]
|
65
|
+
else
|
66
|
+
injection[key] << var
|
67
|
+
end
|
68
|
+
else
|
69
|
+
injection[key] = var
|
70
|
+
end
|
71
|
+
|
72
|
+
injection
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_lookup_hash()
|
77
|
+
h = Hash.new
|
78
|
+
self.each do |e|
|
79
|
+
h[e] = block_given? ? yield(e) : true
|
80
|
+
end
|
81
|
+
return h
|
82
|
+
end
|
83
|
+
|
84
|
+
# useful for creating in memory mappings of ActiveRecord models
|
85
|
+
def to_identity_hash(id_proc = nil)
|
86
|
+
h = Hash.new
|
87
|
+
self.each do |e|
|
88
|
+
key = id_proc ? id_proc.call(e) : (block_given? ? yield(e) : e.id)
|
89
|
+
h[key] = e
|
90
|
+
end
|
91
|
+
return h
|
92
|
+
end
|
93
|
+
|
94
|
+
def rand
|
95
|
+
self[Kernel.rand(self.length)]
|
96
|
+
end
|
97
|
+
|
98
|
+
def next
|
99
|
+
@next_index = @next_index.nil? ? 0 : (@next_index + 1) % self.size
|
100
|
+
return self[@next_index]
|
101
|
+
end
|
102
|
+
|
103
|
+
def shuffle
|
104
|
+
self.sort_by{ Kernel.rand }
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete_first(element)
|
108
|
+
index = self.index(element)
|
109
|
+
self.delete_at(index) if index
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module HashExtension
|
4
|
+
|
5
|
+
#returns:: The sum of each element in the hash
|
6
|
+
#An optional block can be given which takes two parameters, key and value for each element in the hash. The return value of the block is summed for each element in the hash.
|
7
|
+
#If the result of sum is nil, 0 is returned
|
8
|
+
#Examples:
|
9
|
+
# {:a => 1, :b => 2, :c => 3}.sum #returns 6
|
10
|
+
# {:a => ['foo', 1], :b => ['monkey', 2], :c => ['bunny', 3]}.sum { |k, v| v[1] } #returns 6
|
11
|
+
# {:a => ['foo', 1], :b => ['monkey', 2], :c => ['bunny', 3]}.sum { |k, v| v } #returns ['foo', 1, 'monkey', 2, 'bunny', 3] <b>Order of elements is NOT guaranteed</b>
|
12
|
+
# {}.sum #returns 0
|
13
|
+
def sum(&block)
|
14
|
+
block = lambda { |k, v| v } if block.nil?
|
15
|
+
|
16
|
+
total = nil
|
17
|
+
|
18
|
+
each do |k, v|
|
19
|
+
total.nil? ? total = block.call(k, v) : total += block.call(k, v)
|
20
|
+
end
|
21
|
+
|
22
|
+
return total || 0
|
23
|
+
end
|
24
|
+
|
25
|
+
#Increments the value pointed to from the key
|
26
|
+
#If the key is nil then it is initialized to amount
|
27
|
+
#An optional block may be given, which is passed +key+, and each key in the hash. The block should return true for the key you wish to increment.
|
28
|
+
#Examples:
|
29
|
+
# h = Hash.new #h['foo'] = nil
|
30
|
+
# h.increment('foo') #1
|
31
|
+
# h.increment('foo') #2
|
32
|
+
#
|
33
|
+
# age = Hash.new
|
34
|
+
# age['0 - 18'] = 0
|
35
|
+
# age['18 - 24'] = 0
|
36
|
+
# age['24 - 99999'] = 0
|
37
|
+
#
|
38
|
+
# #increment the key where 18 is in range of the min and max
|
39
|
+
# age.increment(18) do |v, e|
|
40
|
+
# min, max = e.split(' - ')
|
41
|
+
# v >= min.to_i && v < max.to_i
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# assert_equal(0, age['0 - 18'])
|
45
|
+
# assert_equal(1, age['18 - 24'])
|
46
|
+
# assert_equal(0, age['24 - 99999'])
|
47
|
+
def increment(key, amount=1, &block)
|
48
|
+
if block.nil?
|
49
|
+
key_to_use = key
|
50
|
+
else
|
51
|
+
key_to_use = self.keys.detect { |k| block.call(key, k) }
|
52
|
+
end
|
53
|
+
|
54
|
+
if self[key_to_use].nil?
|
55
|
+
self[key_to_use] = amount
|
56
|
+
else
|
57
|
+
self[key_to_use] += amount
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
#returns:: What percent of the sum of the hash is the value for +key+
|
62
|
+
#Examples:
|
63
|
+
# {:a => 1, :b => 2, :c => 3, :d => 4}.percent(:c) #0.30 because 3 is 30% of 10 and 10 is the total of the hash
|
64
|
+
# {:a => ['foo', 1], :b => ['monkey', 2], :c => ['bunny', 3], :d => ['kitty', 4]}.percent(:c) { |e| e[1] } #0.30
|
65
|
+
# {:a => 0} #0.0, if the sum of the hash is 0, then 0 is returned
|
66
|
+
def percent(key, &block)
|
67
|
+
block = lambda { |v| v } if block.nil?
|
68
|
+
sum_block = lambda { |k, v| block.call(v) }
|
69
|
+
|
70
|
+
total = self.sum(&sum_block)
|
71
|
+
return 0 if total == 0
|
72
|
+
return block.call(self[key]) / total.to_f
|
73
|
+
end
|
74
|
+
|
75
|
+
#If keys does not include everything in required_keys, throw an ArgumentError.
|
76
|
+
#
|
77
|
+
#*required_keys*: list of keys that must be present for the hash (if a required key hashes to nil, but is passed it is still valid.
|
78
|
+
#Example::
|
79
|
+
# {:happy => nil}.assert_required_keys(:happy) #will not raise error
|
80
|
+
#required_keys may be passed as strings, symbols or both. Likewise the keys in the has maybe a strings or symbols or both.
|
81
|
+
#Example::
|
82
|
+
# {:happy => 'bunny'}.assert_required_keys(:happy) #will not raise error
|
83
|
+
# {'happy' => 'bunny'}.assert_required_keys(:happy) #will not raise error
|
84
|
+
#====Raises
|
85
|
+
#*ArgumentError*:: if a required key is missing
|
86
|
+
def assert_required_keys(*required_keys)
|
87
|
+
keys_not_passed = [required_keys].flatten.map { |required_key| required_key.to_s } - self.stringify_keys.keys
|
88
|
+
raise(ArgumentError, "The following keys were nil when expected not to be: #{keys_not_passed.join(", ")}") unless keys_not_passed.empty?
|
89
|
+
end
|
90
|
+
alias_method :must_include, :assert_required_keys
|
91
|
+
|
92
|
+
|
93
|
+
def select_pairs(&block)
|
94
|
+
return Hash[*self.select(&block).flatten]
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.included(base)
|
98
|
+
base.extend ClassMethods
|
99
|
+
end
|
100
|
+
|
101
|
+
module ClassMethods
|
102
|
+
# Generates a hash from XML. This is specifically being used generate JSON for the API. It makes the assumption that elements contain arrays
|
103
|
+
# must be explicitly specified. Assuming otherwise generates larger JSON.
|
104
|
+
# This also utilizes XML attributes which Hash.from_xml ignores.
|
105
|
+
def from_xml_string(s, options = {})
|
106
|
+
options = {:array_nodes => [], :parent_array_nodes => []}.merge(options)
|
107
|
+
return node_to_hash(Nokogiri.parse(s).children.first, options)
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def node_to_hash(node, options)
|
113
|
+
children = node.children.select {|n| n.element?}
|
114
|
+
node_name = node.name
|
115
|
+
hash = {}
|
116
|
+
|
117
|
+
if ! node.attributes.empty?
|
118
|
+
hash[node_name] = {}
|
119
|
+
node.attributes.each do |attr_name, attr_value|
|
120
|
+
hash[node_name][attr_name] = attr_value.to_s
|
121
|
+
end
|
122
|
+
content_hash = hash[node_name]
|
123
|
+
# this is the name of the key for the sub elements, could also use something like 'content'
|
124
|
+
node_name = node.name
|
125
|
+
else
|
126
|
+
content_hash = hash
|
127
|
+
end
|
128
|
+
|
129
|
+
if children.empty?
|
130
|
+
nc = node.children.detect{|n| n.text?}
|
131
|
+
content_hash[node_name] = nc.nil? ? nil : nc.text
|
132
|
+
|
133
|
+
if options[:parent_array_nodes].include?(node_name)
|
134
|
+
content_hash[node_name] = []
|
135
|
+
end
|
136
|
+
else
|
137
|
+
children_hash = children.inject({}) do |acc, node|
|
138
|
+
child_hash = node_to_hash(node, options)
|
139
|
+
child_hash.each do |key, value|
|
140
|
+
if options[:array_nodes].include?(key)
|
141
|
+
acc[key] = [] if acc[key].nil?
|
142
|
+
acc[key] << value
|
143
|
+
else
|
144
|
+
acc[key] = value if acc[key].nil?
|
145
|
+
end
|
146
|
+
end
|
147
|
+
acc
|
148
|
+
end
|
149
|
+
content_hash[node_name] = children_hash
|
150
|
+
|
151
|
+
if options[:parent_array_nodes].include?(node_name)
|
152
|
+
content_hash[node_name] = children_hash.values.flatten
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
return hash
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module ObjectExtension
|
2
|
+
def has_additional_functionality_in(*files)
|
3
|
+
files.each do |file|
|
4
|
+
require_dependency "#{name.underscore}_extensions/#{file}"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def send_if_respond_to(method, *args)
|
9
|
+
self.respond_to?(method) ? self.send(method, *args) : nil
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "oha_extensions/version"
|
2
|
+
require "oha_extensions/hash_extension"
|
3
|
+
require "oha_extensions/object_extension"
|
4
|
+
require "oha_extensions/array_extension"
|
5
|
+
|
6
|
+
class Array
|
7
|
+
include ArrayExtension
|
8
|
+
end
|
9
|
+
|
10
|
+
class Hash
|
11
|
+
include HashExtension
|
12
|
+
end
|
13
|
+
|
14
|
+
class Object
|
15
|
+
include ObjectExtension
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/oha_extensions/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Bookrenter/Rafter"]
|
6
|
+
gem.email = ["cp@bookrenter.com"]
|
7
|
+
gem.description = %q{Extends Object, Hash and Array classes with additional methods.}
|
8
|
+
gem.summary = %q{Additional methods for Object, Hash, Array classes.}
|
9
|
+
gem.homepage = "https://github.com/bkr/oha_extensions"
|
10
|
+
|
11
|
+
gem.add_development_dependency('mocha', '> 0')
|
12
|
+
gem.add_development_dependency('shoulda', "> 0")
|
13
|
+
gem.add_dependency('nokogiri', '> 0')
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($\)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.name = "oha_extensions"
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.version = OhaExtensions::VERSION
|
21
|
+
end
|
@@ -0,0 +1,373 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ArrayExtensionTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_array_stats
|
9
|
+
#= Test Empty
|
10
|
+
stats = [].stats
|
11
|
+
stats.values.all? { |e| e == 0 }
|
12
|
+
|
13
|
+
#== Only 0s
|
14
|
+
stats = Array[0].stats
|
15
|
+
assert_equal 0, stats[:sum]
|
16
|
+
assert_equal 0, stats[:mean]
|
17
|
+
assert_equal 0, stats[:min]
|
18
|
+
assert_equal 0, stats[:max]
|
19
|
+
assert_equal 0, stats[:median]
|
20
|
+
assert_equal 1, stats[:count]
|
21
|
+
assert_equal 0, stats[:std_dev]
|
22
|
+
|
23
|
+
stats = Array[0, 0, 0].stats
|
24
|
+
assert_equal 0, stats[:sum]
|
25
|
+
assert_equal 0, stats[:mean]
|
26
|
+
assert_equal 0, stats[:min]
|
27
|
+
assert_equal 0, stats[:max]
|
28
|
+
assert_equal 0, stats[:median]
|
29
|
+
assert_equal 3, stats[:count]
|
30
|
+
assert_equal 0, stats[:std_dev]
|
31
|
+
|
32
|
+
#== Integers
|
33
|
+
#==== simple in order (even size)
|
34
|
+
stats = Array[0, 1, 2, 3].stats
|
35
|
+
assert_equal 6, stats[:sum]
|
36
|
+
assert_in_delta(1.5, stats[:mean], 2 ** -20)
|
37
|
+
assert_equal 0, stats[:min]
|
38
|
+
assert_equal 3, stats[:max]
|
39
|
+
assert_in_delta(1.5, stats[:median], 2 ** -20)
|
40
|
+
assert_equal 4, stats[:count]
|
41
|
+
assert_in_delta(1.29,stats[:std_dev], 0.1)
|
42
|
+
|
43
|
+
#==== simple out of order (odd size)
|
44
|
+
stats = Array[0, 1, 2, 3, 1].stats
|
45
|
+
assert_equal 7, stats[:sum]
|
46
|
+
assert_in_delta(1.4, stats[:mean], 2 ** -20)
|
47
|
+
assert_equal 0, stats[:min]
|
48
|
+
assert_equal 3, stats[:max]
|
49
|
+
assert_in_delta(1, stats[:median], 2 ** -20)
|
50
|
+
assert_equal 5, stats[:count]
|
51
|
+
assert_in_delta(1.14,stats[:std_dev], 0.1)
|
52
|
+
|
53
|
+
#==== 0 sum with negatives
|
54
|
+
stats = Array[-99, 99, 0].stats
|
55
|
+
assert_equal 0, stats[:sum]
|
56
|
+
assert_in_delta(0.0, stats[:mean], 2 ** -20)
|
57
|
+
assert_equal -99, stats[:min]
|
58
|
+
assert_equal 99, stats[:max]
|
59
|
+
assert_in_delta(0.0, stats[:median], 2 ** -20)
|
60
|
+
assert_equal 3, stats[:count]
|
61
|
+
assert_in_delta(99, stats[:std_dev], 0.1)
|
62
|
+
|
63
|
+
#==== Random
|
64
|
+
a = rand(1000) - rand(1000)
|
65
|
+
b = rand(1000) - rand(1000)
|
66
|
+
c = rand(1000) - rand(1000)
|
67
|
+
input = [a, b, c]
|
68
|
+
stats = input.stats
|
69
|
+
assert_in_delta((a + b + c), stats[:sum], 2 ** -20)
|
70
|
+
assert_in_delta((a + b + c)/3.0, stats[:mean], 2 ** -20)
|
71
|
+
assert_equal input.min, stats[:min]
|
72
|
+
assert_equal input.max, stats[:max]
|
73
|
+
assert_in_delta(input[1], stats[:median], 2 ** -20)
|
74
|
+
assert_equal 3, stats[:count]
|
75
|
+
|
76
|
+
#== Floats
|
77
|
+
#==== simple in order (even size)
|
78
|
+
stats = Array[0.0, 1.4, 2.567, 3.8].stats
|
79
|
+
assert_in_delta(7.767, stats[:sum], 2 ** -20)
|
80
|
+
assert_in_delta(1.94175, stats[:mean], 2 ** -20)
|
81
|
+
assert_in_delta(0.0, stats[:min], 2 ** -20)
|
82
|
+
assert_in_delta(3.8, stats[:max] , 2 ** -20)
|
83
|
+
assert_in_delta(1.9835, stats[:median], 2 ** -20)
|
84
|
+
assert_equal 4, stats[:count]
|
85
|
+
assert_in_delta(1.62, stats[:std_dev], 0.1)
|
86
|
+
|
87
|
+
#==== simple out of order (odd size)
|
88
|
+
stats = Array[0.0, 1.4, 2.567, 3.8, 1.378].stats
|
89
|
+
assert_in_delta(9.145, stats[:sum], 2 ** -20)
|
90
|
+
assert_in_delta(1.829, stats[:mean], 2 ** -20)
|
91
|
+
assert_in_delta(0.0, stats[:min], 2 ** -20)
|
92
|
+
assert_in_delta(3.8, stats[:max] , 2 ** -20)
|
93
|
+
assert_in_delta(1.4, stats[:median], 2 ** -20)
|
94
|
+
assert_equal 5, stats[:count]
|
95
|
+
assert_in_delta(1.428, stats[:std_dev], 0.1)
|
96
|
+
|
97
|
+
|
98
|
+
#==== 0 sum with negatives
|
99
|
+
stats = Array[-99.88, 99.88, 0].stats
|
100
|
+
assert_in_delta(0.0, stats[:sum], 2 ** -20)
|
101
|
+
assert_in_delta(0.0, stats[:mean], 2 ** -20)
|
102
|
+
assert_in_delta(-99.88, stats[:min], 2 ** -20)
|
103
|
+
assert_in_delta(99.88, stats[:max] , 2 ** -20)
|
104
|
+
assert_in_delta(0.0, stats[:median], 2 ** -20)
|
105
|
+
assert_equal 3, stats[:count]
|
106
|
+
assert_in_delta(99.88, stats[:std_dev], 0.1)
|
107
|
+
|
108
|
+
|
109
|
+
#==== Random
|
110
|
+
a = rand() * rand(1000) - rand() * rand(1000)
|
111
|
+
b = rand() * rand(1000) - rand() * rand(1000)
|
112
|
+
c = rand() * rand(1000) - rand() * rand(1000)
|
113
|
+
input = [a, b, c]
|
114
|
+
stats = input.stats
|
115
|
+
assert_in_delta((a + b + c), stats[:sum], 2 ** -20)
|
116
|
+
assert_in_delta((a + b + c)/3.0, stats[:mean], 2 ** -20)
|
117
|
+
assert_in_delta(input.min, stats[:min], 2 ** -20)
|
118
|
+
assert_in_delta(input.max, stats[:max] , 2 ** -20)
|
119
|
+
assert_in_delta(input[1], stats[:median], 2 ** -20)
|
120
|
+
assert_equal 3, stats[:count]
|
121
|
+
|
122
|
+
#== Mixed
|
123
|
+
a = rand() * rand(1000) - rand() * rand(1000)
|
124
|
+
b = rand() * rand(1000) - rand() * rand(1000)
|
125
|
+
c = rand() * rand(1000) - rand() * rand(1000)
|
126
|
+
d = rand(1000) - rand(1000)
|
127
|
+
e = rand(1000) - rand(1000)
|
128
|
+
f = rand(1000) - rand(1000)
|
129
|
+
input = [a, b, c, d, e, f]
|
130
|
+
stats = input.stats
|
131
|
+
assert_in_delta((a + b + c + d + e + f), stats[:sum], 2 ** -20)
|
132
|
+
assert_in_delta((a + b + c + d + e + f)/6.0, stats[:mean], 2 ** -20)
|
133
|
+
assert_in_delta(input.min, stats[:min], 2 ** -20)
|
134
|
+
assert_in_delta(input.max, stats[:max] , 2 ** -20)
|
135
|
+
assert_in_delta((input[3] + input[2])/2.0, stats[:median], 2 ** -20)
|
136
|
+
assert_equal 6, stats[:count]
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_average
|
140
|
+
assert_equal 0, [-1, 0, 1].average
|
141
|
+
assert_equal 1, [1].average
|
142
|
+
assert_equal 46.5, [10, 45, 34, 97].average
|
143
|
+
assert_equal 66.065, [34.7, 97.43].average
|
144
|
+
|
145
|
+
a = rand() * rand(1000) - rand() * rand(1000)
|
146
|
+
b = rand() * rand(1000) - rand() * rand(1000)
|
147
|
+
c = rand() * rand(1000) - rand() * rand(1000)
|
148
|
+
d = rand(1000) - rand(1000)
|
149
|
+
e = rand(1000) - rand(1000)
|
150
|
+
f = rand(1000) - rand(1000)
|
151
|
+
input = [a, b, c, d, e, f]
|
152
|
+
assert_in_delta((a + b + c + d + e + f)/6.0, input.average, 2 ** -20)
|
153
|
+
end
|
154
|
+
|
155
|
+
#======================================================================
|
156
|
+
#====================== Test process_in_batches =======================
|
157
|
+
#======================================================================
|
158
|
+
def setup_process_in_batches_tests
|
159
|
+
@array = %W[one two three four five six seven eight nine ten]
|
160
|
+
@empty = Array.new
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_process_in_batches_missing_batch_size_argument
|
164
|
+
setup_process_in_batches_tests
|
165
|
+
assert_raise(ArgumentError) { @array.process_in_batches }
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_process_in_batches_empty_array
|
169
|
+
setup_process_in_batches_tests
|
170
|
+
assert_nil @empty.process_in_batches(3)
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_process_in_batches_batch_size_less_than_or_equal_to_zero
|
174
|
+
setup_process_in_batches_tests
|
175
|
+
assert_raise(ArgumentError) { @array.process_in_batches(0) }
|
176
|
+
assert_raise(ArgumentError) { @array.process_in_batches(-3) }
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_process_in_batches_batch_size_less_than_array_size
|
180
|
+
setup_process_in_batches_tests
|
181
|
+
|
182
|
+
#should get three batchs; two with 4 elements and one with 2 elements
|
183
|
+
batch_size = 4
|
184
|
+
batch_cnt = 0
|
185
|
+
|
186
|
+
@array.process_in_batches(batch_size) do |batch|
|
187
|
+
case batch_cnt
|
188
|
+
when 0, 1
|
189
|
+
assert_equal batch_size, batch.size
|
190
|
+
when 2
|
191
|
+
assert_equal 2, batch.size
|
192
|
+
end
|
193
|
+
batch_cnt += 1
|
194
|
+
end
|
195
|
+
|
196
|
+
assert_equal 3, batch_cnt
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_process_in_batches_batch_size_greater_than_array_size
|
200
|
+
setup_process_in_batches_tests
|
201
|
+
|
202
|
+
#should get one batch only
|
203
|
+
batch_size = @array.size + 1
|
204
|
+
batch_cnt = 0
|
205
|
+
|
206
|
+
@array.process_in_batches(batch_size) do |batch|
|
207
|
+
batch_cnt += 1
|
208
|
+
assert_equal @array.size, batch.size
|
209
|
+
end
|
210
|
+
assert_equal 1, batch_cnt
|
211
|
+
end
|
212
|
+
|
213
|
+
def test_process_in_batches_batch_size_equal_to_array_size
|
214
|
+
setup_process_in_batches_tests
|
215
|
+
|
216
|
+
#should get one batch only
|
217
|
+
batch_size = @array.size
|
218
|
+
batch_cnt = 0
|
219
|
+
|
220
|
+
@array.process_in_batches(batch_size) do |batch|
|
221
|
+
batch_cnt += 1
|
222
|
+
assert_equal @array.size, batch.size
|
223
|
+
end
|
224
|
+
assert_equal 1, batch_cnt
|
225
|
+
end
|
226
|
+
#======================================================================
|
227
|
+
#==================== End test process_in_batches =====================
|
228
|
+
#======================================================================
|
229
|
+
|
230
|
+
def test_to_hash_with_keys
|
231
|
+
assert_equal({}, [].to_hash_with_keys { |e| e })
|
232
|
+
|
233
|
+
actual = [1, 2, 3, 4].to_hash_with_keys { |e| e + 10 }
|
234
|
+
expected = {11 => 1, 12 => 2, 13 => 3, 14 => 4}
|
235
|
+
assert_equal(expected, actual)
|
236
|
+
|
237
|
+
actual = [{:msg => 'apples'}, {:msg => 'foos'}, {:msg => 'cows'}].to_hash_with_keys { |e| "i_like_#{e[:msg]}".to_sym }
|
238
|
+
expected = { :i_like_apples => {:msg => 'apples'}, :i_like_foos => {:msg => 'foos'}, :i_like_cows => {:msg => 'cows'} }
|
239
|
+
assert_equal(expected, actual)
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_to_lookup_hash
|
243
|
+
assert_equal({}, [].to_lookup_hash)
|
244
|
+
assert_equal({1 => true, 2 => true, 3 => true}, [1, 2, 3].to_lookup_hash)
|
245
|
+
assert_equal({1 => 2, 2 => 3, 3 => 4}, [1, 2, 3].to_lookup_hash {|e| e + 1})
|
246
|
+
end
|
247
|
+
|
248
|
+
context "to_identity_hash" do
|
249
|
+
should "use id attribute as key with no args" do
|
250
|
+
o1 = mock
|
251
|
+
o1.stubs(:id).returns(123)
|
252
|
+
o2 = mock
|
253
|
+
o2.stubs(:id).returns(456)
|
254
|
+
o3 = mock
|
255
|
+
o3.stubs(:id).returns(789)
|
256
|
+
assert_equal({o1.id => o1, o2.id => o2, o3.id => o3}, [o1, o2, o3].to_identity_hash)
|
257
|
+
end
|
258
|
+
|
259
|
+
should "allow symbol proc as the first argument for key creation" do
|
260
|
+
o1 = mock
|
261
|
+
o1.stubs(:foo).returns(123)
|
262
|
+
o2 = mock
|
263
|
+
o2.stubs(:foo).returns(456)
|
264
|
+
o3 = mock
|
265
|
+
o3.stubs(:foo).returns(789)
|
266
|
+
assert_equal({o1.foo => o1, o2.foo => o2, o3.foo => o3}, [o1, o2, o3].to_identity_hash(&:foo))
|
267
|
+
end
|
268
|
+
|
269
|
+
should "allow proc to specify key" do
|
270
|
+
o1 = mock
|
271
|
+
o1.stubs(:foo).returns(123)
|
272
|
+
o2 = mock
|
273
|
+
o2.stubs(:foo).returns(456)
|
274
|
+
o3 = mock
|
275
|
+
o3.stubs(:foo).returns(789)
|
276
|
+
assert_equal({o1.foo => o1, o2.foo => o2, o3.foo => o3}, [o1, o2, o3].to_identity_hash {|e| e.foo})
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
context "rand" do
|
282
|
+
context "empty array" do
|
283
|
+
setup do
|
284
|
+
@execute_result = [].rand
|
285
|
+
end
|
286
|
+
|
287
|
+
should "be nil" do
|
288
|
+
assert_nil(@execute_result)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
context "array with 1 item" do
|
293
|
+
setup do
|
294
|
+
@execute_result = ['foo'].rand
|
295
|
+
end
|
296
|
+
|
297
|
+
should "be just that item" do
|
298
|
+
assert_equal 'foo', @execute_result
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context "array with multiple items" do
|
303
|
+
setup do
|
304
|
+
@array = ['a', 'b', 'c']
|
305
|
+
@execute_result = @array.rand
|
306
|
+
end
|
307
|
+
|
308
|
+
should "return an item from the array" do
|
309
|
+
assert_contains(@array, @execute_result)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
context "next" do
|
315
|
+
context "empty array" do
|
316
|
+
should "return nil" do
|
317
|
+
assert_equal nil, [].next
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
context "single element" do
|
322
|
+
setup do
|
323
|
+
@element = 1
|
324
|
+
@array = [@element]
|
325
|
+
end
|
326
|
+
|
327
|
+
should "always return only element" do
|
328
|
+
assert_equal @element, @array.next
|
329
|
+
assert_equal @element, @array.next
|
330
|
+
assert_equal @element, @array.next
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
context "multiple elements" do
|
335
|
+
setup do
|
336
|
+
@array = [1, 'a']
|
337
|
+
end
|
338
|
+
|
339
|
+
should "loop" do
|
340
|
+
assert_equal 1, @array.next
|
341
|
+
assert_equal 'a', @array.next
|
342
|
+
assert_equal 1, @array.next
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context "delete_first" do
|
348
|
+
context "empty array" do
|
349
|
+
should "return nil" do
|
350
|
+
assert_equal nil, [].delete_first("a")
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
context "multiple elements" do
|
355
|
+
setup do
|
356
|
+
@ary = [1,'a',3,'a']
|
357
|
+
end
|
358
|
+
|
359
|
+
should "delete first element found" do
|
360
|
+
deleted = @ary.delete_first('a')
|
361
|
+
assert_equal 'a', deleted
|
362
|
+
assert_equal [1, 3, 'a'], @ary
|
363
|
+
end
|
364
|
+
|
365
|
+
should "return nil if nothing found" do
|
366
|
+
deleted = @ary.delete_first('b')
|
367
|
+
assert_equal nil, deleted
|
368
|
+
assert_equal [1, 'a', 3, 'a'], @ary
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class HashExtensionTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@simple = {:a => 1, :b => 2, :c => 3}
|
7
|
+
@simple_sum = 6
|
8
|
+
|
9
|
+
@zeros = {:zero => 0, :another_zero => 0, :yet_another_zero => 0}
|
10
|
+
|
11
|
+
@integers = {:an_integer => 48, :an_negative => -77, :zero => 0}
|
12
|
+
@integers_sum = -29
|
13
|
+
|
14
|
+
@floats = {:a_float => 24.5, :a_negative_float => -74.8, :zero => 0.0}
|
15
|
+
@floats_sum = -50.3
|
16
|
+
|
17
|
+
@mixed = @integers.merge(@floats)
|
18
|
+
@mixed_sum = @integers_sum + @floats_sum
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_empty
|
22
|
+
assert_equal 0, {}.sum
|
23
|
+
assert_equal 0, {}.sum { |k, v| v }
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_zeros
|
27
|
+
assert_equal 0, @zeros.sum
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_integers
|
31
|
+
assert_equal @integers_sum, @integers.sum
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_floats
|
35
|
+
assert_equal @floats_sum, @floats.sum
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_mixed
|
39
|
+
assert_equal @mixed_sum, @mixed.sum
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_block
|
43
|
+
test = {:a => ['foo', 48], :b => ['monkey', -99], :c => ['bunny', 34]}
|
44
|
+
|
45
|
+
#sum index 1 of each element
|
46
|
+
assert_equal -17, test.sum { |k, v| v[1] }
|
47
|
+
|
48
|
+
#sum index 0
|
49
|
+
assert_match 'foo', test.sum { |k, v| v[0] }
|
50
|
+
assert_match 'monkey', test.sum { |k, v| v[0] }
|
51
|
+
assert_match 'bunny', test.sum { |k, v| v[0] }
|
52
|
+
|
53
|
+
#sum key
|
54
|
+
assert_match 'a', test.sum { |k, v| k.to_s }
|
55
|
+
assert_match 'b', test.sum { |k, v| k.to_s }
|
56
|
+
assert_match 'c', test.sum { |k, v| k.to_s }
|
57
|
+
|
58
|
+
#sum entire array
|
59
|
+
assert_equal true, test.sum { |k, v| v }.any? { |e| e == 'foo' }
|
60
|
+
assert_equal true, test.sum { |k, v| v }.any? { |e| e == 'monkey' }
|
61
|
+
assert_equal true, test.sum { |k, v| v }.any? { |e| e == 'bunny' }
|
62
|
+
assert_equal true, test.sum { |k, v| v }.any? { |e| e == 48 }
|
63
|
+
assert_equal true, test.sum { |k, v| v }.any? { |e| e == -99 }
|
64
|
+
assert_equal true, test.sum { |k, v| v }.any? { |e| e == 34 }
|
65
|
+
|
66
|
+
#sum manipulating value
|
67
|
+
assert_equal 0, test.sum { |k, v| v[1] - v[1] }
|
68
|
+
assert_equal -34, test.sum { |k, v| v[1] * 2 }
|
69
|
+
assert_equal [], test.sum { |k, v| v - v }
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def test_increment
|
74
|
+
h = Hash.new
|
75
|
+
|
76
|
+
assert_equal nil, h['foo']
|
77
|
+
assert_equal 1, h.increment('foo')
|
78
|
+
assert_equal 2, h.increment('foo')
|
79
|
+
|
80
|
+
h = Hash.new
|
81
|
+
assert_equal 10, h.increment(['foo'], 10)
|
82
|
+
assert_equal 20, h.increment(['foo'], 10)
|
83
|
+
|
84
|
+
age = Hash.new
|
85
|
+
age['0 - 18'] = 0
|
86
|
+
age['18 - 24'] = 0
|
87
|
+
age['24 - 99999'] = 0
|
88
|
+
|
89
|
+
age.increment(18) do |v, e|
|
90
|
+
min, max = e.split(' - ')
|
91
|
+
v >= min.to_i && v < max.to_i
|
92
|
+
end
|
93
|
+
|
94
|
+
assert_equal(0, age['0 - 18'])
|
95
|
+
assert_equal(1, age['18 - 24'])
|
96
|
+
assert_equal(0, age['24 - 99999'])
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_percent
|
100
|
+
h = {'a' => 25, 'b' => 50, 'c' => 25}
|
101
|
+
assert_in_delta(0.25, h.percent('a'), 2 ** -20)
|
102
|
+
assert_in_delta(0.50, h.percent('b'), 2 ** -20)
|
103
|
+
assert_in_delta(0.25, h.percent('c'), 2 ** -20)
|
104
|
+
|
105
|
+
h = {'hot' => ['steamy', 3], 'hotter' => ['smoking', 4], 'hottest' => ['global warming', 7]}
|
106
|
+
assert_in_delta(0.21428571428571428571, h.percent('hot') { |v| v[1] }, 2 ** -20)
|
107
|
+
assert_in_delta(0.28571428571428571428, h.percent('hotter') { |v| v[1] }, 2 ** -20)
|
108
|
+
assert_in_delta(0.5, h.percent('hottest') { |v| v[1] }, 2 ** -20)
|
109
|
+
|
110
|
+
assert_raise(NoMethodError) { h.percent('cold') }
|
111
|
+
|
112
|
+
h = {'a' => 0}
|
113
|
+
assert_equal 0, h.percent('a')
|
114
|
+
end
|
115
|
+
|
116
|
+
context ".select_pairs" do
|
117
|
+
context "empty" do
|
118
|
+
setup do
|
119
|
+
@hash = {}
|
120
|
+
@execute_result = @hash.select_pairs { |k, v| v % 2 == 0 }
|
121
|
+
end
|
122
|
+
|
123
|
+
should "return empty hash" do
|
124
|
+
assert_equal({}, @execute_result)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "populated" do
|
129
|
+
setup do
|
130
|
+
@hash = {'A' => 1, 'B' => 2}
|
131
|
+
@execute_result = @hash.select_pairs { |k, v| v % 2 == 0 }
|
132
|
+
end
|
133
|
+
|
134
|
+
should "return hash where values are even" do
|
135
|
+
assert_equal({'B' => 2}, @execute_result)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context ".from_xml_string" do
|
141
|
+
should "handle empty root node" do
|
142
|
+
xml = '<a />'
|
143
|
+
expected = {'a' => nil}
|
144
|
+
assert_equal expected, Hash.from_xml_string(xml)
|
145
|
+
end
|
146
|
+
|
147
|
+
should "handle empty string root node" do
|
148
|
+
xml = '<a></a>'
|
149
|
+
expected = {'a' => nil}
|
150
|
+
assert_equal expected, Hash.from_xml_string(xml)
|
151
|
+
end
|
152
|
+
|
153
|
+
should "handle text string root node" do
|
154
|
+
xml = '<a>text</a>'
|
155
|
+
expected = {'a' => 'text'}
|
156
|
+
assert_equal expected, Hash.from_xml_string(xml)
|
157
|
+
end
|
158
|
+
|
159
|
+
should "handle attributes in root node" do
|
160
|
+
xml = '<a a1="aye1" />'
|
161
|
+
expected = {'a' => {'a1' => 'aye1', 'a' => nil}}
|
162
|
+
assert_equal expected, Hash.from_xml_string(xml)
|
163
|
+
end
|
164
|
+
|
165
|
+
should "handle element nodes" do
|
166
|
+
xml = '<a><b>value</b></a>'
|
167
|
+
expected = {'a' => {'b' => 'value'}}
|
168
|
+
assert_equal expected, Hash.from_xml_string(xml)
|
169
|
+
end
|
170
|
+
|
171
|
+
should "handle empty element nodes" do
|
172
|
+
xml = '<a><b/><c/></a>'
|
173
|
+
expected = {'a' => {'b' => nil, 'c' => nil}}
|
174
|
+
assert_equal expected, Hash.from_xml_string(xml)
|
175
|
+
end
|
176
|
+
|
177
|
+
should "handle attributes in nodes" do
|
178
|
+
xml = '<a a1="aye1" a2="aye2"><b b1="bee1"/><c c1="see1">see2</c></a>'
|
179
|
+
expected = {'a' => {'a1' => 'aye1', 'a2' => 'aye2', 'a' => {
|
180
|
+
'b' => {'b1' => 'bee1', 'b' => nil},
|
181
|
+
'c' => {'c1' => 'see1', 'c' => 'see2'}}}}
|
182
|
+
assert_equal expected, Hash.from_xml_string(xml)
|
183
|
+
end
|
184
|
+
|
185
|
+
should "flatten array nodes by default" do
|
186
|
+
xml = '<a><b>bee1</b><b>bee2</b><c /></a>'
|
187
|
+
expected = {'a' => {'b' => 'bee1', 'c' => nil}}
|
188
|
+
assert_equal expected, Hash.from_xml_string(xml)
|
189
|
+
end
|
190
|
+
|
191
|
+
context "with array nodes" do
|
192
|
+
should "not flatten array nodes when array_nodes specified" do
|
193
|
+
xml = '<a><b>bee1</b><b>bee2</b><c /></a>'
|
194
|
+
expected = {'a' => {'b' => ['bee1', 'bee2'], 'c' => nil}}
|
195
|
+
assert_equal expected, Hash.from_xml_string(xml, :array_nodes => ['b'])
|
196
|
+
end
|
197
|
+
|
198
|
+
should "not flatten array nodes and remove inner nodes when array_nodes and parent_array_nodes specified" do
|
199
|
+
xml = '<a><b>bee1</b><b>bee2</b><c>see</c></a>'
|
200
|
+
expected = {'a' => ['bee1', 'bee2', 'see']}
|
201
|
+
assert_equal expected, Hash.from_xml_string(xml, :array_nodes => ['b', 'c'], :parent_array_nodes => ['a'])
|
202
|
+
end
|
203
|
+
|
204
|
+
should "have empty array node when no child nodes present and parent_array_nodes specified" do
|
205
|
+
xml = '<a> \n </a>'
|
206
|
+
expected = {'a' => []}
|
207
|
+
assert_equal expected, Hash.from_xml_string(xml, :parent_array_nodes => ['a'])
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'test/unit'
|
12
|
+
require 'shoulda'
|
13
|
+
require 'mocha'
|
14
|
+
require File.dirname(__FILE__) + '/../lib/oha_extensions'
|
15
|
+
|
16
|
+
require 'oha_extensions'
|
17
|
+
|
18
|
+
class Test::Unit::TestCase
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oha_extensions
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bookrenter/Rafter
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mocha
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>'
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '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: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: shoulda
|
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
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: nokogiri
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>'
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>'
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Extends Object, Hash and Array classes with additional methods.
|
63
|
+
email:
|
64
|
+
- cp@bookrenter.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/oha_extensions.rb
|
75
|
+
- lib/oha_extensions/array_extension.rb
|
76
|
+
- lib/oha_extensions/hash_extension.rb
|
77
|
+
- lib/oha_extensions/object_extension.rb
|
78
|
+
- lib/oha_extensions/version.rb
|
79
|
+
- oha_extensions.gemspec
|
80
|
+
- test/oha_extensions/array_extension_test.rb
|
81
|
+
- test/oha_extensions/hash_extension_test.rb
|
82
|
+
- test/test_helper.rb
|
83
|
+
homepage: https://github.com/bkr/oha_extensions
|
84
|
+
licenses: []
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.8.24
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Additional methods for Object, Hash, Array classes.
|
107
|
+
test_files:
|
108
|
+
- test/oha_extensions/array_extension_test.rb
|
109
|
+
- test/oha_extensions/hash_extension_test.rb
|
110
|
+
- test/test_helper.rb
|