reactive_support 0.1.2

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.
@@ -0,0 +1,14 @@
1
+ class Object
2
+
3
+ # The +#exists?+ method and its alias, +#exist?+, return +true+ if the object
4
+ # calling the method is not +nil+.
5
+ #
6
+ # 'foobar'.exists? # => true
7
+ # nil.exists? # => false
8
+
9
+ def exists?
10
+ !self.nil?
11
+ end
12
+
13
+ alias_method :exist?, :exists?
14
+ end
@@ -0,0 +1,39 @@
1
+ class Object
2
+
3
+ # The +#in?+ method returns +true+ if the calling object is included in the
4
+ # +object+ passed as a parameter. The +object+ parameter must be a String,
5
+ # Enumerable, or other object that responds to +#include?+. If it doesn't,
6
+ # the #in? method will raise an ArgumentError.
7
+ #
8
+ # When passed an Array object, +#in?+ returns +true+ if the calling object
9
+ # is included as a member of the array:
10
+ #
11
+ # 'foo'.in? ['foo', 'bar'] # => true
12
+ # 'foo'.in? ['bar', 'baz'] # => false
13
+ #
14
+ # When passed a Hash object, +#in?+ returns true if the calling object is
15
+ # included as a key in the hash. To look for an object in a hash's values,
16
+ # use the hash's +#values+ method.
17
+ #
18
+ # 'foo'.in? {'foo' => 'bar'} # => true
19
+ # 'foo'.in? {'bar' => 'foo'} # => false
20
+ # 'foo'.in? {'bar' => 'foo'}.values # => true
21
+ #
22
+ # When passed a String object, +#in?+ returns +true+ if the the calling object
23
+ # (which must also be a string) appears verbatim within the parameter object.
24
+ # If the passed-in object is a string but the calling object is something else,
25
+ # a TypeError will be returned.
26
+ #
27
+ # 'foo'.in? 'foobar' # => true
28
+ # 'foo'.in? 'foto' # => false
29
+ # ['foo'].in? 'foobar' # => TypeError
30
+ #
31
+ # When passed an object that does not respond to +#include?+, +#in?+ raises
32
+ # an ArgumentError.
33
+
34
+ def in?(object)
35
+ object.include?(self)
36
+ rescue NoMethodError
37
+ raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
38
+ end
39
+ end
@@ -0,0 +1,37 @@
1
+ class Object
2
+ # The +#instance_values+ method returns a hash mapping all the calling object's
3
+ # instance variable names to the variables' values. The hash keys are the
4
+ # variables' names, as strings, without the '@' prepended to them. Each of the
5
+ # hash's values is the value corresponding to the given variable name.
6
+ #
7
+ # class Widget
8
+ # def initialize(x,y)
9
+ # @x, @y = x, y
10
+ # end
11
+ # end
12
+ #
13
+ # widget = Widget.new('foo', 'bar')
14
+ # widget.instance_values # => {'x' => 'foo', 'y' => 'bar'}
15
+
16
+ def instance_values
17
+ Hash[instance_variables.map {|name| [name[1..-1], instance_variable_get(name) ] }]
18
+ end
19
+
20
+ # The +#instance_variable_names+ method returns an array of the names of
21
+ # the instance variables defined on the calling object. The names themselves
22
+ # are returned as strings and, unlike in the +#instance_values+ method,
23
+ # include the +'@'+ prefix.
24
+ #
25
+ # class Widget
26
+ # def initialize(x,y)
27
+ # @x, @y = x, y
28
+ # end
29
+ # end
30
+ #
31
+ # widget = Widget.new(1, 2)
32
+ # widget.instance_variable_names # => ['@x', '@y']
33
+
34
+ def instance_variable_names
35
+ instance_variables.map {|name| name.to_s }
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ class Object
2
+ # The +#try+ method calls the given +method+ (with given +*args+ and +&block+)
3
+ # on the object calling it. The +#try+ method returns the output of the
4
+ # method or, if an error is raised, +nil+. It accepts an arbitrary number
5
+ # of arguments and an optional block, enabling it to be used with any method.
6
+ #
7
+ # The first argument is the name of the method to be called, given as a symbol.
8
+ # The rest are the arguments (if any) that should be passed into that
9
+ # method. Likewise, the +&block+ parameter will be passed on to the method
10
+ # being called.
11
+ #
12
+ # Examples of a method being called without args:
13
+ # 'foo'.try(:upcase) # => 'FOO'
14
+ # nil.try(:upcase) # => nil
15
+ #
16
+ # Examples of a method being called with args:
17
+ # %w(foo bar baz).try(:join, '.') # => 'foo.bar.baz'
18
+ # nil.try(:join, '.') # => nil
19
+ #
20
+ # Examples of a method being called with a block:
21
+ # { foo: 10, bar: 18, baz: 32 }.try(:reject!) {|k,v| v < 25 } # => { baz: 32 }
22
+ # nil.try(:reject) {|k,v| v == 'foo' } # => nil
23
+ #
24
+ # When called on +nil+, +#try+ returns +nil+ even if the method being sent
25
+ # is defined for NilClass:
26
+ # nil.try(:inspect) # => nil
27
+ #
28
+ # The +#try+ method can also be called with a block and no arguments. In this case,
29
+ # the block will be instance_eval'ed:
30
+ # 'foobar'.try { upcase.truncate(3) } # => 'FOO'
31
+ # nil.try { upcase.truncate(3) } # => nil
32
+
33
+ def try(*args, &block)
34
+ return self if self.nil?
35
+
36
+ if args.empty? && block_given?
37
+ if block.arity.zero?
38
+ instance_eval(&block)
39
+ else
40
+ yield self
41
+ end
42
+ else
43
+ public_send(*args, &block) if args.respond_to?(:first)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ require 'reactive_support'
2
+
3
+ # The ReactiveExtensions module consists of methods I wish ActiveSupport provided.
4
+ # These methods do not adhere to the ActiveSupport API. If you wish to include
5
+ # them in your project, you will need to put this in your main project file:
6
+ # require 'reactive_support/extensions'
7
+ #
8
+ # ReactiveExtensions requires ReactiveSupport, so there is no need to require both
9
+ # explicitly.
10
+
11
+ module ReactiveExtensions
12
+
13
+ # The +#try_rescue+ method extends ReactiveSupport's +#try+ method so it
14
+ # rescues NoMethodErrors and TypeErrors as well as returning +nil+ when
15
+ # called on a +nil+ value.
16
+ #
17
+ # Like the +#try+ method, +#try_rescue+ takes 1 or more arguments. The first
18
+ # argument is the method to be called on the calling object, passed as a
19
+ # symbol. The others are zero or more arguments that will be passed through to
20
+ # that method, and +&block+ is an optional block that will be similarly passed through.
21
+ #
22
+ # Example of usage identical to +#try+:
23
+ # nil.try(:map) {|a| a.to_s } # => nil
24
+ # nil.try_rescue(:map) {|a| a.to_s } # => nil
25
+ #
26
+ # Example of usage calling a method that is not defined on the calling object:
27
+ # 10.try(:to_h) # => TypeError
28
+ # 10.try_rescue(:to_h) # => nil
29
+ #
30
+ # Example of usage with invalid arguments:
31
+ # %w(foo, bar, baz).try(:join, [:hello, :world]) # => TypeError
32
+ # %w(foo, bar, baz).try_rescue(:join, [:hello, :world]) # => nil
33
+
34
+ def try_rescue(*args, &block)
35
+ self.try(*args, &block) rescue nil
36
+ end
37
+ end
38
+
39
+ class Object
40
+ include ReactiveExtensions
41
+ end
@@ -0,0 +1,15 @@
1
+ Dir['./lib/reactive_support/core_ext/object/**/*.rb'].each {|f| require f }
2
+
3
+ # The ReactiveSupport module implements methods from ActiveSupport. It can be
4
+ # included in Ruby's +Object+ class by adding +require 'reactive_support'+ to
5
+ # your project file. Then, ReactiveSupport methods can be called on any Ruby
6
+ # object just like the object's own methods.
7
+ #
8
+ # In this example, ReactiveSupport's #try method is called on an array:
9
+ # require 'reactive_support'
10
+ #
11
+ # arr = %w(foo, bar, baz)
12
+ # arr.try(:join, '.')
13
+
14
+ module ReactiveSupport
15
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path('../version.rb', __FILE__)
2
+ require File.expand_path('../files.rb', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.specification_version = 1 if s.respond_to? :specification_version=
6
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
7
+ s.required_ruby_version = '>= 1.9.3'
8
+
9
+ s.name = 'reactive_support'
10
+ s.version = ReactiveSupport.gem_version
11
+ s.date = '2014-09-24'
12
+
13
+ s.description = "ActiveSupport methods re-implemented independently of the Rails ecosystem"
14
+ s.summary = "ReactiveSupport provides useful ActiveSupport methods in a gem that is simple, stable, agnostic, and transparent."
15
+
16
+ s.authors = ["Dana Scheider"]
17
+ s.email = "dana.scheider@gmail.com"
18
+
19
+ # = MANIFEST =
20
+ s.files = ReactiveSupport.files
21
+ s.require_path = 'lib'
22
+ # = MANIFEST =
23
+
24
+ s.test_files = s.files.select {|path| path =~ /^spec\/.*.rb/ }
25
+ s.licenses = 'MIT'
26
+
27
+ s.extra_rdoc_files = %w[CONTRIBUTING.md README.md LICENSE]
28
+
29
+ s.add_development_dependency 'rspec', '~> 3.1'
30
+ s.add_development_dependency 'bundler', '~> 1.6'
31
+ s.add_development_dependency 'coveralls', '~> 0.7'
32
+ s.add_development_dependency 'simplecov', '~> 0.9'
33
+
34
+ s.has_rdoc = true
35
+ s.homepage = "http://github.com/danascheider/reactive_support"
36
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "ReactiveSupport"]
37
+ s.require_paths = %w[lib]
38
+ s.rubygems_version = '1.1.1'
39
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Array do
4
+ describe '#from method' do
5
+ context 'normal use' do
6
+ context 'positive position' do
7
+ it 'returns the end of the array' do
8
+ expect([1, 2, 3, 4, 5].from(2)).to eql [3, 4, 5]
9
+ end
10
+ end
11
+
12
+ context 'negative position' do
13
+ it 'returns the end of the array' do
14
+ expect([1, 2, 3, 4, 5].from(-2)).to eql [4, 5]
15
+ end
16
+ end
17
+ end
18
+
19
+ context 'when array is empty' do
20
+ it 'returns an empty array' do
21
+ expect([].from(0)).to eql []
22
+ end
23
+ end
24
+
25
+ context 'position higher than max index' do
26
+ it 'returns an empty array' do
27
+ expect([1, 2, 3, 4, 5].from(100)).to eql []
28
+ end
29
+ end
30
+
31
+ context 'position lower than min index' do
32
+ it 'returns an empty array' do
33
+ expect([1, 2, 3].from(-4)).to eql []
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#to method' do
39
+ context 'normal use' do
40
+ context 'positive position' do
41
+ it 'returns the beginning of the array' do
42
+ expect([1, 2, 3, 4, 5].to(2)). to eql [1, 2, 3]
43
+ end
44
+ end
45
+
46
+ context 'negative position' do
47
+ it 'returns the beginning of the array' do
48
+ expect([1, 2, 3, 4, 5].to(-2)).to eql [1, 2, 3, 4]
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'when array is empty' do
54
+ it 'returns an empty array' do
55
+ expect([].to(0)).to eql []
56
+ end
57
+ end
58
+
59
+ context 'position higher than max index' do
60
+ it 'returns the whole array' do
61
+ expect([1, 2, 3].to(10)).to eql [1, 2, 3]
62
+ end
63
+ end
64
+
65
+ context 'position lower than min index' do
66
+ it 'returns an empty array' do
67
+ expect([1, 2, 3].to(-5)).to eql []
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe ReactiveExtensions do
4
+
5
+ describe '#try_rescue method' do
6
+ context 'when self is nil' do
7
+ it 'returns nil' do
8
+ expect(nil.try_rescue(:collect) {|i| i + 2 }).to eql nil
9
+ end
10
+ end
11
+
12
+ context 'when the method can be executed successfully' do
13
+ it 'calls the method' do
14
+ expect('foo'.try_rescue(:upcase)).to eql 'FOO'
15
+ end
16
+ end
17
+
18
+ context 'when a NoMethodError is raised' do
19
+ it 'returns nil' do
20
+ expect(('foo').try_rescue(:bar)).to eql nil
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,314 @@
1
+ require 'spec_helper'
2
+
3
+ describe ReactiveSupport do
4
+ describe '#blank? method' do
5
+ context 'when true' do
6
+ hash = {
7
+ 'empty string' => '',
8
+ 'whitespace string' => ' ',
9
+ 'FalseClass' => false,
10
+ 'NilClass' => nil,
11
+ 'empty array' => [],
12
+ 'empty hash' => {}
13
+ }
14
+
15
+ hash.each do |k,v|
16
+ specify "#{k} returns true" do
17
+ expect(v.blank?).to be true
18
+ end
19
+ end
20
+ end
21
+
22
+ context 'when false' do
23
+ hash = {
24
+ 'non-blank object' => 'foo',
25
+ 'TrueClass' => true,
26
+ 'enumerable with blank members' => [nil, false],
27
+ 'numeric' => 10
28
+ }
29
+
30
+ hash.each do |k,v|
31
+ specify "#{k} returns false" do
32
+ expect(v.blank?).to be false
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#deep_dup method' do
39
+ context 'duplicable object' do
40
+ before(:each) do
41
+ @str = 'foo'
42
+ end
43
+
44
+ it 'returns a duplicate' do
45
+ expect(@str.deep_dup).not_to be @str
46
+ end
47
+
48
+ it 'does not affect the original' do
49
+ @str.deep_dup.instance_variable_set(:@a, 1)
50
+ expect(@string.instance_variable_get(:@a)).to eql nil
51
+ end
52
+ end
53
+
54
+ context 'non-duplicable object' do
55
+ it 'returns itself' do
56
+ num = 10
57
+ expect(num.deep_dup).to be num
58
+ end
59
+ end
60
+
61
+ context 'array' do
62
+ it 'does not affect the original' do
63
+ arr = [ [1, 2], 3 ]
64
+ arr.deep_dup[0][1] = 4
65
+ expect(arr[0][1]).to eql 2
66
+ end
67
+ end
68
+
69
+ context 'hash' do
70
+ it 'does not affect the original' do
71
+ h = { foo: { bar: 1 } }
72
+ h.deep_dup[:foo][:bar] = 10
73
+ expect(h[:foo][:bar]).to eql 1
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#duplicable? method' do
79
+ context 'when true' do
80
+ it 'returns true' do
81
+ expect(['foo', 'bar', 'baz'].duplicable?).to be true
82
+ end
83
+ end
84
+
85
+ context 'when false' do
86
+ hash = {
87
+ 'NilClass' => nil,
88
+ 'FalseClass' => false,
89
+ 'TrueClass' => true,
90
+ 'symbol' => :symbol,
91
+ 'numeric' => 10,
92
+ 'method' => method(:puts)
93
+ }
94
+
95
+ hash.each do |k,v|
96
+ specify "#{k} returns false" do
97
+ expect(v.duplicable?).to be false
98
+ end
99
+ end
100
+ end
101
+
102
+ context 'BigDecimal' do
103
+ let(:dec) { BigDecimal.new('4.56') }
104
+
105
+ before(:each) do
106
+ @condition = !!(RUBY_VERSION =~ /^1\.9/ || RUBY_VERSION =~ /jruby/)
107
+ end
108
+ context 'Ruby version >= 2.0.0' do
109
+ it 'returns true' do
110
+ expect(dec.duplicable?).to be true unless @condition
111
+ end
112
+ end
113
+
114
+ context 'Ruby version 1.9.x' do
115
+ it 'returns false' do
116
+ expect(dec.duplicable?).to be false if @condition
117
+ end
118
+
119
+ it 'doesn\'t raise an error' do
120
+ expect{ dec.duplicable? }.not_to raise_error if @condition
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ describe '#exist? method' do
127
+ context 'existent object' do
128
+ it 'returns true' do
129
+ expect({foo: :bar}.exist?).to be true
130
+ end
131
+ end
132
+
133
+ context 'nil' do
134
+ it 'returns false' do
135
+ expect(nil.exist?).to be false
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#exists? method' do
141
+ context 'existent object' do
142
+ it 'returns true' do
143
+ expect({foo: :bar}.exists?).to be true
144
+ end
145
+ end
146
+
147
+ context 'nil' do
148
+ it 'returns false' do
149
+ expect(nil.exists?).to be false
150
+ end
151
+ end
152
+ end
153
+
154
+ describe '#in? method' do
155
+ context 'when true' do
156
+ hash = {
157
+ 'array contains calling object' => ['foo', 'bar', 'baz'],
158
+ 'hash contains the given key' => { 'foo' => 'bar' },
159
+ 'string contains the given characters' => 'foolish'
160
+ }
161
+
162
+ hash.each do |k,v|
163
+ specify k do
164
+ expect('foo'.in? v).to be true
165
+ end
166
+ end
167
+ end
168
+
169
+ context 'when false' do
170
+ hash = {
171
+ 'array doesn\'t contain calling object' => ['bar', 'baz'],
172
+ 'hash doesn\'t contain given key' => { bar: :baz },
173
+ 'string doesn\'t contain given characters' => 'something else'
174
+ }
175
+
176
+ hash.each do |k,v|
177
+ specify k do
178
+ expect('foo'.in? v).to be false
179
+ end
180
+ end
181
+ end
182
+
183
+ context 'error' do
184
+ hash = {
185
+ 'incompatible data types' => [{foo: 'bar'}, 'foobar', TypeError],
186
+ 'invalid parameter' => [10, 1000, ArgumentError]
187
+ }
188
+
189
+ hash.each do |k,v|
190
+ specify k do
191
+ expect{ v[0].in? v[1] }.to raise_error(v[2])
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ describe '#instance_values method' do
198
+ before(:each) do
199
+ class C
200
+ def initialize(x,y)
201
+ @x, @y = x, y
202
+ end
203
+ end
204
+
205
+ @c = C.new('foo', 'bar')
206
+ end
207
+
208
+ it 'returns a hash' do
209
+ expect(@c.instance_values).to be_a(Hash)
210
+ end
211
+
212
+ it 'uses instance variable names without @ as keys' do
213
+ ['x', 'y'].each {|var| expect(@c.instance_values).to have_key(var) }
214
+ end
215
+
216
+ it 'returns variable values' do
217
+ [['x', 'foo'], ['y','bar']].each {|(var, val)| expect(@c.instance_values[var]). to eql val }
218
+ end
219
+ end
220
+
221
+ describe '#instance_variable_names method' do
222
+ before(:all) do
223
+ class C
224
+ def initialize(x, y)
225
+ @x, @y = x, y
226
+ end
227
+ end
228
+
229
+ @c = C.new(1, 2)
230
+ end
231
+
232
+ it 'returns an array' do
233
+ expect(@c.instance_variable_names).to be_an Array
234
+ end
235
+
236
+ it 'includes the name of the instance variables' do
237
+ ['@x', '@y'].each {|var| expect(@c.instance_variable_names).to include(var) }
238
+ end
239
+ end
240
+
241
+ describe '#present? method' do
242
+ context 'when true' do
243
+ hash = {
244
+ 'non-empty object' => ['foo'],
245
+ 'TrueClass' => true,
246
+ 'enumerable with blank members' => [nil, false],
247
+ 'numeric' => 8.2
248
+ }
249
+
250
+ hash.each do |k,v|
251
+ specify k do
252
+ expect(v.present?).to be true
253
+ end
254
+ end
255
+ end
256
+
257
+ context 'when false' do
258
+ hash = {
259
+ 'empty string' => '',
260
+ 'whitespace string' => ' ',
261
+ 'FalseClass' => false,
262
+ 'NilClass' => nil,
263
+ 'empty array' => [],
264
+ 'empty hash' => {}
265
+ }
266
+
267
+ hash.each do |k,v|
268
+ specify k do
269
+ expect(v.present?).to be false
270
+ end
271
+ end
272
+ end
273
+ end
274
+
275
+ describe '#try method' do
276
+ context 'NilClass' do
277
+ it 'returns nil' do
278
+ expect(nil.try(:inspect)).to be nil
279
+ end
280
+ end
281
+
282
+ context 'when the calling object is not nil' do
283
+ context 'no args, no block' do
284
+ it 'evaluates the method' do
285
+ expect('foo'.try(:to_i)).to eql 0
286
+ end
287
+ end
288
+
289
+ context 'no method, no args, block given' do
290
+ it 'tries to evaluate the block' do
291
+ expect(-17.25.try { abs.truncate }).to eql 17
292
+ end
293
+ end
294
+
295
+ context 'with args' do
296
+ it 'evaluates the method' do
297
+ expect(%w(foo bar baz).try(:join, '.')).to eql 'foo.bar.baz'
298
+ end
299
+ end
300
+
301
+ context 'with block' do
302
+ it 'evaluates the method' do
303
+ expect([1, 2, 3].try(:collect) {|i| i + 1 }).to eql [2, 3, 4]
304
+ end
305
+ end
306
+ end
307
+
308
+ context 'when the method is invalid' do
309
+ it 'raises an error' do
310
+ expect{ 'foo'.try(:collect) {|i| i * 3 } }.to raise_error(NoMethodError)
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,18 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+ require 'rspec'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
9
+ SimpleCov.start if ENV["COVERAGE"]
10
+ Coveralls.wear!
11
+
12
+ require_relative '../lib/reactive_support'
13
+ require_relative '../lib/reactive_support/core_ext/array/access'
14
+ require_relative '../lib/reactive_support/extensions/reactive_extensions'
15
+
16
+ RSpec.configure do |c|
17
+ c.order = 'random'
18
+ end
data/version.rb ADDED
@@ -0,0 +1,14 @@
1
+ module ReactiveSupport
2
+ def self.gem_version
3
+ Gem::Version.new Version::STRING
4
+ end
5
+
6
+ module Version
7
+ MAJOR = '0'
8
+ MINOR = '1'
9
+ PATCH = '2'
10
+ PRE = nil
11
+
12
+ STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.').chomp('.')
13
+ end
14
+ end