one-pivot 0.9.3 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source :rubygems
2
2
 
3
- gem 'shoulda', '2.11.3'
4
- gem 'yard', '0.6.4'
5
- gem 'bluecloth', '2.0.10'
6
-
3
+ gem 'eventmachine', '0.12.10'
7
4
 
5
+ group :test do
6
+ gem 'shoulda', '2.11.3'
7
+ end
@@ -1,14 +1,12 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- bluecloth (2.0.10)
4
+ eventmachine (0.12.10)
5
5
  shoulda (2.11.3)
6
- yard (0.6.4)
7
6
 
8
7
  PLATFORMS
9
8
  ruby
10
9
 
11
10
  DEPENDENCIES
12
- bluecloth (= 2.0.10)
11
+ eventmachine (= 0.12.10)
13
12
  shoulda (= 2.11.3)
14
- yard (= 0.6.4)
data/README.md CHANGED
@@ -15,130 +15,113 @@ Lets have a look at some examples.
15
15
  _Note: there are a few advanced features not demonstrated in the examples below. For example, adding identifiers to pivots or attaching observers to pivot operations. We use these features at 1on1 to cache pivot results for each item. This gives us a big performance boost when the same item participates in multiple pivots during its lifetime... especially when the pivot Proc is an expensive operation. Have a look at the [tests](https://github.com/one-on-one/pivot/tree/master/test) when you want to dig a little deeper._
16
16
 
17
17
  ##Installation
18
- <pre>
19
- <code>
20
- gem install one-pivot
21
- </code>
22
- </pre>
18
+ gem install one-pivot
23
19
 
24
20
  ##A simple single pivot
25
- <pre>
26
- <code>
27
- require 'one-pivot'
21
+ require 'one-pivot'
28
22
 
29
- # create the pivot instance
30
- pivoter = One::Pivoter.new
23
+ # create the pivot instance
24
+ pivoter = One::Pivoter.new
31
25
 
32
- # create a list of objects to pivot
33
- list = [1,2,3,4,5,6,7,8,9]
26
+ # create a list of objects to pivot
27
+ list = [1,2,3,4,5,6,7,8,9]
34
28
 
35
- # run a single pivot
36
- # note: the block passed to the pivot method is invoked for each item in the list
37
- # note: the result from the block will act as the 'key' in the resulting Hash
38
- result = pivoter.pivot(list) {|item| item <= 5}
39
-
40
- # 'result' will be a Hash with the following structure
41
- {
42
- true => [1,2,3,4,5],
43
- false => [6,7,8,9]
44
- }
45
- </code>
46
- </pre>
29
+ # run a single pivot
30
+ # note: the block passed to the pivot method is invoked for each item in the list
31
+ # note: the result from the block will act as the 'key' in the resulting Hash
32
+ result = pivoter.pivot(list) {|item| item <= 5}
47
33
 
34
+ # 'result' will be a Hash with the following structure
35
+ {
36
+ true => [1,2,3,4,5],
37
+ false => [6,7,8,9]
38
+ }
48
39
 
49
40
  ##A simple multi-pivot
50
- <pre>
51
- <code>
52
- require 'one-pivot'
41
+ require 'one-pivot'
53
42
 
54
- # create the pivot instance
55
- # note: the multi-pivot delimiter that was specified
56
- pivoter = One::Pivoter.new
43
+ # create the pivot instance
44
+ # note: the multi-pivot delimiter that was specified
45
+ pivoter = One::Pivoter.new
57
46
 
58
- # create a list of objects to pivot
59
- list = [1,2,3,4,5,6,7,8,9]
47
+ # create a list of objects to pivot
48
+ list = [1,2,3,4,5,6,7,8,9]
60
49
 
61
- # run several pivots together
62
- pivots = []
50
+ # run several pivots together
51
+ pivots = []
63
52
 
64
- pivots << lambda do |i|
65
- key = "less than or equal to 5" if i <= 5
66
- key ||= "greater than 5"
67
- end
53
+ pivots << lambda do |i|
54
+ key = "less than or equal to 5" if i <= 5
55
+ key ||= "greater than 5"
56
+ end
68
57
 
69
- pivots << lambda do |i|
70
- key = "greater than or equal to 3" if i >= 3
71
- key ||= "less than 3"
72
- end
58
+ pivots << lambda do |i|
59
+ key = "greater than or equal to 3" if i >= 3
60
+ key ||= "less than 3"
61
+ end
73
62
 
74
- pivots << {:delimiter => " & "}
63
+ pivots << {:delimiter => " & "}
75
64
 
76
- result = pivoter.multi_pivot(list, *pivots)
65
+ result = pivoter.multi_pivot(list, *pivots)
77
66
 
78
- # 'result' will be a Hash with the following structure
79
- {
80
- "less than or equal to 5 & greater than or equal to 3" => [3, 4, 5],
81
- "less than or equal to 5 & less than 3" => [1, 2],
82
- "greater than 5 & greater than or equal to 3" => [6, 7, 8, 9]
83
- }
84
- </code>
85
- </pre>
67
+ # 'result' will be a Hash with the following structure
68
+ {
69
+ "less than or equal to 5 & greater than or equal to 3" => [3, 4, 5],
70
+ "less than or equal to 5 & less than 3" => [1, 2],
71
+ "greater than 5 & greater than or equal to 3" => [6, 7, 8, 9]
72
+ }
86
73
 
87
74
  ##A real world example
88
- <pre>
89
- <code>
90
- # we'll be working with ActiveRecord objects based on the following schema
91
- # ============================================================================
92
- # create_table "users", :force => true do |t|
93
- # t.string "name"
94
- # t.datetime "created_at"
95
- # t.datetime "updated_at"
96
- # end
97
- #
98
- # create_table "skills", :force => true do |t|
99
- # t.string "name"
100
- # t.datetime "created_at"
101
- # t.datetime "updated_at"
102
- # end
103
- #
104
- # create_table "skills_users", :id => false, :force => true do |t|
105
- # t.integer "user_id"
106
- # t.integer "skill_id"
107
- # end
108
- #
109
- # and will seed the datbase with the following data
110
- # ============================================================================
111
- # ruby = Skill.create(:name => "Ruby")
112
- # python = Skill.create(:name => "Python")
113
- # php = Skill.create(:name => "PHP")
114
- # javascript = Skill.create(:name => "JavaScript")
115
- #
116
- # users = User.create([
117
- # {:name => "Ryan", :skills => [ruby]},
118
- # {:name => "Dave", :skills => [php, javascript]},
119
- # {:name => "Brett", :skills => [ruby, javascript]},
120
- # {:name => "Jay", :skills => [ruby, python, php, javascript]},
121
- # {:name => "Doug"}
122
- # ])
123
- #
124
-
125
- require 'one-pivot'
126
-
127
- pivoter = One::Pivoter.new
128
- # note that the pivot block returns an array
129
- # each unique value in the arrays returned will become a key in the resulting Hash
130
- result = pivoter.pivot(User.all) do |user|
131
- user.skills
132
- end
133
-
134
- # 'result' will be Hash with the following structure
135
- # note: I've simplified the object structure (using names only) for clarity
136
- {
137
- "Ruby" => ["Brett", "Jay", "Ryan"],
138
- "PHP" => ["Dave", "Jay"],
139
- "JavaScript" => ["Brett", "Dave", "Jay"],
140
- "Python" => ["Jay"],
141
- nil => ["Doug"]
142
- }
143
- </code>
144
- </pre>
75
+ # we'll be working with ActiveRecord objects based on the following schema
76
+ # ============================================================================
77
+ # create_table "users", :force => true do |t|
78
+ # t.string "name"
79
+ # t.datetime "created_at"
80
+ # t.datetime "updated_at"
81
+ # end
82
+ #
83
+ # create_table "skills", :force => true do |t|
84
+ # t.string "name"
85
+ # t.datetime "created_at"
86
+ # t.datetime "updated_at"
87
+ # end
88
+ #
89
+ # create_table "skills_users", :id => false, :force => true do |t|
90
+ # t.integer "user_id"
91
+ # t.integer "skill_id"
92
+ # end
93
+ #
94
+ # and will seed the datbase with the following data
95
+ # ============================================================================
96
+ # ruby = Skill.create(:name => "Ruby")
97
+ # python = Skill.create(:name => "Python")
98
+ # php = Skill.create(:name => "PHP")
99
+ # javascript = Skill.create(:name => "JavaScript")
100
+ #
101
+ # users = User.create([
102
+ # {:name => "Ryan", :skills => [ruby]},
103
+ # {:name => "Dave", :skills => [php, javascript]},
104
+ # {:name => "Brett", :skills => [ruby, javascript]},
105
+ # {:name => "Jay", :skills => [ruby, python, php, javascript]},
106
+ # {:name => "Doug"}
107
+ # ])
108
+ #
109
+
110
+ require 'one-pivot'
111
+
112
+ pivoter = One::Pivoter.new
113
+ # note that the pivot block returns an array
114
+ # each unique value in the arrays returned will become a key in the resulting Hash
115
+ result = pivoter.pivot(User.all) do |user|
116
+ user.skills
117
+ end
118
+
119
+ # 'result' will be Hash with the following structure
120
+ # note: I've simplified the object structure (using names only) for clarity
121
+ {
122
+ "Ruby" => ["Brett", "Jay", "Ryan"],
123
+ "PHP" => ["Dave", "Jay"],
124
+ "JavaScript" => ["Brett", "Dave", "Jay"],
125
+ "Python" => ["Jay"],
126
+ nil => ["Doug"]
127
+ }
@@ -1,2 +1,3 @@
1
+ require 'rubygems'
1
2
  require File.expand_path(File.join(File.dirname(__FILE__), 'one', 'pivot'))
2
3
  require File.expand_path(File.join(File.dirname(__FILE__), 'one', 'pivoter'))
@@ -1,4 +1,8 @@
1
+ require 'rubygems'
1
2
  require 'observer'
3
+ require 'eventmachine'
4
+ require 'thread'
5
+
2
6
 
3
7
  # Namespace module for One on One Marketing.
4
8
  module One
@@ -16,44 +20,77 @@ module One
16
20
  # # the result will be a Hash with the following structure
17
21
  # {
18
22
  # true=>[1, 2, 3, 4, 5],
19
- # false=>[6, 7, 8, 9]
23
+ # false=>[6, 7, 8, 9]
20
24
  # }
21
25
  #
22
26
  # @param [Array<Object>] list The list to pivot
23
27
  # @param [optional, Hash] options Options to pivot with
24
- # @option options [Object] :identifier A name that uniquely identifies the pivot operation
25
- # @yield [item] The block passed to pivot will be called for each item in the list
28
+ # @option options [Object] :identifier A name that uniquely identifies the pivot operation
29
+ # @yield [item] The block passed to pivot will be called for each item in the list
26
30
  # @yieldparam [Object] item An item in the list
27
31
  # @yieldreturn [Object] The value returned from the pivot block will serve as the key in the pivot results
28
32
  # @return [Hash] The pivoted results
29
33
  def pivot(list, options={}, &block)
30
34
  pivoted = {}
31
- list.each do |item|
32
- value = yield(item)
33
-
34
- # notify observers that a pivot block was just called
35
- identifier = options[:identifier] || "#{item.hash}:#{block.hash}"
36
- changed
37
- notify_observers(identifier, item, value)
38
-
39
- if value.is_a?(Array)
40
- if value.empty?
41
- pivoted[nil] ||= []
42
- pivoted[nil] << item
43
- else
44
- value.each do |val|
45
- pivoted[val] ||= []
46
- pivoted[val] << item
35
+ semaphore = Mutex.new
36
+
37
+ lists = list.each_slice(chunk_size(list)).to_a
38
+ loops = 0
39
+
40
+ reactor_running = EM.reactor_running?
41
+
42
+ work = Proc.new do
43
+ lists.each do |sub_list|
44
+
45
+ pivot_operation = Proc.new do
46
+ sub_list.each do |item|
47
+ # potential long running operation with blocking IO
48
+ value = yield(item)
49
+
50
+ # notify observers that a pivot block was just called
51
+ identifier = options[:identifier] || "#{item.hash}:#{block.hash}"
52
+ changed
53
+ # potential long running operation with blocking IO
54
+ notify_observers(identifier, item, value)
55
+
56
+ semaphore.synchronize {
57
+ if value.is_a?(Array)
58
+ if value.empty?
59
+ pivoted[nil] ||= []
60
+ pivoted[nil] << item
61
+ else
62
+ value.each do |val|
63
+ pivoted[val] ||= []
64
+ pivoted[val] << item
65
+ end
66
+ end
67
+ else
68
+ pivoted[value] ||= []
69
+ pivoted[value] << item
70
+ end
71
+ }
47
72
  end
48
73
  end
49
- else
50
- pivoted[value] ||= []
51
- pivoted[value] << item
74
+
75
+ pivot_callback = Proc.new do
76
+ semaphore.synchronize {
77
+ loops += 1
78
+ EM.stop if loops == lists.length && !reactor_running
79
+ }
80
+ end
81
+
82
+ EM.defer(pivot_operation, pivot_callback)
52
83
  end
53
84
  end
54
85
 
86
+ if reactor_running
87
+ work.call
88
+ else
89
+ EM.run &work
90
+ end
91
+
55
92
  pivoted
56
- end
93
+ end
57
94
 
58
95
  # Runs multiple pivots against a list of Objects.
59
96
  #
@@ -70,17 +107,17 @@ module One
70
107
  # key = "greater than or equal to 3" if i >= 3
71
108
  # key ||= "less than 3"
72
109
  # end
73
- #
110
+ #
74
111
  # # note the last pivot is an options Hash
75
112
  # pivots << {:delimiter => " & "}
76
- #
113
+ #
77
114
  # pivoter = One::Pivot.new
78
- # result = pivoter.multi_pivot(list, *pivots)
79
- #
115
+ # result = pivoter.multi_pivot(list, *pivots)
116
+ #
80
117
  # # the result will be a Hash with the following structure
81
118
  # {
82
- # "less than or equal to 5 & greater than or equal to 3" => [3, 4, 5],
83
- # "less than or equal to 5 & less than 3" => [1, 2],
119
+ # "less than or equal to 5 & greater than or equal to 3" => [3, 4, 5],
120
+ # "less than or equal to 5 & less than 3" => [1, 2],
84
121
  # "greater than 5 & greater than or equal to 3" => [6, 7, 8, 9]
85
122
  # }
86
123
  #
@@ -99,7 +136,7 @@ module One
99
136
 
100
137
  while pivots.length > 0
101
138
  p = pivots.shift
102
-
139
+
103
140
  # handle the case where the pivots are One::Pivot objects
104
141
  pivot_options = {}
105
142
  if p.is_a?(One::Pivot)
@@ -134,6 +171,17 @@ module One
134
171
  key.to_s
135
172
  end
136
173
 
174
+ def chunk_size(list)
175
+ len = list.length
176
+ case len
177
+ when 0..100 then len
178
+ when 100..1000 then len / 2
179
+ when 1000..10000 then len / 4
180
+ when 10000..100000 then len / 8
181
+ else len / 16
182
+ end
183
+ end
184
+
137
185
  end
138
186
  end
139
187
 
@@ -1,6 +1,7 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
2
2
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'one', 'pivot'))
3
3
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'one', 'pivoter'))
4
+ require 'eventmachine'
4
5
 
