activeset 0.5.8 → 0.6.0

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +2 -0
  3. data/lib/.DS_Store +0 -0
  4. data/lib/active_set.rb +24 -26
  5. data/lib/active_set/.DS_Store +0 -0
  6. data/lib/active_set/adapter_activerecord.rb +58 -0
  7. data/lib/active_set/adapter_base.rb +14 -0
  8. data/lib/active_set/instruction.rb +66 -0
  9. data/lib/active_set/instructions.rb +34 -0
  10. data/lib/active_set/processor_base.rb +28 -0
  11. data/lib/active_set/processor_filter.rb +21 -0
  12. data/lib/active_set/processor_filter/active_record_adapter.rb +50 -0
  13. data/lib/active_set/processor_filter/enumerable_adapter.rb +21 -0
  14. data/lib/active_set/processor_paginate.rb +35 -0
  15. data/lib/active_set/{processors/paginate → processor_paginate}/active_record_adapter.rb +11 -13
  16. data/lib/active_set/{processors/paginate → processor_paginate}/enumerable_adapter.rb +10 -12
  17. data/lib/active_set/processor_sort.rb +19 -0
  18. data/lib/active_set/processor_sort/active_record_adapter.rb +50 -0
  19. data/lib/active_set/processor_sort/enumerable_adapter.rb +58 -0
  20. data/lib/active_set/{processors/transform_processor.rb → processor_transform.rb} +6 -6
  21. data/lib/active_set/{processors/transform → processor_transform}/csv_adapter.rb +11 -14
  22. data/lib/active_set/version.rb +1 -1
  23. data/lib/helpers/throws.rb +19 -0
  24. metadata +21 -19
  25. data/lib/active_set/instructions/base.rb +0 -37
  26. data/lib/active_set/instructions/entry.rb +0 -27
  27. data/lib/active_set/instructions/entry/keypath.rb +0 -65
  28. data/lib/active_set/instructions/entry/value.rb +0 -15
  29. data/lib/active_set/processors/base_adapter.rb +0 -17
  30. data/lib/active_set/processors/base_processor.rb +0 -22
  31. data/lib/active_set/processors/filter/active_record_adapter.rb +0 -78
  32. data/lib/active_set/processors/filter/enumerable_adapter.rb +0 -27
  33. data/lib/active_set/processors/filter_processor.rb +0 -18
  34. data/lib/active_set/processors/paginate_processor.rb +0 -36
  35. data/lib/active_set/processors/sort/active_record_adapter.rb +0 -55
  36. data/lib/active_set/processors/sort/enumerable_adapter.rb +0 -41
  37. data/lib/active_set/processors/sort_processor.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 247cfa01133a97a1cd14001735f39c8893991ba2
4
- data.tar.gz: 4486e9c33939988777f2f48b34cd380025754088
3
+ metadata.gz: 8a2838aec338dd5f82088e61e90e37ad419a2039
4
+ data.tar.gz: 741731ac20fec572d99e2b7b62019af6116c89fc
5
5
  SHA512:
6
- metadata.gz: 1fc8a19b38aae78bd7e31e8ab26b670ef8fbdb6f6378c7bf7621f46e8e1b7e34f81f3c44a74c26916fe64d68686d548af0d20ec19ba572ebde493b92ee1b0309
7
- data.tar.gz: 49fdcd2b998a7ec0fdfdb1d9dd3febb284d17abadc54085145883e6a4209b6815a45979b0d0ed6388d048f809f3b6501cabbd7082a004b18cb372f0965029878
6
+ metadata.gz: 406349801340205ffc8035cc71540b8f03cfd89d67ef4c150b2c16fd94dc5838a16acdfee91dc5fd7c74b300fb4b5f39fbee79429b57c4411a2e9d87ee88a60b
7
+ data.tar.gz: 661719703c5b4b1fd8cee0459ef6e261fdf0094ea85dc855c96115ccaeda55bbb773d0eaf93edfc1d08f67544487dfc737d563b484dabfc608c8feafab49e0e5
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ v 0.6.0
2
+ - rewrite from the ground up
1
3
  v 0.5.8
