activeset 0.3.1 → 0.4.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: 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