activeset 0.3.1 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fc73d4a0f511ddca2d0c895b7867816dc571102f
4
- data.tar.gz: 02ac9b548790d30b2dac78a372b3ccc091769ed2
3
+ metadata.gz: eaba5e4aae16ae6f0dc4c2fad28b8c7177231448
4
+ data.tar.gz: 5d9b7ec416a8462f9a98db7f7aa04472357ccd9d
5
5
  SHA512:
6
- metadata.gz: 029232215b1cd1280f1b75757198bce5e97aa2e64603f2fa5c51aef8304407c7b56e1b0928a0e09fac080c9de272b00716842d94ad122a99f97d8f5337a1d58b
7
- data.tar.gz: e73777713e06505233491977d66b4f48966e3ce5a50b4b99dcc242f00db13349e10ac3166a6ac5fff0da0cfcad02fffcf61359cba737b6e20c63d8694df8216a
6
+ metadata.gz: 79079f1f927cb72e863a15a1f0ccee14a3cb3b2b3f7e32cc5b40e2dc71ee52c0b76ba4af21c46e8b98b896183c739f78662f58e16596fef10f5dccd829486df1
7
+ data.tar.gz: be55e192739fe5b3bdcb6a1a6775ec932310467440826c7362c102e2aaba0595facd02acba55f30d30f243ba26101a971f7758b37130dcfe47001196d0ef909b
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ v 0.4.0
2
+ - fix bug with filtering against falsey values
3
+ - remove hack of casting DateTime and Time objects to Integer for filtering
4
+ - add support for ActiveRecord :time columns
5
+ - ensure that an adapter is only run if necessary for a particular instruction; that is, try to process the instruction with the first adapter, if it succeeds, don't run any other adapters, if it doesn't, try the next adapter, etc.
6
+ - allow ActiveRecord sets to be filtered by a scope method
1
7
  v 0.3.1
2
8
  - Implement an Instructions object layer for handling the hashes passed into Processors
3
9
  v 0.3.0
@@ -32,18 +32,18 @@ class ActiveSet
32
32
  @set.respond_to?(method_name) || super
33
33
  end
34
34
 
35
- def filter(structure)
36
- filterer = FilterProcessor.new(@set, structure)
35
+ def filter(instructions)
36
+ filterer = FilterProcessor.new(@set, instructions)
37
37
  self.class.new(filterer.process)
38
38
  end
39
39
 
40
- def sort(structure)
41
- sorter = SortProcessor.new(@set, structure)
40
+ def sort(instructions)
41
+ sorter = SortProcessor.new(@set, instructions)
42
42
  self.class.new(sorter.process)
43
43
  end
44
44
 
45
- def paginate(structure)
46
- paginater = PaginateProcessor.new(@set, structure)
45
+ def paginate(instructions)
46
+ paginater = PaginateProcessor.new(@set, instructions)
47
47
  self.class.new(paginater.process)
48
48
  end
49
49
  end
@@ -8,19 +8,27 @@ class ActiveSet
8
8
 
9
9
  def initialize(hash)
10
10
  @hash = hash
11
- @flattened_hash = hash.flatten_keys
11
+ @flattened_hash = hash.flatten_keys.transform_keys { |k| k.map(&:to_s) }
12
12
  end
13
13
 
14
14
  def process_adapter(set:, adapter:)
15
- @flattened_hash.reject { |_, v| v.blank? }
16
- .reduce(set) do |inner_set, (keypath, value)|
17
- instruction = Entry.new(keypath, value)
18
- adapter.new(instruction).process(inner_set)
19
- end
15
+ @flattened_hash.reduce(set) do |inner_set, (keypath, value)|
16
+ instruction = Entry.new(keypath, value)
17
+ output = adapter.new(inner_set, instruction).process
18
+ remove_from_instruction_set(key: instruction.path) if output[:processed]
19
+
20
+ output[:set]
21
+ end
20
22
  end
21
23
 
22
24
  def get(keypath)
23
25
  @hash.dig(*keypath)
24
26
  end
