actionset 0.8.2 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +24 -0
  3. data/Gemfile.lock +1 -1
  4. data/Rakefile +7 -1
  5. data/actionset.gemspec +1 -1
  6. data/lib/action_set.rb +36 -7
  7. data/lib/action_set/attribute_value.rb +7 -1
  8. data/lib/action_set/helpers/helper_methods.rb +2 -1
  9. data/lib/action_set/helpers/pagination/record_description_for_helper.rb +20 -0
  10. data/lib/action_set/helpers/pagination/record_first_for_helper.rb +20 -0
  11. data/lib/action_set/helpers/pagination/record_last_for_helper.rb +26 -0
  12. data/lib/action_set/helpers/pagination/record_range_for_helper.rb +25 -0
  13. data/lib/action_set/helpers/pagination/record_size_for_helper.rb +9 -0
  14. data/lib/action_set/helpers/pagination/total_pages_for_helper.rb +3 -1
  15. data/lib/active_set/active_record_set_instruction.rb +30 -37
  16. data/lib/active_set/attribute_instruction.rb +2 -2
  17. data/lib/active_set/enumerable_set_instruction.rb +13 -26
  18. data/lib/active_set/filtering/active_record/operators.rb +277 -0
  19. data/lib/active_set/filtering/active_record/query_column.rb +35 -0
  20. data/lib/active_set/filtering/active_record/query_value.rb +47 -0
  21. data/lib/active_set/filtering/active_record/set_instruction.rb +29 -0
  22. data/lib/active_set/filtering/active_record/strategy.rb +87 -0
  23. data/lib/active_set/filtering/constants.rb +349 -0
  24. data/lib/active_set/filtering/enumerable/operators.rb +308 -0
  25. data/lib/active_set/filtering/enumerable/set_instruction.rb +98 -0
  26. data/lib/active_set/filtering/enumerable/strategy.rb +90 -0
  27. data/lib/active_set/filtering/operation.rb +4 -4
  28. data/lib/helpers/flatten_keys_of.rb +1 -1
  29. metadata +16 -4
  30. data/lib/active_set/filtering/active_record_strategy.rb +0 -85
  31. data/lib/active_set/filtering/enumerable_strategy.rb +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dffd9096dbb67844e4cee728dee2dbea434a32e2
4
- data.tar.gz: a6bcb0b49df525a8d3e6e74f06a067310674fd87
3
+ metadata.gz: 1eb88ba89dc96b42af254762a42011c32153b788
4
+ data.tar.gz: 174a5c1c743fff20cf343ca82abe3461a040d342
5
5
  SHA512:
