bkoski-each_with_context 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.rdoc +50 -0
- data/lib/each_with_context/enumerable.rb +11 -0
- data/lib/each_with_context/enumerable_context.rb +75 -0
- data/lib/each_with_context.rb +2 -0
- data/test/test_each_with_context.rb +225 -0
- data/test/test_helper.rb +10 -0
- metadata +61 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Ben Koski
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
= each_with_context
|
2
|
+
|
3
|
+
Inspired by the Liquid templating language's nice {for loop goodies}[http://wiki.github.com/tobi/liquid/liquid-for-designers],
|
4
|
+
each_with_context provides some contextual information about an element in an each loop.
|
5
|
+
|
6
|
+
If you've ever found yourself writing an <tt>each_with_index</tt> and
|
7
|
+
checking <tt>i == 0</tt> to see if the element is the first, or wondering whether the
|
8
|
+
next element is differs from the current, this gem is for you.
|
9
|
+
|
10
|
+
== INSTALLATION
|
11
|
+
|
12
|
+
To use, just install the gem:
|
13
|
+
|
14
|
+
sudo gem install bkoski-each_with_context --source http://gems.github.com
|
15
|
+
|
16
|
+
and
|
17
|
+
|
18
|
+
require 'each_with_context'
|
19
|
+
|
20
|
+
Now you can call <tt>each_with_context</tt> on anything that's already Enumerable[http://www.ruby-doc.org/core/classes/Enumerable.html].
|
21
|
+
|
22
|
+
|
23
|
+
== USAGE
|
24
|
+
|
25
|
+
each_with_context accepts a block with two params: the element, and an EnumerableContext describing
|
26
|
+
the element. For example:
|
27
|
+
|
28
|
+
[1,2,3].each_with_context do |num, context|
|
29
|
+
|
30
|
+
# Here, you can call:
|
31
|
+
context.first? # true if num is first element
|
32
|
+
context.last? # true if num is last element
|
33
|
+
|
34
|
+
context.edge # returns :first if first?, :last if last?, nil otherwise
|
35
|
+
# useful when determining CSS selectors
|
36
|
+
|
37
|
+
context.next # next element; nil if that doesn't exist
|
38
|
+
context.previous # previous element; nil if that doesn't exist
|
39
|
+
|
40
|
+
context.next_differs_on?(:some-method) # These methods return true if element.some_method != next/previous.some_method
|
41
|
+
context.previous_differs_on?(:some-method) # If there is no next/previous, they return nil
|
42
|
+
|
43
|
+
context.previous_is? {|element, previous_element| previous_element > element} # These methods yield element and next/previous, then return the result of the block
|
44
|
+
context.next_is? {|element, next_element| next_element * 2 == element} # Useful for more advanced comparisons. Method returns nil if there is no next/previous.
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
== COPYRIGHT
|
49
|
+
|
50
|
+
Copyright (c) 2009 Ben Koski. See LICENSE for details.
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Enumerable
|
2
|
+
|
3
|
+
# Calls block with two arguments: the current <tt>element</tt> an EnumerableContext,
|
4
|
+
# which provides details about the element's context. See class docs for details.
|
5
|
+
def each_with_context &block
|
6
|
+
each_with_index do |element, i|
|
7
|
+
yield(element, EnumerableContext.new(self,i))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class EnumerableContext
|
2
|
+
|
3
|
+
# Current index into the collection
|
4
|
+
attr_accessor :index
|
5
|
+
|
6
|
+
# Create with a collection and the index into that collection
|
7
|
+
def initialize collection, index
|
8
|
+
@collection = collection
|
9
|
+
@index = index
|
10
|
+
end
|
11
|
+
|
12
|
+
# Is current index the first?
|
13
|
+
def first?
|
14
|
+
index == 0
|
15
|
+
end
|
16
|
+
|
17
|
+
# Is current index the last?
|
18
|
+
def last?
|
19
|
+
index == @collection.length - 1
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns :first if first?; last if last?; nil otherwise
|
23
|
+
def edge
|
24
|
+
:first if first?
|
25
|
+
:last if last?
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the next element in the collection; nil if it does not exist
|
30
|
+
def next
|
31
|
+
last? ? nil : @collection[index + 1]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the previous element in the collection; nil if it does not exist
|
35
|
+
def previous
|
36
|
+
first? ? nil : @collection[index - 1]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns true if the next element does not have the same value for <tt>field</tt>
|
40
|
+
def next_differs_on? field
|
41
|
+
if last?
|
42
|
+
nil
|
43
|
+
else
|
44
|
+
element.send(field) != self.next.send(field) # need to self to differentiate from keyword
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns true if the previous element does not have the same value for <tt>field</tt>
|
49
|
+
def previous_differs_on? field
|
50
|
+
if first?
|
51
|
+
nil
|
52
|
+
else
|
53
|
+
element.send(field) != previous.send(field)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Yields a block with element, next_element as params; returns value returned by block
|
58
|
+
# Returns nil if there is no next element.
|
59
|
+
def next_is? &block
|
60
|
+
last? ? nil : yield(element, self.next)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Yields a block with element, previous_element as params; returns value returned by block
|
64
|
+
# Returns nil if there is no previous element.
|
65
|
+
def previous_is? &block
|
66
|
+
first? ? nil : yield(element, self.previous)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
# Returns the element at the current index
|
71
|
+
def element
|
72
|
+
@collection[index]
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class TestEachWithContext < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "first?" do
|
6
|
+
setup do
|
7
|
+
@collection = [1,2,3,4]
|
8
|
+
end
|
9
|
+
|
10
|
+
should "return true on last element" do
|
11
|
+
@collection.each_with_context do |num,c|
|
12
|
+
assert c.first? if num == 1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
should "return false for all other elements" do
|
17
|
+
@collection.each_with_context do |num,c|
|
18
|
+
assert !c.first?, "first? should be false for #{num}" if num != 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "last?" do
|
24
|
+
setup do
|
25
|
+
@collection = [1,2,3,4]
|
26
|
+
end
|
27
|
+
|
28
|
+
should "return true on last element" do
|
29
|
+
@collection.each_with_context do |num,c|
|
30
|
+
assert c.last? if num == 4
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
should "return false for all other elements" do
|
35
|
+
@collection.each_with_context do |num,c|
|
36
|
+
assert !c.last?, "last? should be false for #{num}" if num != 4
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "edge" do
|
42
|
+
setup do
|
43
|
+
@collection = [1,2,3]
|
44
|
+
end
|
45
|
+
|
46
|
+
should "return :first for first element" do
|
47
|
+
@collection.each_with_context do |num,c|
|
48
|
+
assert_equal :first, c.edge if c == 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
should "return :last for last element" do
|
53
|
+
@collection.each_with_context do |num,c|
|
54
|
+
assert_equal :last, c.edge if c == 3
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
should "return nil for an element in the middle" do
|
59
|
+
@collection.each_with_context do |num,c|
|
60
|
+
assert_nil c.edge if c == 2
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "index" do
|
66
|
+
should "return the zero-based index as index" do
|
67
|
+
[0,1,2,3].each_with_context do |num,c|
|
68
|
+
assert_equal num, c.index
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "next" do
|
74
|
+
setup do
|
75
|
+
@collection = ['a','b','c','d']
|
76
|
+
end
|
77
|
+
|
78
|
+
should "return the next element" do
|
79
|
+
@collection.each_with_context do |element, c|
|
80
|
+
assert_equal(@collection[c.index + 1], c.next) unless c.last?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
should "return nil if there is no next element" do
|
85
|
+
@collection.each_with_context do |element, c|
|
86
|
+
assert_nil c.next if c.last?
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "previous" do
|
92
|
+
setup do
|
93
|
+
@collection = ['a','b','c','d']
|
94
|
+
end
|
95
|
+
|
96
|
+
should "return the previous element" do
|
97
|
+
@collection.each_with_context do |element, c|
|
98
|
+
assert_equal(@collection[c.index - 1], c.previous) unless c.first?
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
should "return nil if there is no previous element" do
|
103
|
+
@collection.each_with_context do |element, c|
|
104
|
+
assert_nil c.previous if c.first?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "next_differs_on?" do
|
110
|
+
setup do
|
111
|
+
@collection = [Date.today, Date.today + 1, Date.today + 2]
|
112
|
+
end
|
113
|
+
|
114
|
+
should "return true if next element doesn't have same value for specified method" do
|
115
|
+
[Date.today, Date.today + 1, Date.today + 2].each_with_context do |date,c|
|
116
|
+
assert c.next_differs_on?(:day) unless c.last?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
should "return false if next element doesn't have same value for specified method" do
|
121
|
+
[Date.today, Date.today, Date.today].each_with_context do |date,c|
|
122
|
+
assert !c.next_differs_on?(:day) unless c.last?
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
should "return nil if there is no next element" do
|
127
|
+
[Date.today, Date.today, Date.today].each_with_context do |date,c|
|
128
|
+
assert_nil c.next_differs_on?(:day) if c.last?
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "previous_differs_on?" do
|
134
|
+
setup do
|
135
|
+
@collection = [Date.today, Date.today + 1, Date.today + 2]
|
136
|
+
end
|
137
|
+
|
138
|
+
should "return true if next element doesn't have same value for specified method" do
|
139
|
+
[Date.today, Date.today + 1, Date.today + 2].each_with_context do |date,c|
|
140
|
+
assert c.previous_differs_on?(:day) unless c.first?
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
should "return false if next element doesn't have same value for specified method" do
|
145
|
+
[Date.today, Date.today, Date.today].each_with_context do |date,c|
|
146
|
+
assert !c.previous_differs_on?(:day) unless c.first?
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
should "return nil if there is no next element" do
|
151
|
+
[Date.today, Date.today, Date.today].each_with_context do |date,c|
|
152
|
+
assert_nil c.previous_differs_on?(:day) if c.first?
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "next_is?" do
|
158
|
+
setup do
|
159
|
+
@collection = [1,2,3,4]
|
160
|
+
end
|
161
|
+
|
162
|
+
should "yield block with element as first parameter" do
|
163
|
+
@collection.each_with_context do |element, c|
|
164
|
+
unless c.last?
|
165
|
+
c.next_is? { |e,n| assert_equal(element, e) }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
should "yield block with next element as second parameter" do
|
171
|
+
@collection.each_with_context do |element, c|
|
172
|
+
unless c.last?
|
173
|
+
c.next_is? { |e,n| assert_equal(c.next, n) }
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
should "return nil if there is no next element" do
|
179
|
+
@collection.each_with_context do |element, c|
|
180
|
+
assert_nil(c.next_is? { |e,n| 29 }) if c.last?
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
should "return value returned by block" do
|
185
|
+
@collection.each_with_context do |element, c|
|
186
|
+
assert_equal(49, c.next_is? { |e,n| 49 }) unless c.last?
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "previous_is?" do
|
192
|
+
setup do
|
193
|
+
@collection = [1,2,3,4]
|
194
|
+
end
|
195
|
+
|
196
|
+
should "yield block with element as first parameter" do
|
197
|
+
@collection.each_with_context do |element, c|
|
198
|
+
unless c.first?
|
199
|
+
c.previous_is? { |e,p| assert_equal(element, e) }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
should "yield block with previous element as second parameter" do
|
205
|
+
@collection.each_with_context do |element, c|
|
206
|
+
unless c.first?
|
207
|
+
c.previous_is? { |e,p| assert_equal(c.previous, p) }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
should "return nil if there is no previous element" do
|
213
|
+
@collection.each_with_context do |element, c|
|
214
|
+
assert_nil(c.previous_is? { |e,p| 'test-value' }) if c.first?
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
should "return value returned by block" do
|
219
|
+
@collection.each_with_context do |element, c|
|
220
|
+
assert_equal('test-value', c.previous_is? { |e,p| 'test-value' }) unless c.first?
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bkoski-each_with_context
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Koski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-15 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: TODO
|
17
|
+
email: gems@benkoski.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/each_with_context
|
26
|
+
- lib/each_with_context.rb
|
27
|
+
- lib/each_with_context/enumerable.rb
|
28
|
+
- lib/each_with_context/enumerable_context.rb
|
29
|
+
- test/test_each_with_context.rb
|
30
|
+
- test/test_helper.rb
|
31
|
+
- README.rdoc
|
32
|
+
- LICENSE
|
33
|
+
has_rdoc: true
|
34
|
+
homepage: http://github.com/bkoski/each_with_context
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options:
|
37
|
+
- --inline-source
|
38
|
+
- --charset=UTF-8
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.2.0
|
57
|
+
signing_key:
|
58
|
+
specification_version: 2
|
59
|
+
summary: Get contextual information about an element in an each loop
|
60
|
+
test_files: []
|
61
|
+
|