27
+
28
+ private
29
+
30
+ def remove_from_instruction_set(key:)
31
+ @flattened_hash.delete(key)
32
+ end
25
33
  end
26
34
  end
@@ -10,7 +10,8 @@ class ActiveSet
10
10
  class Entry
11
11
  attr_reader :keypath
12
12
 
13
- delegate :attribute, :operator, :associations_array, :associations_hash, :value_for, :resource_for,
13
+ delegate :path, :attribute, :operator, :associations_array,
14
+ :associations_hash, :value_for, :resource_for,
14
15
  to: :keypath
15
16
 
16
17
  def initialize(keypath, value)
@@ -2,8 +2,16 @@
2
2
 
3
3
  class ActiveSet
4
4
  class BaseAdapter
5
- def initialize(instruction)
5
+ def initialize(set, instruction)
6
+ @set = set
6
7
  @instruction = instruction
7
8
  end
9
+
10
+ def return_set(set = nil)
11
+ {}.tap do |h|
12
+ h[:set] = set || @set
13
+ h[:processed] = set ? true : false
14
+ end
15
+ end
8
16
  end
9
17
  end
@@ -6,26 +6,40 @@ require_relative '../base_processor'
6
6
  class ActiveSet
7
7
  class FilterProcessor < BaseProcessor
8
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?
9
+ def process
10
+ return return_set unless @set.respond_to? :to_sql
11
+ return return_set(processed_query_set) if attribute_is_field?
12
+ return return_set(processed_scope_set) if attribute_is_class_method? && attribute_is_method_with_params?
13
13
 
14
- query
14
+ return_set
15
15
  end
16
16
 
17
17
  private
18
18
 
19
+ def processed_query_set
20
+ arel_eager_load_associations.where(arel_operation)
21
+ end
22
+
23
+ def processed_scope_set
24
+ tmp_results = attribute_model.public_send(@instruction.attribute, @instruction.value)
25
+ return @set unless tmp_results.is_a?(ActiveRecord::Relation)
26
+ arel_eager_load_associations.merge(tmp_results)
27
+ end
28
+
19
29
  def attribute_is_field?
20
30
  return false unless attribute_model
21
31
  attribute_model.attribute_names
22
32
  .include?(@instruction.attribute)
23
33
  end
24
34
 
25
- def query
26
- @set.includes(@instruction.associations_hash)
27
- .references(@instruction.associations_hash)
28
- .where(arel_operation)
35
+ def attribute_is_class_method?
36
+ return false unless attribute_model
37
+ attribute_model.respond_to?(@instruction.attribute)
38
+ end
39
+
40
+ def attribute_is_method_with_params?
41
+ return false unless attribute_model
42
+ attribute_model.method(@instruction.attribute).arity != 0
29
43
  end
30
44
 
31
45
  def arel_operation
@@ -41,6 +55,11 @@ class ActiveSet
41
55
  end
42
56
  end
43
57
 
58
+ def arel_eager_load_associations
59
+ @set.includes(@instruction.associations_hash)
60
+ .references(@instruction.associations_hash)
61
+ end
62
+
44
63
  def arel_column
45
64
  arel_table[@instruction.attribute]
46
65
  end
@@ -5,28 +5,22 @@ require_relative '../base_adapter'
5
5
  class ActiveSet
6
6
  class FilterProcessor < BaseProcessor
7
7
  class EnumerableAdapter < BaseAdapter
8
- def process(set)
9
- set.select do |item|
10
- value_for(item).send(@instruction.operator,
11
- passed_value)
12
- end
8
+ def process
9
+ return_set(filtered_set)
13
10
  end
14
11
 
15
12
  private
16
13
 
17
- def value_for(item)
18
- convert_datetime_values_to_integer(@instruction.value_for(item: item))
19
- end
20
-
21
- def passed_value
22
- convert_datetime_values_to_integer(@instruction.value)
14
+ def filtered_set
15
+ @set.select do |item|
16
+ select_comparison_for(item: item)
17
+ end
23
18
  end