6
- metadata.gz: f7400ae1649bde246c247752feabb37adc3c290ce464aee8271980c265965be920ac2cdae6c06b009fbbc59df371ec7e1aad075f4035a2e624ead3f6254e0011
7
- data.tar.gz: ca373cfc982b4455ec40c5df823c94ccc4a16e0c2fc1fafeb2eb3cc9ea65b8be664df7135d6ef52c9e1b5cca12e959ae23eda6815809cb62e93baae0acec5d96
6
+ metadata.gz: 5c39ddbc29032e67bb292ce87c79734a399dab2cf4467aa0e63168036a794908b840cc544394e24eb6da3c238293cded4a18b867591a0cfbef7e72b812990404
7
+ data.tar.gz: e982edf3262db42fcb15d871047a33d79a35344841e8a218d6b485d10315fc2cdf3b52d8f63303f9def653220a52ff318b05e69c94f9e8bf352dc59492ffb301
data/CHANGELOG CHANGED
@@ -1,3 +1,27 @@
1
+ v 0.9.1
2
+ - ensure that our RubyGems build is stable, since we had a TravisCI build problem in the last version
3
+ v 0.9.0
4
+ - Add Enumerable support for the base filtering operations
5
+ + EQ, NOT_EQ, EQ_ANY, EQ_ALL, NOT_EQ_ANY, NOT_EQ_ALL
6
+ + IN, NOT_IN, IN_ANY, IN_ALL, NOT_IN_ANY, NOT_IN_ALL
7
+ + MATCHES, DOES_NOT_MATCH, MATCHES_ANY, MATCHES_ALL, DOES_NOT_MATCH_ANY, DOES_NOT_MATCH_ALL
8
+ + LT, LTEQ, LT_ANY, LT_ALL, LTEQ_ANY, LTEQ_ALL
9
+ + GT, GTEQ, GT_ANY, GT_ALL, GTEQ_ANY, GTEQ_ALL
10
+ + BETWEEN, NOT_BETWEEN
11
+ - Add unary predicate filtering operators, for both ActiveRecord and Enumerable
12
+ + IS_TRUE, IS_FALSE
13
+ + IS_NULL, NOT_NULL
14
+ + IS_PRESENT, IS_BLANK
15
+ - Add computed matcher filtering operators, for both ActiveRecord and Enumerable
16
+ + MATCH_START, MATCH_START_ANY, MATCH_START_ALL, MATCH_NOT_START, MATCH_NOT_START_ANY, MATCH_NOT_START_ALL
17
+ + MATCH_END, MATCH_END_ANY, MATCH_END_ALL, MATCH_NOT_END, MATCH_NOT_END_ANY, MATCH_NOT_END_ALL
18
+ + MATCH_CONTAIN, MATCH_CONTAIN_ANY, MATCH_CONTAIN_ALL, MATCH_NOT_CONTAIN, MATCH_NOT_CONTAIN_ANY, MATCH_NOT_CONTAIN_ALL
19
+ - Add view helpers for the range of records shown on the current page
20
+ - Use ActiveRecord's data dictionary to look up database column types when converting filter params to filter instructions
21
+ - Allow app to define type hints for filter attributes when converting filter params to filter instructions
22
+ - Allow sort params to be passed in short or long form
23
+ + e.g. { attribute: x, direction: x } or { attribute: direction }
24
+ - Fix enumerable intersection filtering when working across associations
1
25
  v 0.8.2
2
26
  - add `ActiveSet.configuration.on_asc_sort_nils_come` configuration
3
27
  v 0.8.1
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- actionset (0.8.2)
4
+ actionset (0.9.1)
5
5
  activesupport (>= 4.0.2)
6
6
  railties
7
7
 
data/Rakefile CHANGED
@@ -5,4 +5,10 @@ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- task default: :spec
8
+ task :full_spec do
9
+ ENV['COVERAGE'] = 'true'
10
+ ENV['INSPECT_FAILURE'] = 'true'
11
+ Rake::Task['spec'].invoke
12
+ end
13
+
14
+ task default: :full_spec
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
6
  Gem::Specification.new do |spec|
7
7
  spec.platform = Gem::Platform::RUBY
8
8
  spec.name = 'actionset'
9
- spec.version = '0.8.2'
9
+ spec.version = '0.9.1'
10
10
  spec.authors = ['Stephen Margheim']
11
11
  spec.email = ['stephen.margheim@gmail.com']
12
12
 
@@ -30,7 +30,7 @@ module ActionSet
30
30
 
31
31
  def sort_set(set)
32
32
  active_set = ensure_active_set(set)
33
- active_set = active_set.sort(sort_params) if sort_params.any?
33
+ active_set = active_set.sort(sort_instructions) if sort_params.any?
34
34
  active_set
35
35
  end
36
36
 
@@ -64,18 +64,19 @@ module ActionSet
64
64
  end
65
65
  end
66
66
 
67
- def filter_typecasted_value_for(keypath, value, set)
68
- instruction = ActiveSet::AttributeInstruction.new(keypath, value)
69
- item_with_value = set.find { |i| !instruction.value_for(item: i).nil? }
70
- item_value = instruction.value_for(item: item_with_value)
71
- ActionSet::AttributeValue.new(value)
72
- .cast(to: item_value.class)
67
+ def sort_instructions
68
+ if sort_params.key?(:attribute) && sort_params.key?(:direction)
69
+ { sort_params[:attribute] => sort_params[:direction] }
70
+ else
71
+ sort_params.transform_values { |v| v.remove('ending') }
72
+ end
73
73
  end
74
74
 
75
75
  def paginate_instructions
76
76
  paginate_params.transform_values(&:to_i)
77
77
  end
78
78
 
79
+ # rubocop:disable Metrics/AbcSize
79
80
  def export_instructions
80
81
  {}.tap do |struct|