2
4
  - Avoid a possible error path where relations with a '#union' would blow up when using '#merge' because the '#order' operation was being built with the relation and not with the relation's base class
3
5
  v 0.5.7
Binary file
@@ -2,67 +2,65 @@
2
2
 
3
3
  require 'active_set/version'
4
4
 
5
- require 'active_set/processors/filter_processor'
6
- require 'active_set/processors/sort_processor'
7
- require 'active_set/processors/paginate_processor'
8
- require 'active_set/processors/transform_processor'
5
+ require 'active_set/processor_filter'
6
+ require 'active_set/processor_sort'
7
+ require 'active_set/processor_paginate'
8
+ require 'active_set/processor_transform'
9
9
 
10
10
  class ActiveSet
11
11
  include Enumerable
12
12
 
13
- attr_reader :set, :instructions, :total_count
13
+ attr_reader :set, :view, :instructions
14
14
 
15
- def initialize(set, instructions: {}, total_count: nil)
15
+ def initialize(set, view: nil, instructions: {})
16
16
  @set = set
17
+ @view = view || set
17
18
  @instructions = instructions
18
- @total_count = total_count || @set.count
19
19
  end
20
20
 
21
21
  def each(&block)
22
- @set.each(&block)
22
+ @view.each(&block)
23
23
  end
24
24
 
25
25
  def ==(other)
26
- return @set == other unless other.is_a?(ActiveSet)
27
- @set == other.set
26
+ return @view == other unless other.is_a?(ActiveSet)
27
+ @view == other.view
28
28
  end
29
29
 
30
30
  def method_missing(method_name, *args, &block)
31
- @set.send(method_name, *args, &block) || super
31
+ @view.send(method_name, *args, &block) || super
32
32
  end
33
33
 
34
34
  def respond_to_missing?(method_name, include_private = false)
35
- @set.respond_to?(method_name) || super
35
+ @view.respond_to?(method_name) || super
36
36
  end
37
37
 
38
38
  def filter(instructions)
39
- filterer = FilterProcessor.new(@set, instructions)
40
- filtered_set = filterer.process
41
- new_active_set(filtered_set, :filter, instructions, filtered_set.count)
39
+ filterer = Processor::Filter.new(@view, instructions)
40
+ reinitialize(filterer.process, :filter, instructions)
42
41
  end
43
42
 
44
43
  def sort(instructions)
45
- sorter = SortProcessor.new(@set, instructions)
46
- new_active_set(sorter.process, :sort, instructions)
44
+ sorter = Processor::Sort.new(@view, instructions)
45
+ reinitialize(sorter.process, :sort, instructions)
47
46
  end
48
47
 
49
48
  def paginate(instructions)
50
- paginater = PaginateProcessor.new(@set, instructions)
51
- full_instructions = instructions.reverse_merge(page: paginater.send(:page_number),
52
- size: paginater.send(:page_size))
53
- new_active_set(paginater.process, :paginate, full_instructions)
49
+ paginater = Processor::Paginate.new(@view, instructions)
50
+ reinitialize(paginater.process, :paginate, instructions)
54
51
  end
55
52
 
56
53
  def transform(instructions)
57
- transformer = TransformProcessor.new(@set, instructions)
54
+ transformer = Processor::Transform.new(@view, instructions)
58
55
  transformer.process
59
56
  end
60
57
 
61
58
  private
62
59
 
63
- def new_active_set(set, method, instructions, total_count = nil)
64
- self.class.new(set,
65
- instructions: @instructions.merge(method => instructions),
66
- total_count: total_count || @total_count)
60
+ def reinitialize(processed_set, method, instructions)
61
+ self.class.new(@set,
62
+ view: processed_set,
63
+ instructions: @instructions.merge(
64
+ method => instructions))
67
65
  end
68
66
  end
