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.
- checksums.yaml +4 -4
- data/CHANGELOG +2 -0
- data/lib/.DS_Store +0 -0
- data/lib/active_set.rb +24 -26
- data/lib/active_set/.DS_Store +0 -0
- data/lib/active_set/adapter_activerecord.rb +58 -0
- data/lib/active_set/adapter_base.rb +14 -0
- data/lib/active_set/instruction.rb +66 -0
- data/lib/active_set/instructions.rb +34 -0
- data/lib/active_set/processor_base.rb +28 -0
- data/lib/active_set/processor_filter.rb +21 -0
- data/lib/active_set/processor_filter/active_record_adapter.rb +50 -0
- data/lib/active_set/processor_filter/enumerable_adapter.rb +21 -0
- data/lib/active_set/processor_paginate.rb +35 -0
- data/lib/active_set/{processors/paginate → processor_paginate}/active_record_adapter.rb +11 -13
- data/lib/active_set/{processors/paginate → processor_paginate}/enumerable_adapter.rb +10 -12
- data/lib/active_set/processor_sort.rb +19 -0
- data/lib/active_set/processor_sort/active_record_adapter.rb +50 -0
- data/lib/active_set/processor_sort/enumerable_adapter.rb +58 -0
- data/lib/active_set/{processors/transform_processor.rb → processor_transform.rb} +6 -6
- data/lib/active_set/{processors/transform → processor_transform}/csv_adapter.rb +11 -14
- data/lib/active_set/version.rb +1 -1
- data/lib/helpers/throws.rb +19 -0
- metadata +21 -19
- data/lib/active_set/instructions/base.rb +0 -37
- data/lib/active_set/instructions/entry.rb +0 -27
- data/lib/active_set/instructions/entry/keypath.rb +0 -65
- data/lib/active_set/instructions/entry/value.rb +0 -15
- data/lib/active_set/processors/base_adapter.rb +0 -17
- data/lib/active_set/processors/base_processor.rb +0 -22
- data/lib/active_set/processors/filter/active_record_adapter.rb +0 -78
- data/lib/active_set/processors/filter/enumerable_adapter.rb +0 -27
- data/lib/active_set/processors/filter_processor.rb +0 -18
- data/lib/active_set/processors/paginate_processor.rb +0 -36
- data/lib/active_set/processors/sort/active_record_adapter.rb +0 -55
- data/lib/active_set/processors/sort/enumerable_adapter.rb +0 -41
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a2838aec338dd5f82088e61e90e37ad419a2039
|
4
|
+
data.tar.gz: 741731ac20fec572d99e2b7b62019af6116c89fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 406349801340205ffc8035cc71540b8f03cfd89d67ef4c150b2c16fd94dc5838a16acdfee91dc5fd7c74b300fb4b5f39fbee79429b57c4411a2e9d87ee88a60b
|
7
|
+
data.tar.gz: 661719703c5b4b1fd8cee0459ef6e261fdf0094ea85dc855c96115ccaeda55bbb773d0eaf93edfc1d08f67544487dfc737d563b484dabfc608c8feafab49e0e5
|
data/CHANGELOG
CHANGED
data/lib/.DS_Store
ADDED
Binary file
|
data/lib/active_set.rb
CHANGED
@@ -2,67 +2,65 @@
|
|
2
2
|
|
3
3
|
require 'active_set/version'
|
4
4
|
|
5
|
-
require 'active_set/
|
6
|
-
require 'active_set/
|
7
|
-
require 'active_set/
|
8
|
-
require 'active_set/
|
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, :
|
13
|
+
attr_reader :set, :view, :instructions
|
14
14
|
|
15
|
-
def initialize(set,
|
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
|
-
@
|
22
|
+
@view.each(&block)
|
23
23
|
end
|
24
24
|
|
25
25
|
def ==(other)
|
26
|
-
return @
|
27
|
-
@
|
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
|
-
@
|
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
|
-
@
|
35
|
+
@view.respond_to?(method_name) || super
|
36
36
|
end
|
37
37
|
|
38
38
|
def filter(instructions)
|
39
|
-
filterer =
|
40
|
-
|
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 =
|
46
|
-
|
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 =
|
51
|
-
|
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 =
|
54
|
+
transformer = Processor::Transform.new(@view, instructions)
|
58
55
|
transformer.process
|
59
56
|
end
|
60
57
|
|
61
58
|
private
|
62
59
|
|
63
|
-
def
|
64
|
-
self.class.new(set,
|
65
|
-
|
66
|
-
|
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,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
|