81
82
  struct[:format] = export_params[:format] || request.format.symbol
@@ -93,10 +94,38 @@ module ActionSet
93
94
  elsif respond_to?(:export_set_columns, true)
94
95
  send(:export_set_columns)
95
96
  else
97
+ # :nocov:
96
98
  [{}]
99
+ # :nocov:
97
100
  end
98
101
  end
99
102
  end
103
+ # rubocop:enable Metrics/AbcSize
104
+
105
+ def filter_typecasted_value_for(keypath, value, set)
106
+ klass = klass_for_keypath(keypath, value, set)
107
+ ActionSet::AttributeValue.new(value)
108
+ .cast(to: klass)
109
+ end
110
+
111
+ def klass_for_keypath(keypath, value, set)
112
+ if respond_to?(:filter_set_types, true)
113
+ type_declarations = public_send(:filter_set_types)
114
+ types = type_declarations['types'] || type_declarations[:types]
115
+ klass = types[keypath.join('.')]
116
+ return klass if klass
117
+ end
118
+
119
+ if set.is_a?(ActiveRecord::Relation) || set.view.is_a?(ActiveRecord::Relation)
120
+ klass_type = set.model.columns_hash.fetch(keypath, nil)&.type
121
+ return klass_type.class if klass_type
122
+ end
123
+
124
+ instruction = ActiveSet::AttributeInstruction.new(keypath, value)
125
+ item_with_value = set.find { |i| !instruction.value_for(item: i).nil? }
126
+ item_value = instruction.value_for(item: item_with_value)
127
+ item_value.class
128
+ end
100
129
 
101
130
  def filter_params
102
131
  params.fetch(:filter, {}).to_unsafe_hash
@@ -11,7 +11,7 @@ module ActionSet
11
11
  def cast(to:)
12
12
  adapters.reduce(nil) do |_, adapter|
13
13
  mayble_value_or_nil = adapter.new(@raw, to).process
14
- next if mayble_value_or_nil.nil?
14
+ next nil if mayble_value_or_nil.nil?
15
15
 
16
16
  return mayble_value_or_nil
17
17
  end
@@ -62,7 +62,9 @@ module ActionSet
62
62
  begin
63
63
  require 'active_model/type'
64
64
  rescue LoadError
65
+ # :nocov:
65
66
  require 'active_record/type'
67
+ # :nocov:
66
68
  end
67
69
 
68
70
  def initialize(raw, target)
@@ -106,13 +108,17 @@ module ActionSet
106
108
  def init_typecaster(const_name)
107
109
  type_class.const_get(const_name).new
108
110
  rescue StandardError
111
+ # :nocov:
109
112
  nil
113
+ # :nocov:
110
114
  end
111
115
 
112
116
  def type_class
113
117
  ActiveModel::Type
114
118
  rescue NameError
119
+ # :nocov:
115
120
  ActiveRecord::Type
121
+ # :nocov:
116
122
  end
117
123
  end
118
124
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative './sort/link_for_helper'
4
4
  require_relative './pagination/links_for_helper'
5
- require_relative './pagination/path_for_helper'
5
+ require_relative './pagination/record_description_for_helper'
6
6
  require_relative './params/form_for_object_helper'
7
7
  require_relative './export/path_for_helper'
8
8
 
@@ -11,6 +11,7 @@ module ActionSet
11
11
  module HelperMethods
12
12
  include Sort::LinkForHelper
13
13
  include Pagination::LinksForHelper
14
+ include Pagination::RecordDescriptionForHelper
14
15
  include Params::FormForObjectHelper
15
16
  include Export::PathForHelper
