reactive_support 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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