5
6
  class PivotTest < Test::Unit::TestCase
6
7
 
@@ -13,33 +14,44 @@ class PivotTest < Test::Unit::TestCase
13
14
  list = [1,2,3,4,5,6,7,8,9]
14
15
  result = @pivoter.pivot(list) {|item| item <= 5}
15
16
  assert_equal [6,7,8,9], result[false]
16
- assert_equal [1,2,3,4,5], result[true]
17
+ assert_equal [1,2,3,4,5], result[true]
17
18
  end
18
19
 
19
- should "multi pivot a simple array properly" do
20
+ should "pivot inside of an existing EventMachine reactor" do
21
+ EM.run do
22
+ list = [1,2,3,4,5,6,7,8,9]
23
+ result = @pivoter.pivot(list) {|item| item <= 5}
24
+ sleep(0.01) # allow enough time for the pivot to complete
25
+ assert_equal [6,7,8,9], result[false]
26
+ assert_equal [1,2,3,4,5], result[true]
27
+ EM.stop
28
+ end
29
+ end
30
+
31
+ should "multi pivot a simple array properly" do
20
32
  list = [1,2,3,4,5,6,7,8,9]
21
33
  pivots = []
22
-
34
+
23
35
  pivots << lambda do |i|
24
36
  key = "less than or equal to 5" if i <= 5