16
17
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './record_range_for_helper'
4
+ require_relative './record_size_for_helper'
5
+
6
+ module Pagination
7
+ module RecordDescriptionForHelper
8
+ include Pagination::RecordRangeForHelper
9
+ include Pagination::RecordSizeForHelper
10
+
11
+ def pagination_record_description_for(set)
12
+ [
13
+ pagination_record_range_for(set),
14
+ 'of',
15
+ "<strong>#{pagination_record_size_for(set)}</strong>",
16
+ 'records'
17
+ ].join('&nbsp;').html_safe
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './current_page_for_helper'
4
+ require_relative './page_size_for_helper'
5
+
6
+ module Pagination
7
+ module RecordFirstForHelper
8
+ include Pagination::CurrentPageForHelper
9
+ include Pagination::PageSizeForHelper
10
+
11
+ def pagination_record_first_for(set)
12
+ current_page = pagination_current_page_for(set)
13
+ page_size = pagination_page_size_for(set)
14
+
15
+ return 1 if current_page == 1
16
+
17
+ ((current_page - 1) * page_size) + 1
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './current_page_for_helper'
4
+ require_relative './record_size_for_helper'
5
+ require_relative './page_size_for_helper'
6
+ require_relative './total_pages_for_helper'
7
+
8
+ module Pagination
9
+ module RecordLastForHelper
10
+ include Pagination::RecordSizeForHelper
11
+ include Pagination::CurrentPageForHelper
12
+ include Pagination::PageSizeForHelper
13
+ include Pagination::TotalPagesForHelper
14
+
15
+ def pagination_record_last_for(set)
16
+ record_size = pagination_record_size_for(set)
17
+ current_page = pagination_current_page_for(set)
18
+ page_size = pagination_page_size_for(set)
19
+ total_pages = pagination_total_pages_for(set)
20
+
21
+ return record_size if current_page >= total_pages
22
+
23
+ current_page * page_size
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './record_first_for_helper'
4
+ require_relative './record_last_for_helper'
5
+
6
+ module Pagination
7
+ module RecordRangeForHelper
8
+ include Pagination::CurrentPageForHelper
9
+ include Pagination::TotalPagesForHelper
10
+ include Pagination::RecordFirstForHelper
11
+ include Pagination::RecordLastForHelper
12
+
13
+ def pagination_record_range_for(set)
14
+ current_page = pagination_current_page_for(set)
15
+ total_pages = pagination_total_pages_for(set)
16
+ return 'None' if current_page > total_pages
17
+
18
+ [
19
+ pagination_record_first_for(set),
20
+ '&ndash;',
21
+ pagination_record_last_for(set)
22
+ ].join('&nbsp;').html_safe
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pagination
4
+ module RecordSizeForHelper
5
+ def pagination_record_size_for(set)
6
+ set.instructions.dig(:paginate, :count)
7
+ end
8
+ end
9
+ end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './record_size_for_helper'
3
4
  require_relative './page_size_for_helper'
4
5
 
5
6
  module Pagination
6
7
  module TotalPagesForHelper
8
+ include Pagination::RecordSizeForHelper
7
9
  include Pagination::PageSizeForHelper
8
10
 
9
11
  def pagination_total_pages_for(set)
10
- total_set_size = set.instructions.dig(:paginate, :count)
12
+ total_set_size = pagination_record_size_for(set)
11
13
  return 1 if total_set_size.zero?
12
14
 
13
15
  (total_set_size.to_f / pagination_page_size_for(set)).ceil
@@ -9,15 +9,41 @@ class ActiveSet
9
9
  end
10
10
 
11
11
  def initial_relation
12
- return @set if @attribute_instruction.associations_array.empty?
12
+ return @initial_relation if defined? @initial_relation
13
13
 
14
- @set.eager_load(@attribute_instruction.associations_hash)
14
+ @initial_relation = if @attribute_instruction.associations_array.empty?
15
+ @set
16
+ else
17
+ @set.eager_load(@attribute_instruction.associations_hash)
18
+ end
15
19
  end
16
20
 
21
+ def arel_column
22
+ return @arel_column if defined? @arel_column
23
+
24
+ arel_column = arel_table[@attribute_instruction.attribute]
25
+ arel_column = arel_column.lower if case_insensitive_operation?
26
+
27
+ @arel_column = arel_column
28
+ end
29
+
30
+ def attribute_model
31
+ return @set.klass if @attribute_instruction.associations_array.empty?
32
+ return @attribute_model if defined? @attribute_model
33
+
34
+ @attribute_model = @attribute_instruction
35
+ .associations_array
36
+ .reduce(@set) do |obj, assoc|
37
+ obj.reflections[assoc.to_s]&.klass
38
+ end
39
+ end
40
+
41
+ private
42
+
17
43
  def arel_type
18
44
  attribute_model
19
- .columns_hash[@attribute_instruction.attribute]
20
- .type
45
+ &.columns_hash[@attribute_instruction.attribute]
46
+ &.type
21
47
  end
22
48
 
23
49
  def arel_table
@@ -31,41 +57,8 @@ class ActiveSet
31
57
  end
32
58
  end
33
59
 
34
- # rubocop:disable Lint/UnderscorePrefixedVariableName
35
- def arel_column
36
- _arel_column = arel_table[@attribute_instruction.attribute]
37
- return _arel_column.lower if case_insensitive_operation?
38
-
39
- _arel_column
40
- end
41
- # rubocop:enable Lint/UnderscorePrefixedVariableName
42
-
43
- def arel_operator
44
- @attribute_instruction.operator(default: :eq)
45
- end
46
-
47
- # rubocop:disable Lint/UnderscorePrefixedVariableName
48
- def arel_value
49
- _arel_value = @attribute_instruction.value
50
- return _arel_value.downcase if case_insensitive_operation?
51
-
52
- _arel_value
53
- end
54
- # rubocop:enable Lint/UnderscorePrefixedVariableName
55
-
56
60
  def case_insensitive_operation?
57
61
  @attribute_instruction.case_insensitive? && arel_type.presence_in(%i[string text])
58
62
  end
59
-
60
- def attribute_model
61
- return @set.klass if @attribute_instruction.associations_array.empty?
62
- return @attribute_model if defined? @attribute_model
63
-
64
- @attribute_model = @attribute_instruction
65
- .associations_array
66
- .reduce(@set) do |obj, assoc|
67
- obj.reflections[assoc.to_s]&.klass
68
- end
69
- end
70
63
  end
71
64
  end
@@ -32,11 +32,11 @@ class ActiveSet
32
32
  @attribute = attribute
33
33
  end
34
34
 
35
- def operator(default: '==')
35
+ def operator
36
36
  return @operator if defined? @operator
37
37
 
38
38
  attribute_instruction = @keypath.last
39
- @operator = (attribute_instruction[operator_regex, 1] || default).to_sym
39
+ @operator = attribute_instruction[operator_regex, 1]&.to_sym
40
40
  end
41
41
 
42
42
  def options
@@ -9,40 +9,27 @@ class ActiveSet
9
9
  end
10
10
 
11
11
  def attribute_value_for(item)
12
- item_value = @attribute_instruction
13
- .value_for(item: item)
14
- item_value = item_value.downcase if case_insensitive_operation_for?(item_value)
15
- item_value
12
+ @item_values ||= Hash.new do |h, key|
13
+ item_value = @attribute_instruction.value_for(item: key)
14
+ item_value = item_value.downcase if case_insensitive_operation_for?(item_value)
15
+ h[key] = item_value
16
+ end
17
+
18
+ @item_values[item]
16
19
  end
17
20
 
18
- # rubocop:disable Lint/UnderscorePrefixedVariableName
19
- def attribute_value
20
- _attribute_value = @attribute_instruction.value
21
- _attribute_value = _attribute_value.downcase if case_insensitive_operation_for?(_attribute_value)
22
- _attribute_value
21
+ def instruction_value
22
+ return @instruction_value if defined? @instruction_value
23
+
24
+ instruction_value = @attribute_instruction.value
25
+ instruction_value = instruction_value.downcase if case_insensitive_operation_for?(instruction_value)
26
+ @instruction_value = instruction_value
23
27
  end
24
- # rubocop:enable Lint/UnderscorePrefixedVariableName
25
28
 
26
29
  def case_insensitive_operation_for?(value)
27
30
  return false unless @attribute_instruction.case_insensitive?
28
31
 
29
32
  value.is_a?(String) || value.is_a?(Symbol)
30
33
  end
31
-
32
- def attribute_instance
33
- set_item = @set.find(&:present?)
34
- return set_item if @attribute_instruction.associations_array.empty?
35
- return @attribute_model if defined? @attribute_model
36
-
37
- @attribute_model = @attribute_instruction
38
- .associations_array
39
- .reduce(set_item) do |obj, assoc|
40
- obj.public_send(assoc)
41
- end
42
- end
43
-
44
- def attribute_class
45
- attribute_instance&.class
46
- end
47
34
  end
48
35
  end