activeset 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +0 -1
- data/CHANGELOG +5 -0
- data/Gemfile +5 -0
- data/README.md +24 -7
- data/activeset.gemspec +7 -1
- data/bin/console +1 -1
- data/config.ru +9 -0
- data/lib/active_set.rb +14 -6
- data/lib/active_set/{base_adapter.rb → processors/base_adapter.rb} +2 -2
- data/lib/active_set/{base_processor.rb → processors/base_processor.rb} +9 -0
- data/lib/active_set/processors/filter/active_record_adapter.rb +57 -0
- data/lib/active_set/processors/filter/enumerable_adapter.rb +33 -0
- data/lib/active_set/processors/filter_processor.rb +23 -0
- data/lib/active_set/{paginate → processors/paginate}/enumerable_adapter.rb +3 -3
- data/lib/active_set/processors/paginate_processor.rb +27 -0
- data/lib/active_set/processors/sort/active_record_adapter.rb +52 -0
- data/lib/active_set/processors/sort/enumerable_adapter.rb +30 -0
- data/lib/active_set/processors/sort_processor.rb +23 -0
- data/lib/active_set/structure/path.rb +54 -0
- data/lib/active_set/version.rb +1 -1
- metadata +103 -15
- data/lib/active_set/filter/enumerable_adapter.rb +0 -18
- data/lib/active_set/filter/processor.rb +0 -25
- data/lib/active_set/paginate/processor.rb +0 -29
- data/lib/active_set/sort/enumerable_adapter.rb +0 -17
- data/lib/active_set/sort/processor.rb +0 -25
- data/lib/active_set/structure_path.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46b1fb5697f15f8789f8d174bdee4e946862cbd1
|
4
|
+
data.tar.gz: 2c05138de95c75628c2ddea4d24508ddbd511248
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 788668ec2fb6bf0bc24db2d2675e9dfea2cd9e852cd4197d411e6113486a2b2ac88c165b74a0dc74b944c2246ab101f2137cdcb62797d5871019301e877fb090
|
7
|
+
data.tar.gz: 19a7388ad9014f195860aeb37732810a0dd5494d62a9721a0c4e18402d219ab81c004a4be97f4fe1ea9d835ab39ac0f6f915c472d29c37d13d39dbb92d296473
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/CHANGELOG
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,4 @@
|
|
1
|
-
#
|
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/activeset`. 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
|
1
|
+
# ActiveSet
|
6
2
|
|
7
3
|
## Installation
|
8
4
|
|
@@ -22,7 +18,28 @@ Or install it yourself as:
|
|
22
18
|
|
23
19
|
## Usage
|
24
20
|
|
25
|
-
|
21
|
+
The `ActiveSet` class is an extension of `Enumerable` that adds methods for filtering, sorting, and paginating (right now; other methods are sure to follow).
|
22
|
+
|
23
|
+
Every convenience method added to the `ActiveSet` class is handled via a `Processor` class, and that `Processor` class will then use 1 or more `Adapter` classes to actually fulfill the functional contract.
|
24
|
+
|
25
|
+
an `Adapter` for a particular `Processor` simply handles doing the job of the processor for a particular kind of Set.
|
26
|
+
|
27
|
+
So, for example, the `Filter::Processor` will have an `EnumerableAdapter` (to work with generic enumerable sets) and an `ActiveRecordAdapter` (to work with active record relations).
|
28
|
+
|
29
|
+
When calling a convenience method on an instance of `ActiveSet`, you pass only 1 argument: what I am currently calling a `structure`, which is a plain-old-Ruby-hash. Each convenience method works with hashes of differing signatures.
|
30
|
+
|
31
|
+
e.g. `filter(attribute: 'value', association: { field: 'value' })` or `sort(attribute: :asc, association: { field: 'desc' })` or `paginate(page: 1, size: 10)`
|
32
|
+
|
33
|
+
## Future Feature Ideas
|
34
|
+
|
35
|
+
- allow for filtering thru `to-many` associations (maybe via `associations[any]` or `associations[all]` syntax)
|
36
|
+
- allow for filtering without typecasting the value (maybe via `key*` syntax)
|
37
|
+
|
38
|
+
## Notes
|
39
|
+
|
40
|
+
This library does not deal with typecasting string values; the values of your structures must be of the appropriate data-type.
|
41
|
+
|
42
|
+
This library does not work with `:time` fields in ActiveRecord relations.
|
26
43
|
|
27
44
|
## Development
|
28
45
|
|
@@ -32,7 +49,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
49
|
|
33
50
|
## Contributing
|
34
51
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
52
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/fractaledmind/activeset.
|
36
53
|
|
37
54
|
## License
|
38
55
|
|
data/activeset.gemspec
CHANGED
@@ -24,9 +24,15 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
25
|
spec.require_paths = ['lib']
|
26
26
|
|
27
|
-
spec.add_dependency 'activesupport'
|
27
|
+
spec.add_dependency 'activesupport', '~> 5.1.3'
|
28
28
|
|
29
29
|
spec.add_development_dependency 'bundler', '~> 1.15'
|
30
30
|
spec.add_development_dependency 'rake', '~> 10.0'
|
31
31
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
32
|
+
spec.add_development_dependency 'database_cleaner', '~> 1.6.1'
|
33
|
+
spec.add_development_dependency 'combustion', '~> 0.7.0'
|
34
|
+
spec.add_development_dependency 'factory_girl', '~> 4.8.0'
|
35
|
+
spec.add_development_dependency 'faker', '~> 1.8.4'
|
36
|
+
spec.add_development_dependency 'simplecov', '~> 0.15.0'
|
37
|
+
spec.add_development_dependency 'simplecov-console', '~> 0.4.2'
|
32
38
|
end
|
data/bin/console
CHANGED
data/config.ru
ADDED
data/lib/active_set.rb
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'active_set/version'
|
4
4
|
|
5
|
-
require 'active_set/
|
6
|
-
require 'active_set/
|
7
|
-
require 'active_set/
|
5
|
+
require 'active_set/processors/filter_processor'
|
6
|
+
require 'active_set/processors/sort_processor'
|
7
|
+
require 'active_set/processors/paginate_processor'
|
8
8
|
|
9
9
|
class ActiveSet
|
10
10
|
include Enumerable
|
@@ -24,18 +24,26 @@ class ActiveSet
|
|
24
24
|
@set == other.set
|
25
25
|
end
|
26
26
|
|
27
|
+
def method_missing(method_name, *args, &block)
|
28
|
+
@set.send(method_name, *args, &block) || super
|
29
|
+
end
|
30
|
+
|
31
|
+
def respond_to_missing?(method_name, include_private = false)
|
32
|
+
@set.respond_to?(method_name) || super
|
33
|
+
end
|
34
|
+
|
27
35
|
def filter(structure)
|
28
|
-
filterer =
|
36
|
+
filterer = FilterProcessor.new(@set, structure)
|
29
37
|
self.class.new(filterer.process)
|
30
38
|
end
|
31
39
|
|
32
40
|
def sort(structure)
|
33
|
-
sorter =
|
41
|
+
sorter = SortProcessor.new(@set, structure)
|
34
42
|
self.class.new(sorter.process)
|
35
43
|
end
|
36
44
|
|
37
45
|
def paginate(structure)
|
38
|
-
paginater =
|
46
|
+
paginater = PaginateProcessor.new(@set, structure)
|
39
47
|
self.class.new(paginater.process)
|
40
48
|
end
|
41
49
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative '../structure/path'
|
4
4
|
|
5
5
|
class BaseAdapter
|
6
6
|
def initialize(keypath, value)
|
7
|
-
@structure_path =
|
7
|
+
@structure_path = Structure::Path.new(keypath)
|
8
8
|
@value = value
|
9
9
|
end
|
10
10
|
end
|
@@ -3,8 +3,17 @@
|
|
3
3
|
require 'patches/core_ext/hash/flatten_keys'
|
4
4
|
|
5
5
|
class BaseProcessor
|
6
|
+
def self.queue_adapter(adapter)
|
7
|
+
@adapters ||= []
|
8
|
+
@adapters |= [adapter]
|
9
|
+
end
|
10
|
+
|
6
11
|
def initialize(set, structure)
|
7
12
|
@set = set
|
8
13
|
@structure = structure.flatten_keys
|
9
14
|
end
|
15
|
+
|
16
|
+
def adapters
|
17
|
+
self.class.instance_variable_get(:@adapters)
|
18
|
+
end
|
10
19
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../base_adapter'
|
4
|
+
require_relative '../base_processor'
|
5
|
+
|
6
|
+
class ActiveSet
|
7
|
+
class FilterProcessor < BaseProcessor
|
8
|
+
class ActiveRecordAdapter < BaseAdapter
|
9
|
+
def process(set)
|
10
|
+
@set = set
|
11
|
+
return @set unless @set.respond_to? :to_sql
|
12
|
+
return @set unless attribute_is_field?
|
13
|
+
|
14
|
+
query
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def attribute_is_field?
|
20
|
+
return false unless attribute_model
|
21
|
+
attribute_model.attribute_names
|
22
|
+
.include?(@structure_path.attribute)
|
23
|
+
end
|
24
|
+
|
25
|
+
def query
|
26
|
+
@set.includes(@structure_path.to_h)
|
27
|
+
.references(@structure_path.to_h)
|
28
|
+
.where(arel_operation)
|
29
|
+
end
|
30
|
+
|
31
|
+
def arel_operation
|
32
|
+
Arel::Nodes::InfixOperation.new(@structure_path.operator,
|
33
|
+
arel_column,
|
34
|
+
arel_value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def attribute_model
|
38
|
+
@structure_path.to_a
|
39
|
+
.reduce(@set) do |obj, assoc|
|
40
|
+
obj.reflections[assoc.to_s]&.klass
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def arel_column
|
45
|
+
arel_table[@structure_path.attribute]
|
46
|
+
end
|
47
|
+
|
48
|
+
def arel_value
|
49
|
+
Arel.sql(ActiveRecord::Base.connection.quote(@value))
|
50
|
+
end
|
51
|
+
|
52
|
+
def arel_table
|
53
|
+
Arel::Table.new(attribute_model.table_name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../base_adapter'
|
4
|
+
|
5
|
+
class ActiveSet
|
6
|
+
class FilterProcessor < BaseProcessor
|
7
|
+
class EnumerableAdapter < BaseAdapter
|
8
|
+
def process(set)
|
9
|
+
set.select do |item|
|
10
|
+
value_for(item).send(@structure_path.operator,
|
11
|
+
passed_value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def value_for(item)
|
18
|
+
convert_datetime_values_to_integer(@structure_path.value_for(item: item))
|
19
|
+
end
|
20
|
+
|
21
|
+
def passed_value
|
22
|
+
convert_datetime_values_to_integer(@value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def convert_datetime_values_to_integer(value)
|
26
|
+
# DateTimes (and Times) are tricky to compare, Integers are easier
|
27
|
+
# and I only care about second precision
|
28
|
+
return value unless value.is_a?(DateTime)
|
29
|
+
value.to_i
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
|
5
|
+
require_relative './base_processor'
|
6
|
+
require_relative './filter/enumerable_adapter'
|
7
|
+
require_relative './filter/active_record_adapter'
|
8
|
+
|
9
|
+
class ActiveSet
|
10
|
+
class FilterProcessor < BaseProcessor
|
11
|
+
queue_adapter ActiveRecordAdapter
|
12
|
+
queue_adapter EnumerableAdapter
|
13
|
+
|
14
|
+
def process
|
15
|
+
adapters.reduce(@set) do |outer_set, adapter|
|
16
|
+
@structure.reject { |_, v| v.blank? }
|
17
|
+
.reduce(outer_set) do |inner_set, (keypath, value)|
|
18
|
+
adapter.new(keypath, value).process(inner_set)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../base_adapter'
|
4
|
+
require_relative '../base_processor'
|
4
5
|
|
5
6
|
class ActiveSet
|
6
|
-
|
7
|
+
class PaginateProcessor < BaseProcessor
|
7
8
|
class EnumerableAdapter < BaseAdapter
|
8
9
|
def process(set)
|
9
|
-
|
10
|
-
@set.each_slice(pagesize).take(page_number).last
|
10
|
+
set.each_slice(pagesize).take(page_number).last
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './base_processor'
|
4
|
+
require_relative './paginate/enumerable_adapter'
|
5
|
+
|
6
|
+
class ActiveSet
|
7
|
+
class PaginateProcessor < BaseProcessor
|
8
|
+
def process
|
9
|
+
return @set if @set.count < pagesize
|
10
|
+
adapter.new(page_number, pagesize).process(@set)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def adapter
|
16
|
+
EnumerableAdapter
|
17
|
+
end
|
18
|
+
|
19
|
+
def page_number
|
20
|
+
@structure[[:page]] || 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def pagesize
|
24
|
+
@structure[[:size]] || 25
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../base_adapter'
|
4
|
+
require_relative '../base_processor'
|
5
|
+
|
6
|
+
class ActiveSet
|
7
|
+
class SortProcessor < BaseProcessor
|
8
|
+
class ActiveRecordAdapter < BaseAdapter
|
9
|
+
def process(set)
|
10
|
+
@set = set
|
11
|
+
return @set unless @set.respond_to? :to_sql
|
12
|
+
return @set unless attribute_is_field?
|
13
|
+
|
14
|
+
@set.includes(@structure_path.to_h)
|
15
|
+
.references(@structure_path.to_h)
|
16
|
+
.merge(arel_operation)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def attribute_is_field?
|
22
|
+
return false unless attribute_model
|
23
|
+
attribute_model.attribute_names
|
24
|
+
.include?(@structure_path.attribute)
|
25
|
+
end
|
26
|
+
|
27
|
+
def case_insensitive?
|
28
|
+
@structure_path.operator.to_s == 'i'
|
29
|
+
end
|
30
|
+
|
31
|
+
def arel_operation
|
32
|
+
column = case_insensitive? ? arel_column.lower : arel_column
|
33
|
+
attribute_model.order(column.send(@value))
|
34
|
+
end
|
35
|
+
|
36
|
+
def attribute_model
|
37
|
+
@structure_path.to_a
|
38
|
+
.reduce(@set) do |obj, assoc|
|
39
|
+
obj.reflections[assoc.to_s]&.klass
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def arel_column
|
44
|
+
arel_table[@structure_path.attribute]
|
45
|
+
end
|
46
|
+
|
47
|
+
def arel_table
|
48
|
+
Arel::Table.new(attribute_model.table_name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../base_adapter'
|
4
|
+
require_relative '../base_processor'
|
5
|
+
|
6
|
+
class ActiveSet
|
7
|
+
class SortProcessor < BaseProcessor
|
8
|
+
class EnumerableAdapter < BaseAdapter
|
9
|
+
def process(set)
|
10
|
+
set.sort_by do |item|
|
11
|
+
attribute_value = @structure_path.value_for(item: item)
|
12
|
+
case_insensitive?(attribute_value) ? insensify(attribute_value) : attribute_value
|
13
|
+
end.tap { |c| c.reverse! if @value.to_s == 'desc' }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def case_insensitive?(value)
|
19
|
+
# Cannot sort pure Booleans or Nils, so we _must_ cast to Strings
|
20
|
+
return true if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
21
|
+
return true if value.is_a?(NilClass)
|
22
|
+
@structure_path.operator.to_s == 'i'
|
23
|
+
end
|
24
|
+
|
25
|
+
def insensify(value)
|
26
|
+
value.to_s.downcase
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
|
5
|
+
require_relative './base_processor'
|
6
|
+
require_relative './sort/enumerable_adapter'
|
7
|
+
require_relative './sort/active_record_adapter'
|
8
|
+
|
9
|
+
class ActiveSet
|
10
|
+
class SortProcessor < BaseProcessor
|
11
|
+
queue_adapter ActiveRecordAdapter
|
12
|
+
queue_adapter EnumerableAdapter
|
13
|
+
|
14
|
+
def process
|
15
|
+
adapters.reduce(@set) do |outer_set, adapter|
|
16
|
+
@structure.reject { |_, v| v.blank? }
|
17
|
+
.reduce(outer_set) do |inner_set, (keypath, value)|
|
18
|
+
adapter.new(keypath, value).process(inner_set)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/array/wrap'
|
4
|
+
require 'active_support/core_ext/object/try'
|
5
|
+
|
6
|
+
module Structure
|
7
|
+
class Path
|
8
|
+
attr_reader :path
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
# `path` can be an Array (e.g. [:parent, :child, :grandchild])
|
12
|
+
# or a String (e.g. 'parent.child.grandchild')
|
13
|
+
@path = path.is_a?(String) ? path.split('.') : Array.wrap(path).map(&:to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def attribute
|
17
|
+
attribute = @path.last
|
18
|
+
return attribute.sub(operator_regex, '') if attribute.match operator_regex
|
19
|
+
attribute
|
20
|
+
end
|
21
|
+
|
22
|
+
def operator
|
23
|
+
attribute = @path.last
|
24
|
+
return attribute[operator_regex, 1] if attribute.match operator_regex
|
25
|
+
'=='
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_a
|
29
|
+
@path.slice(0, @path.length - 1)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_h
|
33
|
+
to_a.reverse.reduce({}) { |a, e| { e => a } }
|
34
|
+
end
|
35
|
+
|
36
|
+
def value_for(item:)
|
37
|
+
resource_for(item: item).send(attribute)
|
38
|
+
rescue
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource_for(item:)
|
43
|
+
to_a.reduce(item, :try)
|
44
|
+
rescue
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def operator_regex
|
51
|
+
/\((.*?)\)/
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/active_set/version.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activeset
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Margheim
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.1.3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.1.3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +66,90 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: database_cleaner
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.6.1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.6.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: combustion
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.7.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.7.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: factory_girl
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 4.8.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 4.8.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: faker
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.8.4
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.8.4
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: simplecov
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.15.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.15.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: simplecov-console
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.4.2
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.4.2
|
69
153
|
description: Easily filter, sort, and paginate enumerable sets.
|
70
154
|
email:
|
71
155
|
- stephen.margheim@gmail.com
|
@@ -77,6 +161,7 @@ files:
|
|
77
161
|
- ".rspec"
|
78
162
|
- ".rubocop.yml"
|
79
163
|
- ".travis.yml"
|
164
|
+
- CHANGELOG
|
80
165
|
- Gemfile
|
81
166
|
- LICENSE.txt
|
82
167
|
- README.md
|
@@ -84,16 +169,19 @@ files:
|
|
84
169
|
- activeset.gemspec
|
85
170
|
- bin/console
|
86
171
|
- bin/setup
|
172
|
+
- config.ru
|
87
173
|
- lib/active_set.rb
|
88
|
-
- lib/active_set/base_adapter.rb
|
89
|
-
- lib/active_set/base_processor.rb
|
90
|
-
- lib/active_set/filter/
|
91
|
-
- lib/active_set/filter/
|
92
|
-
- lib/active_set/
|
93
|
-
- lib/active_set/paginate/
|
94
|
-
- lib/active_set/
|
95
|
-
- lib/active_set/sort/
|
96
|
-
- lib/active_set/
|
174
|
+
- lib/active_set/processors/base_adapter.rb
|
175
|
+
- lib/active_set/processors/base_processor.rb
|
176
|
+
- lib/active_set/processors/filter/active_record_adapter.rb
|
177
|
+
- lib/active_set/processors/filter/enumerable_adapter.rb
|
178
|
+
- lib/active_set/processors/filter_processor.rb
|
179
|
+
- lib/active_set/processors/paginate/enumerable_adapter.rb
|
180
|
+
- lib/active_set/processors/paginate_processor.rb
|
181
|
+
- lib/active_set/processors/sort/active_record_adapter.rb
|
182
|
+
- lib/active_set/processors/sort/enumerable_adapter.rb
|
183
|
+
- lib/active_set/processors/sort_processor.rb
|
184
|
+
- lib/active_set/structure/path.rb
|
97
185
|
- lib/active_set/version.rb
|
98
186
|
- lib/patches/core_ext/hash/flatten_keys.rb
|
99
187
|
homepage: https://github.com/fractaledmind/activeset
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../base_adapter'
|
4
|
-
|
5
|
-
class ActiveSet
|
6
|
-
module Filter
|
7
|
-
class EnumerableAdapter < BaseAdapter
|
8
|
-
def process(set)
|
9
|
-
@set = set
|
10
|
-
@set.select do |item|
|
11
|
-
attribute_value = @structure_path.value_for(item: item)
|
12
|
-
attribute_value.send(@structure_path.operator,
|
13
|
-
@value)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/object/blank'
|
4
|
-
|
5
|
-
require_relative '../base_processor'
|
6
|
-
require_relative './enumerable_adapter'
|
7
|
-
|
8
|
-
class ActiveSet
|
9
|
-
module Filter
|
10
|
-
class Processor < BaseProcessor
|
11
|
-
def process
|
12
|
-
@structure.reject { |_, v| v.blank? }
|
13
|
-
.reduce(@set) do |set, (keypath, value)|
|
14
|
-
adapter.new(keypath, value).process(set)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def adapter
|
21
|
-
EnumerableAdapter
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../base_processor'
|
4
|
-
require_relative './enumerable_adapter'
|
5
|
-
|
6
|
-
class ActiveSet
|
7
|
-
module Paginate
|
8
|
-
class Processor < BaseProcessor
|
9
|
-
def process
|
10
|
-
return @set if @set.count < pagesize
|
11
|
-
adapter.new(page_number, pagesize).process(@set)
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def adapter
|
17
|
-
EnumerableAdapter
|
18
|
-
end
|
19
|
-
|
20
|
-
def page_number
|
21
|
-
@structure[[:page]] || 1
|
22
|
-
end
|
23
|
-
|
24
|
-
def pagesize
|
25
|
-
@structure[[:size]] || 25
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../base_adapter'
|
4
|
-
|
5
|
-
class ActiveSet
|
6
|
-
module Sort
|
7
|
-
class EnumerableAdapter < BaseAdapter
|
8
|
-
def process(set)
|
9
|
-
@set = set
|
10
|
-
@set.sort_by do |item|
|
11
|
-
attribute_value = @structure_path.value_for(item: item)
|
12
|
-
attribute_value.is_a?(String) ? attribute_value.downcase : attribute_value
|
13
|
-
end.tap { |c| c.reverse! if @value.to_s == 'desc' }
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/object/blank'
|
4
|
-
|
5
|
-
require_relative '../base_processor'
|
6
|
-
require_relative './enumerable_adapter'
|
7
|
-
|
8
|
-
class ActiveSet
|
9
|
-
module Sort
|
10
|
-
class Processor < BaseProcessor
|
11
|
-
def process
|
12
|
-
@structure.reject { |_, value| value.blank? }
|
13
|
-
.reduce(@set) do |set, (keypath, value)|
|
14
|
-
adapter.new(keypath, value).process(set)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def adapter
|
21
|
-
EnumerableAdapter
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/array/wrap'
|
4
|
-
require 'active_support/core_ext/object/try'
|
5
|
-
|
6
|
-
class StructurePath
|
7
|
-
def initialize(path)
|
8
|
-
# `path` can be an Array (e.g. [:parent, :child, :grandchild])
|
9
|
-
# or a String (e.g. 'parent.child.grandchild')
|
10
|
-
@path = path.is_a?(String) ? path.split('.') : Array.wrap(path).map(&:to_s)
|
11
|
-
end
|
12
|
-
|
13
|
-
def attribute
|
14
|
-
attribute = @path.last
|
15
|
-
return attribute.sub(operator_regex, '') if attribute.match operator_regex
|
16
|
-
attribute
|
17
|
-
end
|
18
|
-
|
19
|
-
def operator
|
20
|
-
attribute = @path.last
|
21
|
-
return attribute[operator_regex, 1] if attribute.match operator_regex
|
22
|
-
'=='
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_a
|
26
|
-
@path.slice(0, @path.length - 1)
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_h
|
30
|
-
to_a.reverse.inject({}) { |a, e| { e => a } }
|
31
|
-
end
|
32
|
-
|
33
|
-
def value_for(item:)
|
34
|
-
@path.inject(item, :try)
|
35
|
-
end
|
36
|
-
|
37
|
-
def resource_for(item:)
|
38
|
-
to_a.inject(item, :try)
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def operator_regex
|
44
|
-
%r{\((.*?)\)}
|
45
|
-
end
|
46
|
-
end
|