activeset 0.5.8 → 0.6.0

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