Binary file
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './adapter_base'
4
+
5
+ class ActiveSet
6
+ class Adapter::ActiveRecord < Adapter::Base
7
+ private
8
+
9
+ def can_query_with_active_record?
10
+ attribute_is_field?
11
+ end
12
+
13
+ def can_merge_with_active_record?
14
+ attribute_is_class_method? &&
15
+ attribute_is_method_with_params?
16
+ end
17
+
18
+ def arel_eager_load_associations
19
+ @set.eager_load(instruction.associations_hash)
20
+ end
21
+
22
+ def arel_column
23
+ arel_table[instruction.attribute]
24
+ end
25
+
26
+ def arel_table
27
+ Arel::Table.new(attribute_model.table_name)
28
+ end
29
+
30
+ def attribute_is_field?
31
+ return false unless attribute_model
32
+
33
+ attribute_model.attribute_names
34
+ .include?(instruction.attribute)
35
+ end
36
+
37
+ def attribute_is_class_method?
38
+ return false unless attribute_model
39
+
40
+ attribute_model.respond_to?(instruction.attribute)
41
+ end
42
+
43
+ def attribute_is_method_with_params?
44
+ return false unless attribute_model
45
+
46
+ attribute_model.method(instruction.attribute).arity != 0
47
+ end
48
+
49
+ def attribute_model
50
+ tmp_model = instruction.associations_array
51
+ .reduce(@set) do |obj, assoc|
52
+ obj.reflections[assoc.to_s]&.klass
53
+ end
54
+ # return tmp_model.klass if tmp_model.is_a?(ActiveRecord::Relation)
55
+ # tmp_model
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActiveSet
4
+ module Adapter
5
+ class Base
6
+ attr_accessor :instruction
7
+
8
+ def initialize(set, instructions)
9
+ @set = set
10
+ @instructions = instructions
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/array/wrap'
4
+
5
+ class ActiveSet
6
+ class Instruction
7
+ attr_reader :keypath, :value
8
+
9
+ def initialize(keypath, value)
10
+ # `keypath` can be an Array (e.g. [:parent, :child, :grandchild])
11
+ # or a String (e.g. 'parent.child.grandchild')
12
+ @keypath = Array.wrap(keypath).map(&:to_s).flat_map { |x| x.split('.') }
13
+ @value = value
14
+ end
15
+
16
+ def attribute
17
+ attribute = @keypath.last
18
+ return attribute.sub(operator_regex, '') if attribute&.match operator_regex
19
+
20
+ attribute
21
+ end
22
+
23
+ def operator(default: '==')
24
+ attribute = @keypath.last
25
+ return attribute[operator_regex, 1] if attribute&.match operator_regex
26
+
27
+ default
28
+ end
29
+
30
+ def associations_array
31
+ return [] unless @keypath.any?
32
+
33
+ @keypath.slice(0, @keypath.length - 1)
34
+ end
35
+
36
+ def associations_hash
37
+ return {} unless @keypath.any?
38
+
39
+ associations_array.reverse.reduce({}) do |hash, association|
40
+ { association => hash }
41
+ end
42
+ end
43
+
44
+ def value_for(item:)
45
+ resource_for(item: item).public_send(attribute)
46
+ rescue
47
+ nil
48
+ end
49
+
50
+ def resource_for(item:)
51
+ associations_array.reduce(item) do |resource, association|
52
+ return nil unless resource.respond_to? association
53
+
54
+ resource.public_send(association)
55
+ end
56
+ rescue
57
+ nil
58
+ end
59
+
60
+ private
61
+
62
+ def operator_regex
63
+ /\((.*?)\)/
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../patches/core_ext/hash/flatten_keys'
4
+ require_relative './instruction'
5
+
6
+ class ActiveSet
7
+ class Instructions
8
+ include Enumerable
9
+
10
+ def initialize(hash)
11
+ @instructions = hash.flatten_keys.map do |keypath, value|
12
+ Instruction.new(keypath, value)
13
+ end
14
+ end
15
+
16
+ def each(&block)
17
+ @instructions.each(&block)
18
+ end
19
+
20
+ def method_missing(method_name, *args, &block)
21
+ @instructions.send(method_name, *args, &block) || super
22
+ end
23
+
24
+ def respond_to_missing?(method_name, include_private = false)
25
+ @instructions.respond_to?(method_name) || super
26
+ end
27
+
28
+ def get(key)
29
+ @instructions.find do |instruction|
30
+ instruction.attribute.to_s == key.to_s
31
+ end&.value
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './instructions'
4
+
5
+ class ActiveSet
6
+ module Processor
7
+ class Base
8
+ attr_reader :set, :instructions, :adapters
9
+
10
+ def self.register_adapter(adapter, precedence = 0)
11
+ @adapters ||= []
12
+ @adapters.insert(precedence, adapter)
13
+ end
14
+
15
+ def initialize(set, instructions)
16
+ @set = set
17
+ @instructions = Instructions.new(validate_instructions(instructions))
18
+ @adapters = self.class.instance_variable_get(:@adapters)
19
+ end
20
+
21
+ private
22
+
23
+ def validate_instructions(instructions)
24
+ instructions
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './processor_base'
4
+ require_relative './processor_filter/enumerable_adapter'
5
+ require_relative './processor_filter/active_record_adapter'
6
+
7
+ class ActiveSet
8
+ class Processor::Filter < Processor::Base
9
+ def process
10
+ @instructions.reduce(@set) do |set, instruction|
11
+ adapters.each do |adapter|
12
+ result = adapter.new(set, [instruction]).process
13
+ break(result) if result
14
+ end
15
+ end
16
+ end
17
+
18
+ register_adapter(EnumerableAdapter)
19
+ register_adapter(ActiveRecordAdapter)
20
+ end
21
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../adapter_activerecord'
4
+ require_relative '../processor_base'
5
+ require_relative '../../helpers/throws'
6
+
7
+ class ActiveSet
8
+ class Processor::Filter < Processor::Base
9
+ class ActiveRecordAdapter < Adapter::ActiveRecord
10
+ def process
11
+ return false unless @set.respond_to?(:to_sql)
12
+
13
+ if can_query_with_active_record?
14
+ statement = arel_eager_load_associations
15
+ .where(arel_operation)
16
+ elsif can_merge_with_active_record?
17
+ statement = arel_eager_load_associations
18
+ .merge(attribute_model.public_send(instruction.attribute,
19
+ instruction.value))
20
+ else
21
+ return false
22
+ end
23
+
24
+ return false if throws?(ActiveRecord::StatementInvalid) { statement.load }
25
+
26
+ statement
27
+ end
28
+
29
+ private
30
+
31
+ def instruction
32
+ @instructions.first
33
+ end
34
+
35
+ def arel_operation
36
+ Arel::Nodes::InfixOperation.new(arel_operator,
37
+ arel_column,
38
+ arel_value)
39
+ end
40
+
41
+ def arel_value
42
+ Arel.sql(ActiveRecord::Base.connection.quote(instruction.value))
43
+ end
44
+
45
+ def arel_operator
46
+ instruction.operator
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../adapter_base'
4
+
5
+ class ActiveSet
6
+ class Processor::Filter < Processor::Base
7
+ class EnumerableAdapter < Adapter::Base
8
+ def process
9
+ return false unless @set.respond_to? :select
10
+
11
+ @instructions.reduce(@set) do |set, instruction|
12
+ set.select do |item|
13
+ instruction.value_for(item: item)
14
+ .send(instruction.operator,
15
+ instruction.value)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './processor_base'
4
+ require_relative './processor_paginate/enumerable_adapter'
5
+ require_relative './processor_paginate/active_record_adapter'
6
+
7
+ class ActiveSet
8
+ class Processor::Paginate < Processor::Base
9
+ def process
10
+ adapters.each do |adapter|
11
+ result = adapter.new(@set, @instructions).process
12
+ break(result) if result
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def validate_instructions(instructions)
19
+ {
20
+ page: (instructions[:page] ||
21
+ instructions['page'] ||
22
+ 1).to_i,
23
+ size: (instructions[:size] ||
24
+ instructions['size'] ||
25
+ 25).to_i,
26
+ outset: (instructions[:outset] ||
27
+ instructions['outset'] ||
28
+ 0).to_i
29
+ }
30
+ end
31
+
32
+ register_adapter(EnumerableAdapter)
33
+ register_adapter(ActiveRecordAdapter)
34
+ end
35
+ end