active_enumerable 0.1.0 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8b42bdf39bf71038c5874ee160fd48a83a3055d0
4
- data.tar.gz: 84c95fda2a65d555b3b25071bfd14f826ea7f198
3
+ metadata.gz: 016716ccb116b9441ef93f1d0698201c9b41ac50
4
+ data.tar.gz: a164e13d533c2aa282b9d7663ee65f1a28b8ea17
5
5
  SHA512:
6
- metadata.gz: 92a9a0c81dd3dbf728b53df5870cd18ad69a7dcc21f36d177e1f5306d161eeadaf3d896645118df068bfded6b14d3d8b05e498b60047bbc74a816189eca588a3
7
- data.tar.gz: 40f02181da88f8d49d9ab5df82628907f35c2ba8598628dea895f297274bf36dac553ec671d78aa486e1b798336ed85191eeb905bf273d4222f5c8017236a571
6
+ metadata.gz: 7ab10d01e6738b89b9f375b1a1df9156f5cf8c2a3f96b026e698f244c232fd73a9188adc3523d678c721bb995b5902620e0b04dc4647c6fb90148e26f3112cb1
7
+ data.tar.gz: cb3a52fd97b0aae04319d7749e8df84b23fa9b30747cbc54c861f90b9c0d009ee6ba695e6dfb6a4db185fe541999e524ce5d72b86c799afc94a7ebdf4aac4c47
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # ActiveEnumerable
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/active_enumerable`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Include ActiveRecord like query methods to Ruby enumerable collections.
6
4
 
7
5
  ## Installation
8
6
 
@@ -22,7 +20,33 @@ Or install it yourself as:
22
20
 
23
21
  ## Usage
24
22
 
25
- TODO: Write usage instructions here
23
+ ```ruby
24
+ require "active_enumerable"
25
+
26
+ class Customers
27
+ include ActiveEnumerable
28
+
29
+ scope :unpaid, -> { where(paid: false).or(credit: 0) }
30
+ end
31
+
32
+ customers = Customers.new([{paid: true, credit: 1000}, {paid: false, credit: 2000}, {paid: false, credit: 0}])
33
+
34
+ customers.unpaid
35
+ # => <#Customers [{:paid=>false, :credit=>2000}, {:paid=>false, :credit=>0}]]>
36
+
37
+ customers.scope { select { |y| y > 1000 } }
38
+ #=> <#Customers [{paid: true, credit: 1000}, {paid: false, credit: 2000}]>
39
+
40
+ customers.sum(:credit)
41
+ #=> 3000
42
+
43
+ customers.create({paid: true, credit: 1500}) # defaults to Hash creations
44
+
45
+ Customers.item_class = Customer
46
+
47
+ customers.create({paid: true, credit: 1500}).to_a.last
48
+ #=> <#Customer paid: true, credit: 1500>
49
+ ```
26
50
 
27
51
  ## Development
28
52
 
@@ -32,7 +56,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
56
 
33
57
  ## Contributing
34
58
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/active_enumerable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
59
+ Bug reports and pull requests are welcome on GitHub at https://github.com/zeisler/active_enumerable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
36
60
 
37
61
 
38
62
  ## License
@@ -1,5 +1,25 @@
1
1
  require "active_enumerable/version"
2
+ require "active_enumerable/base"
3
+ require "active_enumerable/comparable"
4
+ require "active_enumerable/enumerable"
5
+ require "active_enumerable/finder"
6
+ require "active_enumerable/method_caller"
7
+ require "active_enumerable/scopes"
8
+ require "active_enumerable/where"
9
+ require "active_enumerable/queries"
2
10
 
3
11
  module ActiveEnumerable
4
- # Your code goes here...
12
+ include Base
13
+ include Enumerable
14
+ include Comparable
15
+ include Queries
16
+ include Scopes
17
+
18
+ module ClassMethods
19
+ include Scopes::ClassMethods
20
+ end
21
+
22
+ def self.included(base)
23
+ base.extend(ClassMethods)
24
+ end
5
25
  end
@@ -0,0 +1,44 @@
1
+ module ActiveEnumerable
2
+ module Base
3
+ def initialize(collection=[])
4
+ @to_a = collection.to_ary
5
+ end
6
+
7
+ attr_reader :to_a
8
+
9
+ # @private
10
+ def __new_relation__(collection)
11
+ self.class.new(collection)
12
+ end
13
+
14
+ def create(attributes)
15
+ add(if (klass = self.class.item_class)
16
+ klass.new(attributes)
17
+ else
18
+ attributes
19
+ end)
20
+ end
21
+
22
+ def add(item)
23
+ to_a << item
24
+ end
25
+
26
+ def all
27
+ self
28
+ end
29
+
30
+ module ClassMethods
31
+ def item_class
32
+ @item_class
33
+ end
34
+
35
+ def item_class=(klass)
36
+ @item_class = klass
37
+ end
38
+ end
39
+
40
+ def self.included(base)
41
+ base.extend(ClassMethods)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveEnumerable
2
+ module Comparable
3
+ include ::Comparable
4
+
5
+ def <=>(anOther)
6
+ if anOther.is_a?(Array) || self.class == anOther.class
7
+ to_a <=> anOther.to_a
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveEnumerable
2
+ module Enumerable
3
+ include ::Enumerable
4
+
5
+ def each(*args, &block)
6
+ @to_a.send(:each, *args, &block)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ module ActiveEnumerable
2
+ # @private
3
+ class Finder
4
+ def initialize(record)
5
+ @method_caller = MethodCaller.new(record)
6
+ end
7
+
8
+ def is_of(conditions={})
9
+ conditions.all? do |col, match|
10
+ if match.is_a? Hash
11
+ hash_match(col, match)
12
+ elsif match.is_a? ::Enumerable
13
+ any_match(col, match)
14
+ else
15
+ compare(col, match)
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def hash_match(col, match)
23
+ next_record = @method_caller.call(col)
24
+ if next_record.is_a? Array
25
+ next_record.any? { |record| Finder.new(record).is_of(match) }
26
+ else
27
+ Finder.new(next_record).is_of(match)
28
+ end
29
+ end
30
+
31
+ def any_match(col, match)
32
+ match.any? { |m| compare(col, m) }
33
+ end
34
+
35
+ def compare(col, match)
36
+ @method_caller.call(col) == match
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveEnumerable
2
+ # @private
3
+ class MethodCaller
4
+ attr_reader :object, :raise_no_method
5
+
6
+ def initialize(object, raise_no_method: true)
7
+ @object = object
8
+ @raise_no_method = raise_no_method
9
+ end
10
+
11
+ def call(method)
12
+ if object.is_a? Hash
13
+ object.fetch(method)
14
+ else
15
+ object.public_send(method)
16
+ end
17
+ rescue NoMethodError, KeyError => e
18
+ raise e if raise_no_method
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,234 @@
1
+ module ActiveEnumerable
2
+ module Queries
3
+ include ActiveEnumerable::Where
4
+
5
+ # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
6
+ # If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key
7
+ # is an integer, find by id coerces its arguments using +to_i+.
8
+ #
9
+ # <#ActiveEnumerable>.find(1) # returns the object for ID = 1
10
+ # <#ActiveEnumerable>.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
11
+ # <#ActiveEnumerable>.find([7, 17]) # returns an array for objects with IDs in (7, 17)
12
+ # <#ActiveEnumerable>.find([1]) # returns an array for the object with ID = 1
13
+ #
14
+ # <tt>ActiveEnumerable::RecordNotFound</tt> will be raised if one or more ids are not found.
15
+ def find(ids)
16
+ raise RecordNotFound.new("Couldn't find #{self.name} without an ID") if ids.nil?
17
+ results = [*ids].map do |id|
18
+ find_by!(id: id.to_i)
19
+ end
20
+ return __new_relation__(results) if ids.class == Array
21
+ results.first
22
+ end
23
+
24
+ # Updates all records with details given if they match a set of conditions supplied, limits and order can
25
+ # also be supplied.
26
+ #
27
+ # ==== Parameters
28
+ #
29
+ # * +updates+ - A string, array, or hash.
30
+ #
31
+ # ==== Examples
32
+ #
33
+ # # Update all customers with the given attributes
34
+ # <#ActiveEnumerable>.update_all wants_email: true
35
+ #
36
+ # # Update all books with 'Rails' in their title
37
+ # <#ActiveEnumerable>.where(title: 'Rails').update_all(author: 'David')
38
+ #
39
+ # # Update all books that match conditions, but limit it to 5 ordered by date
40
+ # <#ActiveEnumerable>.where(title: 'Rails').order(:created_at).limit(5).update_all(author: 'David')
41
+ def update_all(attributes)
42
+ all.each { |i| i.update(attributes) }
43
+ end
44
+
45
+ # Updates an object (or multiple objects) and saves it.
46
+ #
47
+ # ==== Parameters
48
+ #
49
+ # * +id+ - This should be the id or an array of ids to be updated.
50
+ # * +attributes+ - This should be a hash of attributes or an array of hashes.
51
+ #
52
+ # ==== Examples
53
+ #
54
+ # # Updates one record
55
+ # <#ActiveEnumerable>.update(15, user_name: 'Samuel', group: 'expert')
56
+ #
57
+ # # Updates multiple records
58
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
59
+ # <#ActiveEnumerable>.update(people.keys, people.values)
60
+ def update(id, attributes)
61
+ if id.is_a?(Array)
62
+ id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
63
+ else
64
+ object = find(id)
65
+ object.update(attributes)
66
+ object
67
+ end
68
+ end
69
+
70
+ # Finds the first record matching the specified conditions. There
71
+ # is no implied ordering so if order matters, you should specify it
72
+ # yourself.
73
+ #
74
+ # If no record is found, returns <tt>nil</tt>.
75
+ #
76
+ # <#ActiveEnumerable>.find_by name: 'Spartacus', rating: 4
77
+ def find_by(conditions = {})
78
+ to_a.detect do |record|
79
+ Finder.new(record).is_of(conditions)
80
+ end
81
+ end
82
+
83
+ # Like <tt>find_by</tt>, except that if no record is found, raises
84
+ # an <tt>ActiveEnumerable::RecordNotFound</tt> error.
85
+ def find_by!(conditions={})
86
+ result = find_by(conditions)
87
+ if result.nil?
88
+ raise RecordNotFound.new("Couldn't find #{self.name} with '#{conditions.keys.first}'=#{conditions.values.first}")
89
+ end
90
+ result
91
+ end
92
+
93
+ # Finds the first record with the given attributes, or creates a record
94
+ # with the attributes if one is not found:
95
+ #
96
+ # # Find the first user named "Penélope" or create a new one.
97
+ # <#ActiveEnumerable>.find_or_create_by(first_name: 'Penélope')
98
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
99
+ #
100
+ # # Find the first user named "Penélope" or create a new one.
101
+ # # We already have one so the existing record will be returned.
102
+ # <#ActiveEnumerable>.find_or_create_by(first_name: 'Penélope')
103
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
104
+ #
105
+ # This method accepts a block, which is passed down to +create+. The last example
106
+ # above can be alternatively written this way:
107
+ #
108
+ # # Find the first user named "Scarlett" or create a new one with a
109
+ # # different last name.
110
+ # <#ActiveEnumerable>.find_or_create_by(first_name: 'Scarlett') do |user|
111
+ # user.last_name = 'Johansson'
112
+ # end
113
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
114
+ #
115
+ def find_or_create_by(attributes, &block)
116
+ find_by(attributes) || create(attributes, &block)
117
+ end
118
+
119
+ alias_method :find_or_create_by!, :find_or_create_by
120
+
121
+ # Like <tt>find_or_create_by</tt>, but calls <tt>new</tt> instead of <tt>create</tt>.
122
+ def find_or_initialize_by(attributes, &block)
123
+ find_by(attributes) || new(attributes, &block)
124
+ end
125
+
126
+ # Count the records.
127
+ #
128
+ # <#ActiveEnumerable>.count
129
+ # # => the total count of all people
130
+ #
131
+ # <#ActiveEnumerable>.count(:age)
132
+ # # => returns the total count of all people whose age is not nil
133
+ def count(name = nil)
134
+ return all.size if name.nil?
135
+ where.not(name => nil).size
136
+ end
137
+
138
+ # Specifies a limit for the number of records to retrieve.
139
+ #
140
+ # <#ActiveEnumerable>.limit(10)
141
+ def limit(num)
142
+ relation = __new_relation__(all.take(num))
143
+ relation.send(:set_from_limit)
144
+ relation
145
+ end
146
+
147
+ # Calculates the sum of values on a given attribute. The value is returned
148
+ # with the same data type of the attribute, 0 if there's no row.
149
+ #
150
+ # <#ActiveEnumerable>.sum(:age) # => 4562
151
+ def sum(key)
152
+ values = values_by_key(key)
153
+ values.inject(0) do |sum, n|
154
+ sum + (n || 0)
155
+ end
156
+ end
157
+
158
+ # Calculates the average value on a given attribute. Returns +nil+ if there's
159
+ # no row.
160
+ #
161
+ # <#ActiveEnumerable>.average(:age) # => 35.8
162
+ def average(key)
163
+ values = values_by_key(key)
164
+ total = values.inject { |sum, n| sum + n }
165
+ BigDecimal.new(total) / BigDecimal.new(values.count)
166
+ end
167
+
168
+ # Calculates the minimum value on a given attribute. The value is returned
169
+ # with the same data type of the attribute, or +nil+ if there's no row.
170
+ #
171
+ # <#ActiveEnumerable>.minimum(:age) # => 7
172
+ def minimum(key)
173
+ values_by_key(key).min_by { |i| i }
174
+ end
175
+
176
+ # Calculates the maximum value on a given attribute. The value is returned
177
+ # with the same data type of the attribute, or +nil+ if there's no row.
178
+ #
179
+ # <#ActiveEnumerable>.maximum(:age) # => 93
180
+ def maximum(key)
181
+ values_by_key(key).max_by { |i| i }
182
+ end
183
+
184
+ # Allows to specify an order attribute:
185
+ #
186
+ # <#ActiveEnumerable>.order('name')
187
+ #
188
+ # <#ActiveEnumerable>.order(:name)
189
+ def order(key)
190
+ __new_relation__(all.sort_by { |item| MethodCaller.new(item).call(key) })
191
+ end
192
+
193
+ # Reverse the existing order clause on the relation.
194
+ #
195
+ # <#ActiveEnumerable>.order('name').reverse_order
196
+ def reverse_order
197
+ __new_relation__(to_a.reverse)
198
+ end
199
+
200
+ # Returns a chainable relation with zero records.
201
+ #
202
+ # Any subsequent condition chained to the returned relation will continue
203
+ # generating an empty relation.
204
+ #
205
+ # Used in cases where a method or scope could return zero records but the
206
+ # result needs to be chainable.
207
+ #
208
+ # For example:
209
+ #
210
+ # @posts = current_user.visible_posts.where(name: params[:name])
211
+ # # => the visible_posts method is expected to return a chainable Relation
212
+ #
213
+ # def visible_posts
214
+ # case role
215
+ # when 'Country Manager'
216
+ # <#ActiveEnumerable>.where(country: country)
217
+ # when 'Reviewer'
218
+ # <#ActiveEnumerable>.published
219
+ # when 'Bad User'
220
+ # <#ActiveEnumerable>.none # It can't be chained if [] is returned.
221
+ # end
222
+ # end
223
+ #
224
+ def none
225
+ __new_relation__([])
226
+ end
227
+
228
+ private
229
+
230
+ def values_by_key(key)
231
+ all.map { |obj| obj.send(key) }
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,4 @@
1
+ module ActiveEnumerable
2
+ class RecordNotFound < StandardError
3
+ end
4
+ end
@@ -0,0 +1,52 @@
1
+ module ActiveEnumerable
2
+ module Scopes
3
+ def method_missing(meth, *args, &block)
4
+ if create_scope_method(meth)
5
+ send(meth, *args, &block)
6
+ else
7
+ super
8
+ end
9
+ end
10
+
11
+ def respond_to_missing?(meth, include_private = false)
12
+ if create_scope_method(meth)
13
+ true
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ def create_scope_method(meth)
20
+ if (scope = self.class.__scoped_methods__.find { |a| a.first == meth })
21
+ self.define_singleton_method(scope.first) do
22
+ scope(&scope.last)
23
+ end
24
+ end
25
+ end
26
+
27
+ def scope(&block)
28
+ result = instance_exec(&block)
29
+ if result.is_a? Array
30
+ __new_relation__(result)
31
+ else
32
+ result
33
+ end
34
+ end
35
+
36
+ private :create_scope_method
37
+
38
+ module ClassMethods
39
+ def scope(name, block)
40
+ __scoped_methods__ << [name, block]
41
+ end
42
+
43
+ def __scoped_methods__
44
+ @__scoped_methods__ ||= []
45
+ end
46
+ end
47
+
48
+ def self.included(base)
49
+ base.extend(ClassMethods)
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveEnumerable
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,99 @@
1
+ module ActiveEnumerable
2
+ module Where
3
+ class WhereNotChain
4
+ def initialize(collection, parent_class)
5
+ @collection = collection
6
+ @parent_class = parent_class
7
+ end
8
+
9
+ # Returns a new relation expressing WHERE + NOT condition according to
10
+ # the conditions in the arguments.
11
+ #
12
+ # #not accepts conditions as a string, array, or hash. See Where#where for
13
+ # more details on each format.
14
+ #
15
+ # <#ActiveEnumerable>.where.not(name: "Jon")
16
+ # <#ActiveEnumerable>.where.not(name: nil)
17
+ # <#ActiveEnumerable>.where.not(name: %w(Ko1 Nobu))
18
+ # <#ActiveEnumerable>.where.not(name: "Jon", role: "admin")
19
+ def not(conditions={})
20
+ @parent_class.call(@collection.reject do |record|
21
+ Finder.new(record).is_of(conditions)
22
+ end)
23
+ end
24
+ end
25
+
26
+ # Returns a new relation, which is the result of filtering the current relation
27
+ # according to the conditions in the arguments.
28
+ #
29
+ # === hash
30
+ #
31
+ # #where will accept a hash condition, in which the keys are fields and the values
32
+ # are values to be searched for.
33
+ #
34
+ # Fields can be symbols or strings. Values can be single values, arrays, or ranges.
35
+ #
36
+ # <#ActiveEnumerable>.where({ name: "Joe", email: "joe@example.com" })
37
+ #
38
+ # <#ActiveEnumerable>.where({ name: ["Alice", "Bob"]})
39
+ #
40
+ # <#ActiveEnumerable>.where({ created_at: (Time.now.midnight - 1.day)..Time.now.midnight })
41
+ #
42
+ # <#ActiveEnumerable>.where(contracts:[{ created_at: (Time.now.midnight - 1.day)..Time.now.midnight }])
43
+ #
44
+ # .or
45
+ #
46
+ # Returns a new relation, which is the logical union of this relation and the one passed as an
47
+ # argument.
48
+ #
49
+ # The two relations must be structurally compatible: they must be scoping the same model, and
50
+ # they must differ only by #where.
51
+ #
52
+ # <#ActiveEnumerable>.where(id: 1).or(<#ActiveEnumerable>.where(author_id: 3))
53
+ #
54
+ # Additional conditions can be passed to where in hash form.
55
+ #
56
+ # <#ActiveEnumerable>.where(id: 1).or(author_id: 3)
57
+ #
58
+ def where(conditions=nil)
59
+ return WhereNotChain.new(all, method(:__new_relation__)) if conditions.nil?
60
+ enable_or create_where_relation(conditions, to_a.select do |record|
61
+ Finder.new(record).is_of(conditions)
62
+ end)
63
+ end
64
+
65
+ def enable_or(relation)
66
+ pre_where_to_a = to_a
67
+ relation.define_singleton_method(:or) do |conditions_or_relation|
68
+ conditions = get_conditions(conditions_or_relation)
69
+ or_result = create_where_relation(where_conditions, pre_where_to_a).where(conditions)
70
+ create_where_relation(or_result.where_conditions, relation.to_a.concat(or_result.to_a).uniq)
71
+ end
72
+ relation
73
+ end
74
+
75
+ private :enable_or
76
+
77
+ def get_conditions(conditions_or_relation)
78
+ if conditions_or_relation.respond_to?(:where_conditions)
79
+ conditions_or_relation.where_conditions
80
+ else
81
+ conditions_or_relation
82
+ end
83
+ end
84
+
85
+ private :get_conditions
86
+
87
+ def where_conditions
88
+ @where_conditions ||= {}
89
+ end
90
+
91
+ def create_where_relation(conditions, array)
92
+ nr = __new_relation__(array)
93
+ nr.where_conditions.merge!(conditions)
94
+ nr
95
+ end
96
+
97
+ private :create_where_relation
98
+ end
99
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_enumerable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dustin Zeisler
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-01-01 00:00:00.000000000 Z
11
+ date: 2016-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -72,7 +72,16 @@ files:
72
72
  - bin/console
73
73
  - bin/setup
74
74
  - lib/active_enumerable.rb
75
+ - lib/active_enumerable/base.rb
76
+ - lib/active_enumerable/comparable.rb
77
+ - lib/active_enumerable/enumerable.rb
78
+ - lib/active_enumerable/finder.rb
79
+ - lib/active_enumerable/method_caller.rb
80
+ - lib/active_enumerable/queries.rb
81
+ - lib/active_enumerable/record_not_found.rb
82
+ - lib/active_enumerable/scopes.rb
75
83
  - lib/active_enumerable/version.rb
84
+ - lib/active_enumerable/where.rb
76
85
  homepage: https://github.com/zeisler/active_enumerable
77
86
  licenses:
78
87
  - MIT
@@ -93,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
102
  version: '0'
94
103
  requirements: []
95
104
  rubyforge_project:
96
- rubygems_version: 2.4.5.1
105
+ rubygems_version: 2.2.5
97
106
  signing_key:
98
107
  specification_version: 4
99
108
  summary: Gives ActiveRecord like querying methods to ruby enumerable objects.