25
37
  key ||= "greater than 5"
26
38
  end
27
-
39
+
28
40
  pivots << lambda do |i|
29
41
  key = "greater than or equal to 3" if i >= 3
30
42
  key ||= "less than 3"
31
43
  end
32
-
44
+
33
45
  pivots << {:delimiter => " & "}
34
46
 
35
47
  result = @pivoter.multi_pivot(list, *pivots)
36
-
48
+
37
49
  # the result will be a Hash with the following structure:
38
50
  hash = {
39
- "less than or equal to 5 & greater than or equal to 3" => [3, 4, 5],
40
- "less than or equal to 5 & less than 3" => [1, 2],
51
+ "less than or equal to 5 & greater than or equal to 3" => [3, 4, 5],
52
+ "less than or equal to 5 & less than 3" => [1, 2],
41
53
  "greater than 5 & greater than or equal to 3" => [6, 7, 8, 9]
42
- }
54
+ }
43
55
 
44
56
  assert_equal hash, result
45
57
  end
@@ -47,38 +59,38 @@ class PivotTest < Test::Unit::TestCase
47
59
  should "support both procs and One::Pivot objects with multi_pivot" do
48
60
  list = [1,2,3,4,5,6,7,8,9]
49
61
  pivots = []
50
-
62
+
51
63
  pivots << One::Pivot.new("comparison to 5") do |i|