24
19
 
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
20
+ def select_comparison_for(item:)
21
+ @instruction.value_for(item: item)
22
+ .send(@instruction.operator,
23
+ @instruction.value)
30
24
  end
31
25
  end
32
26
  end
@@ -6,12 +6,17 @@ require_relative '../base_processor'
6
6
  class ActiveSet
7
7
  class PaginateProcessor < BaseProcessor
8
8
  class EnumerableAdapter < BaseAdapter
9
- def process(set)
10
- set.each_slice(pagesize).take(page_number).last
9
+ def process
10
+ return return_set if @set.count < pagesize
11
+ return_set(paginated_set)
11
12
  end
12
13
 
13
14
  private
14
15
 
16
+ def paginated_set
17
+ @set.each_slice(pagesize).take(page_number).last
18
+ end
19
+
15
20
  def pagesize
16
21
  @instruction.value
17
22
  end
@@ -6,8 +6,7 @@ require_relative './paginate/enumerable_adapter'
6
6
  class ActiveSet
7
7
  class PaginateProcessor < BaseProcessor
8
8
  def process
9
- return @set if @set.count < pagesize
10
- adapter.new(instruction).process(@set)
9
+ adapter.new(@set, instruction).process
11
10
  end
12
11
 
13
12
  private
@@ -6,14 +6,11 @@ require_relative '../base_processor'
6
6
  class ActiveSet
7
7
  class SortProcessor < BaseProcessor
8
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?
9
+ def process
10
+ return return_set unless @set.respond_to? :to_sql
11
+ return return_set unless attribute_is_field?
13
12
 
14
- @set.includes(@instruction.associations_hash)
15
- .references(@instruction.associations_hash)
16
- .merge(arel_operation)
13
+ return_set(processed_set)
17
14
  end
18
15
 
19
16
  private
@@ -24,6 +21,12 @@ class ActiveSet
24
21
  .include?(@instruction.attribute)
25
22
  end
26
23
 
24
+ def processed_set
25
+ @set.includes(@instruction.associations_hash)
26
+ .references(@instruction.associations_hash)
27
+ .merge(arel_operation)
28
+ end
29
+
27
30
  def arel_operation
28
31
  column = case_insensitive? ? arel_column.lower : arel_column
29
32
  attribute_model.order(column.send(@instruction.value))
@@ -6,15 +6,22 @@ require_relative '../base_processor'
6
6
  class ActiveSet
7
7
  class SortProcessor < BaseProcessor
8
8
  class EnumerableAdapter < BaseAdapter
9
- def process(set)
10
- set.sort_by do |item|
11
- attribute_value = @instruction.value_for(item: item)
12
- case_insensitive?(attribute_value) ? insensify(attribute_value) : attribute_value
13
- end.tap { |c| c.reverse! if @instruction.value.to_s == 'desc' }
9
+ def process
10
+ return_set(sorted_set)
14
11
  end
15
12
 
16
13
  private
17
14
 
15
+ def sorted_set
16
+ @set.sort_by { |item| sortable_attribute_for(item: item) }
17
+ .tap { |c| c.reverse! if descending? }
18
+ end
19
+
20
+ def sortable_attribute_for(item:)
21
+ attribute_value = @instruction.value_for(item: item)
22
+ case_insensitive?(attribute_value) ? insensify(attribute_value) : attribute_value
23
+ end
24
+
18
25
  def case_insensitive?(value)
19
26
  # Cannot sort pure Booleans or Nils, so we _must_ cast to Strings
20
27
  return true if value.is_a?(TrueClass) || value.is_a?(FalseClass)
@@ -25,6 +32,10 @@ class ActiveSet
25
32
  def insensify(value)
26
33
  value.to_s.downcase
27
34
  end
35
+
36
+ def descending?
37
+ @instruction.value.to_s == 'desc'
38
+ end
28
39
  end
29
40
  end
30
41
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ActiveSet
4
- VERSION = '0.3.1'
4
+ VERSION = '0.4.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeset
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.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-20 00:00:00.000000000 Z
11
+ date: 2017-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport