each_with_context 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -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,2 @@
1
+ require File.join(File.dirname(__FILE__), 'each_with_context/enumerable')
2
+ require File.join(File.dirname(__FILE__), 'each_with_context/enumerable_context')
@@ -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
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'each_with_context'
8
+
9
+ class Test::Unit::TestCase
10
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: 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-12-14 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Contextual information about an element in an each loop
17
+ email: gems@benkoski.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - LICENSE
27
+ - README.rdoc
28
+ - lib/each_with_context.rb
29
+ - lib/each_with_context/enumerable.rb
30
+ - lib/each_with_context/enumerable_context.rb
31
+ - test/test_each_with_context.rb
32
+ - test/test_helper.rb
33
+ has_rdoc: true
34
+ homepage: http://github.com/bkoski/each_with_context
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --charset=UTF-8
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.3.5
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Get contextual information about an element in an each loop
61
+ test_files:
62
+ - test/test_each_with_context.rb
63
+ - test/test_helper.rb