52
64
  key = "less than or equal to 5" if i <= 5
53
65
  key ||= "greater than 5"
54
66
  end
55
-
67
+
56
68
  pivots << One::Pivot.new("comparison to 3") do |i|
57
69
  key = "greater than or equal to 3" if i >= 3
58
70
  key ||= "less than 3"
59
71
  end
60
-
72
+
61
73
  pivots << {:delimiter => " & "}
62
74
 
63
75
  result = @pivoter.multi_pivot(list, *pivots)
64
-
76
+
65
77
  # the result will be a Hash with the following structure:
66
78
  hash = {
67
- "less than or equal to 5 & greater than or equal to 3" => [3, 4, 5],
68
- "less than or equal to 5 & less than 3" => [1, 2],
79
+ "less than or equal to 5 & greater than or equal to 3" => [3, 4, 5],
80
+ "less than or equal to 5 & less than 3" => [1, 2],
69
81
  "greater than 5 & greater than or equal to 3" => [6, 7, 8, 9]
70
- }
82
+ }
71
83
 
72
84
  assert_equal hash, result
73
85
  end
74
86
 
75
87
  should "support pivot observers (for example: to perform caching)" do
76
88
  # Pivot operations can be expensive.
77
- #
89
+ #
78
90
  # Demonstrate how you can use the observer pattern to cache
