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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31f1479fd17f9303641a356e7db5697e2b5b113c
4
- data.tar.gz: a65c103541374642713b8a27fbeb7b0e4b09518e
3
+ metadata.gz: 46b1fb5697f15f8789f8d174bdee4e946862cbd1
4
+ data.tar.gz: 2c05138de95c75628c2ddea4d24508ddbd511248
5
5
  SHA512:
6
- metadata.gz: e089a327e4e5ce48a62c38b47889e5f696e1cf4641da3c69f04ffa9a69bc93ae1cdc98a6c0a36258207f0d4e3ed190bdc9c7d3e80eb562436cbc11a40e8ecdcc
7
- data.tar.gz: 1c9781eaa496a291f361ca10e3e3ee44e6adf66d6353c00b8810d15b0962aeef0270241a3edf71fd9f2f5538f861757c13cfd7fffd6f9967593c37d748cf897f
6
+ metadata.gz: 788668ec2fb6bf0bc24db2d2675e9dfea2cd9e852cd4197d411e6113486a2b2ac88c165b74a0dc74b944c2246ab101f2137cdcb62797d5871019301e877fb090
7
+ data.tar.gz: 19a7388ad9014f195860aeb37732810a0dd5494d62a9721a0c4e18402d219ab81c004a4be97f4fe1ea9d835ab39ac0f6f915c472d29c37d13d39dbb92d296473
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
13
  *.gem
14
+ *.sqlite
data/.rspec CHANGED
@@ -1,2 +1 @@
1
- --format documentation
2
1
  --color
@@ -0,0 +1,5 @@
1
+ v 0.2.0
2
+ - allows for filtering and sorting enumerable collections via associated attributes
3
+ v 0.1.0
4
+ - initial version
5
+ - allows for filtering, sorting, and pagination enumerable collections via direct attributes
data/Gemfile CHANGED
@@ -6,3 +6,8 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
7
  # Specify your gem's dependencies in activeset.gemspec
8
8
  gemspec
9
+
10
+ group :test do
11
+ gem 'activerecord'
12
+ gem 'sqlite3'
13
+ end
data/README.md CHANGED
@@ -1,8 +1,4 @@
1
- # Activeset
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
- TODO: Write usage instructions here
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/[USERNAME]/activeset.
52
+ Bug reports and pull requests are welcome on GitHub at https://github.com/fractaledmind/activeset.
36
53
 
37
54
  ## License
38
55
 
@@ -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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'bundler/setup'
4
- require 'activeset'
4
+ require 'active_set'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+
6
+ Bundler.require :default, :development
7
+
8
+ Combustion.initialize! :all
9
+ run Combustion::Application
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'active_set/version'
4
4
 
5
- require 'active_set/filter/processor'
6
- require 'active_set/sort/processor'
7
- require 'active_set/paginate/processor'
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 = Filter::Processor.new(@set, structure)
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 = Sort::Processor.new(@set, structure)
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 = Paginate::Processor.new(@set, structure)
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 './structure_path'
3
+ require_relative '../structure/path'
4
4
 
5
5
  class BaseAdapter
6
6
  def initialize(keypath, value)
7
- @structure_path = StructurePath.new(keypath)
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
- module Paginate
7
+ class PaginateProcessor < BaseProcessor
7
8
  class EnumerableAdapter < BaseAdapter
8
9
  def process(set)
9
- @set = set
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ActiveSet
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
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.2.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-09 00:00:00.000000000 Z
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: '0'
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: '0'
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/enumerable_adapter.rb
91
- - lib/active_set/filter/processor.rb
92
- - lib/active_set/paginate/enumerable_adapter.rb
93
- - lib/active_set/paginate/processor.rb
94
- - lib/active_set/sort/enumerable_adapter.rb
95
- - lib/active_set/sort/processor.rb
96
- - lib/active_set/structure_path.rb
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