79
91
  # pivot results per item to save time on subsequent pivots for the same item.
80
- #
81
- # This strategy can be helpful if each item remains in memory for a period of time
92
+ #
93
+ # This strategy can be helpful if each item remains in memory for a period of time
82
94
  # that spans its participation in several pivot calls
83
95
 
84
96
  # extend Fixnum so we can cache pivot results
@@ -99,11 +111,11 @@ class PivotTest < Test::Unit::TestCase
99
111
  # this method gets invoked by the pivot object whenever a pivot block is executed
100
112
  def update(identifier, item, value)
101
113
  item.pivot_cache[identifier] = value
102
- end
103
- end
114
+ end
115
+ end
104
116
 
105
117
  observer = PivotObserver.new(@pivoter)
106
-
118
+
107
119
  list = [1,2,3,4,5,6,7,8,9]
108
120
  result = @pivoter.pivot(list, :identifier => :less_than_5) do |item|
109
121
  if item.pivot_cache.has_key?(:less_than_5)
@@ -116,8 +128,8 @@ class PivotTest < Test::Unit::TestCase
116
128
  list.each do |item|
117
129
  assert item.pivot_cache.has_key?(:less_than_5)
118
130
  end
119
-
120
- # pivot again but this time use the pivot cache stored on each item
131
+
132
+ # pivot again but this time use the pivot cache stored on each item
121
133
  cached_result = @pivoter.pivot(list, :identifier => :less_than_5) do |item|
122
134
  if item.pivot_cache.has_key?(:less_than_5)
123
135
  item.pivot_cache[:less_than_5]
@@ -131,11 +143,11 @@ class PivotTest < Test::Unit::TestCase
131
143
 
132
144
  should "support pivot observers when using multi_pivot (for example: to perform caching)" do
133
145
  # Pivot operations can be expensive.
134
- #
146
+ #
135
147
  # Demonstrate how you can use the observer pattern to cache
136
148
  # pivot results per item to save time on subsequent pivots for the same item.
137
- #
138
- # This strategy can be helpful if each item remains in memory for a period of time
149
+ #
150
+ # This strategy can be helpful if each item remains in memory for a period of time
139
151
  # that spans its participation in several pivot calls
140
152
 
141
153
  # extend Fixnum so we can cache pivot results
@@ -156,44 +168,44 @@ class PivotTest < Test::Unit::TestCase
156
168
  # this method gets invoked by the pivot object whenever a pivot block is executed
157
169
  def update(identifier, item, value)
158
170
  item.pivot_cache[identifier] = value
159
- end
160
- end
171
+ end
172
+ end
161
173
 
162
174
  observer = PivotObserver.new(@pivoter)
163
-
175
+
164
176
  list = [1,2,3,4,5,6,7,8,9]
165
177
 
166
178
  pivots = []
167
-
179
+
168
180
  pivots << One::Pivot.new("comparison to 5") do |i|
169
181
  key = "less than or equal to 5" if i <= 5
170
182
  key ||= "greater than 5"
171
183
  end
172
-
184
+
173
185
  pivots << One::Pivot.new("comparison to 3") do |i|
174
186
  key = "greater than or equal to 3" if i >= 3
175
187
  key ||= "less than 3"
176
188
  end
177
189
 
178
- result = @pivoter.multi_pivot(list, *pivots)
190
+ result = @pivoter.multi_pivot(list, *pivots)
179
191
 
180
192
  list.each do |item|
181
193
  assert item.pivot_cache.has_key?("comparison to 5")
182
194
  assert item.pivot_cache.has_key?("comparison to 3")
183
195
  end
184
-
185
- # pivot again but this time use the pivot cache stored on each item
196
+
197
+ # pivot again but this time use the pivot cache stored on each item
186
198
  pivots = []
187
-
199
+
188
200
  pivots << One::Pivot.new("comparison to 5") do |i|
189
201
  i.pivot_cache["comparison to 5"]
190
202
  end
191
-
203
+
192
204
  pivots << One::Pivot.new("comparison to 3") do |i|
193
205
  i.pivot_cache["comparison to 3"]
194
206
  end
195
207
 
196
- cached_result = @pivoter.multi_pivot(list, *pivots)
208
+ cached_result = @pivoter.multi_pivot(list, *pivots)
197
209
  assert_equal result, cached_result
198
210
  end
199
211
 
@@ -210,16 +222,16 @@ class PivotTest < Test::Unit::TestCase
210
222
 
211
223
  # Frank gets Read
212
224
  users[0][:roles] << "Read"
213
-
225
+
214
226
  # Joe gets Write
215
- users[1][:roles] << "Write"
227
+ users[1][:roles] << "Write"
216
228
 
217
229
  # John gets Read & Write
218
- users[2][:roles] << "Read"
230
+ users[2][:roles] << "Read"
219
231
  users[2][:roles] << "Write"
220
232
 
221
233
  # Sally gets no roles
222
-
234
+
223
235
  @users = users
224
236
  end
225
237
 
@@ -229,15 +241,15 @@ class PivotTest < Test::Unit::TestCase
229
241
  # this is what the resulting hash should look like:
230
242
  hash = {
231
243
  nil => [
232
- {:name=>"Sally", :gender => "F", :roles=>[]}],
244
+ {:name=>"Sally", :gender => "F", :roles=>[]}],
233
245
  "Read" => [
234
- {:name=>"Frank", :gender => "M", :roles=>["Read"]},
235
- {:name=>"John", :gender => "M", :roles=>["Read", "Write"]}],
246
+ {:name=>"Frank", :gender => "M", :roles=>["Read"]},
247
+ {:name=>"John", :gender => "M", :roles=>["Read", "Write"]}],
236
248
  "Write" => [
237
- {:name=>"Joe", :gender => "M", :roles=>["Write"]},
249
+ {:name=>"Joe", :gender => "M", :roles=>["Write"]},
238
250
  {:name=>"John", :gender => "M", :roles=>["Read", "Write"]}]
239
251
  }
240
-
252
+
241
253
  assert_equal hash, result
242
254
  end
243
255
 
@@ -245,21 +257,21 @@ class PivotTest < Test::Unit::TestCase
245
257
  pivots = []
246
258
  pivots << lambda {|user| user[:gender]}
247
259
  pivots << lambda {|user| user[:roles]}
248
-
260
+
249
261
  result = @pivoter.multi_pivot(@users, *pivots)
250
262
 
251
263
  # this is what the resulting hash should look like
252
264
  hash = {
253
265
  "M[PIVOT]Write" => [
254
- {:name=>"Joe", :gender=>"M", :roles=>["Write"]},
255
- {:name=>"John", :gender=>"M", :roles=>["Read", "Write"]}],
266
+ {:name=>"Joe", :gender=>"M", :roles=>["Write"]},
267
+ {:name=>"John", :gender=>"M", :roles=>["Read", "Write"]}],
256
268
  "M[PIVOT]Read" => [
257
- {:name=>"Frank", :gender=>"M", :roles=>["Read"]},
258
- {:name=>"John", :gender=>"M", :roles=>["Read", "Write"]}],
269
+ {:name=>"Frank", :gender=>"M", :roles=>["Read"]},
270
+ {:name=>"John", :gender=>"M", :roles=>["Read", "Write"]}],
259
271
  "F[PIVOT]nil" => [
260
272
  {:name=>"Sally", :gender=>"F", :roles=>[]}]
261
273
  }
262
-
274
+
263
275
  assert_equal hash, result
264
276
  end
265
277
 
metadata CHANGED
@@ -1,40 +1,47 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: one-pivot
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.7
4
5
  prerelease:
5
- version: 0.9.3
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Nathan Hopkins
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-02-23 00:00:00 -07:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
17
- name: shoulda
12
+ date: 2011-11-08 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: &2161266960 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - =
20
+ - !ruby/object:Gem::Version
21
+ version: 0.12.10
22
+ type: :runtime
18
23
  prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
24
+ version_requirements: *2161266960
25
+ - !ruby/object:Gem::Dependency
26
+ name: shoulda
27
+ requirement: &2161266360 !ruby/object:Gem::Requirement
20
28
  none: false
21
- requirements:
22
- - - "="
23
- - !ruby/object:Gem::Version
29
+ requirements:
30
+ - - =
31
+ - !ruby/object:Gem::Version
24
32
  version: 2.11.3
25
33
  type: :development
26
- version_requirements: *id001
27
- description: " Pivot provides basic pivoting functionality for sorting an Array of objects.\n"
28
- email:
34
+ prerelease: false
35
+ version_requirements: *2161266360
36
+ description: ! " A simple and intuitive way to perform powerful data mining on
37
+ lists of objects.\n Think of it as MapReduce for mortals.\n"
38
+ email:
29
39
  - natehop@gmail.com
30
40
  - natehop@1on1.com
31
41
  executables: []
32
-
33
42
  extensions: []
34
-
35
43
  extra_rdoc_files: []
36
-
37
- files:
44
+ files:
38
45
  - lib/one/pivot.rb
39
46
  - lib/one/pivoter.rb
40
47
  - lib/one-pivot.rb
@@ -45,33 +52,29 @@ files:
45
52
  - test/pivot_test.rb
46
53
  - test/pivoter_test.rb
47
54
  - test/test_helper.rb
48
- has_rdoc: true
49
55
  homepage: https://github.com/one-on-one/pivot
50
- licenses:
56
+ licenses:
51
57
  - MIT
52
58
  post_install_message:
53
59
  rdoc_options: []
54
-
55
- require_paths:
60
+ require_paths:
56
61
  - lib
57
- required_ruby_version: !ruby/object:Gem::Requirement
62
+ required_ruby_version: !ruby/object:Gem::Requirement
58
63
  none: false
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: "0"
63
- required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
69
  none: false
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: "0"
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
69
74
  requirements: []
70
-
71
75
  rubyforge_project:
72
- rubygems_version: 1.5.2
76
+ rubygems_version: 1.8.10
73
77
  signing_key:
74
78
  specification_version: 3
75
- summary: Pivot provides basic pivoting functionality for sorting an Array of objects.
79
+ summary: Enumerable#group_by on steroids with an extra dash of awesome.
76
80
  test_files: []
77
-