ransack 2.4.0 → 2.4.2

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/SECURITY.md +12 -0
  4. data/.github/workflows/cronjob.yml +105 -0
  5. data/.github/workflows/rubocop.yml +20 -0
  6. data/.github/workflows/test.yml +154 -0
  7. data/.rubocop.yml +44 -0
  8. data/CHANGELOG.md +8 -3
  9. data/CONTRIBUTING.md +2 -5
  10. data/Gemfile +4 -2
  11. data/README.md +95 -25
  12. data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
  13. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +71 -0
  14. data/docs/img/create_release.png +0 -0
  15. data/docs/release_process.md +20 -0
  16. data/lib/polyamorous/activerecord_6.2_ruby_2/join_association.rb +1 -0
  17. data/lib/polyamorous/activerecord_6.2_ruby_2/join_dependency.rb +1 -0
  18. data/lib/polyamorous/activerecord_6.2_ruby_2/reflection.rb +1 -0
  19. data/lib/polyamorous/polyamorous.rb +1 -1
  20. data/lib/ransack.rb +2 -2
  21. data/lib/ransack/adapters/active_record/base.rb +4 -0
  22. data/lib/ransack/adapters/active_record/context.rb +10 -3
  23. data/lib/ransack/adapters/active_record/ransack/constants.rb +1 -1
  24. data/lib/ransack/configuration.rb +17 -1
  25. data/lib/ransack/constants.rb +2 -3
  26. data/lib/ransack/helpers.rb +1 -1
  27. data/lib/ransack/helpers/form_builder.rb +3 -3
  28. data/lib/ransack/nodes/attribute.rb +1 -1
  29. data/lib/ransack/nodes/condition.rb +0 -2
  30. data/lib/ransack/nodes/sort.rb +1 -1
  31. data/lib/ransack/nodes/value.rb +1 -1
  32. data/lib/ransack/search.rb +2 -1
  33. data/lib/ransack/version.rb +1 -1
  34. data/ransack.gemspec +4 -3
  35. data/spec/blueprints/articles.rb +1 -1
  36. data/spec/blueprints/comments.rb +1 -1
  37. data/spec/blueprints/notes.rb +1 -1
  38. data/spec/blueprints/tags.rb +1 -1
  39. data/spec/console.rb +5 -5
  40. data/spec/helpers/ransack_helper.rb +1 -1
  41. data/spec/ransack/adapters/active_record/base_spec.rb +6 -3
  42. data/spec/ransack/adapters/active_record/context_spec.rb +1 -2
  43. data/spec/ransack/configuration_spec.rb +10 -0
  44. data/spec/ransack/helpers/form_helper_spec.rb +16 -16
  45. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  46. data/spec/ransack/predicate_spec.rb +1 -1
  47. data/spec/ransack/search_spec.rb +49 -2
  48. data/spec/spec_helper.rb +7 -6
  49. data/spec/support/schema.rb +2 -2
  50. metadata +16 -4
  51. data/.travis.yml +0 -47
@@ -0,0 +1,78 @@
1
+ # test-ransack-scope-and-column-same-name.rb
2
+
3
+ # This is a stand-alone test case.
4
+
5
+ # Run it in your console with: `ruby test-ransack-scope-and-column-same-name.rb`
6
+
7
+ # If you change the gem dependencies, run it with:
8
+ # `rm gemfile* && ruby test-ransack-scope-and-column-same-name.rb`
9
+
10
+ unless File.exist?('Gemfile')
11
+ File.write('Gemfile', <<-GEMFILE)
12
+ source 'https://rubygems.org'
13
+
14
+ # Rails master
15
+ gem 'rails', github: 'rails/rails', branch: '6-1-stable'
16
+
17
+ # Rails last release
18
+ # gem 'rails'
19
+
20
+ gem 'sqlite3'
21
+ gem 'ransack', github: 'activerecord-hackery/ransack'
22
+ GEMFILE
23
+
24
+ system 'bundle install'
25
+ end
26
+
27
+ require 'bundler'
28
+ Bundler.setup(:default)
29
+
30
+ require 'active_record'
31
+ require 'minitest/autorun'
32
+ require 'logger'
33
+ require 'ransack'
34
+
35
+ # This connection will do for database-independent bug reports.
36
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
37
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
38
+
39
+ # Display versions.
40
+ message = "Running test case with Ruby #{RUBY_VERSION}, Active Record #{
41
+ ::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION} and #{
42
+ ::ActiveRecord::Base.connection.adapter_name}"
43
+ line = '=' * message.length
44
+ puts line, message, line
45
+
46
+ ActiveRecord::Schema.define do
47
+ create_table :users, force: true do |t|
48
+ t.boolean :active, null: false, default: true
49
+ end
50
+ end
51
+
52
+ class User < ActiveRecord::Base
53
+ scope :activated, -> (boolean = true) { where(active: boolean) }
54
+
55
+ private
56
+
57
+ def self.ransackable_scopes(auth_object = nil)
58
+ %i(activated)
59
+ end
60
+ end
61
+
62
+ class BugTest < Minitest::Test
63
+ def test_activated_scope_equals_true
64
+ sql = User.ransack({ activated: true }).result.to_sql
65
+ puts sql
66
+ assert_equal(
67
+ "SELECT \"users\".* FROM \"users\" WHERE \"users\".\"active\" = 1", sql
68
+ )
69
+ end
70
+
71
+ def test_activated_scope_equals_false
72
+ sql = User.ransack({ activated: false }).result.to_sql
73
+ puts sql
74
+ assert_equal(
75
+ "SELECT \"users\".* FROM \"users\"", sql
76
+ )
77
+ end
78
+ end
@@ -0,0 +1,71 @@
1
+ # test-ransacker-arel-present-predicate.rb
2
+
3
+ # Run it in your console with: `ruby test-ransacker-arel-present-predicate.rb`
4
+
5
+ # If you change the gem dependencies, run it with:
6
+ # `rm gemfile* && ruby test-ransacker-arel-present-predicate.rb`
7
+
8
+ unless File.exist?('Gemfile')
9
+ File.write('Gemfile', <<-GEMFILE)
10
+ source 'https://rubygems.org'
11
+
12
+ # Rails master
13
+ gem 'rails', github: 'rails/rails', branch: '6-1-stable'
14
+
15
+ # Rails last release
16
+ # gem 'rails'
17
+
18
+ gem 'sqlite3'
19
+ gem 'ransack', github: 'activerecord-hackery/ransack'
20
+ GEMFILE
21
+
22
+ system 'bundle install'
23
+ end
24
+
25
+ require 'bundler'
26
+ Bundler.setup(:default)
27
+
28
+ require 'active_record'
29
+ require 'minitest/autorun'
30
+ require 'logger'
31
+ require 'ransack'
32
+
33
+ # This connection will do for database-independent bug reports.
34
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
35
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
36
+
37
+ # Display versions.
38
+ message = "Running test case with Ruby #{RUBY_VERSION}, Active Record #{
39
+ ::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION} and #{
40
+ ::ActiveRecord::Base.connection.adapter_name}"
41
+ line = '=' * message.length
42
+ puts line, message, line
43
+
44
+ ActiveRecord::Schema.define do
45
+ create_table :projects, force: true do |t|
46
+ t.string :name
47
+ t.string :number
48
+ end
49
+ end
50
+
51
+ class Project < ActiveRecord::Base
52
+ ransacker :name do
53
+ Arel.sql('projects.name')
54
+ end
55
+
56
+ ransacker :number do |parent|
57
+ parent.table[:number]
58
+ end
59
+ end
60
+
61
+ class BugTest < Minitest::Test
62
+ def test_ransackers
63
+ sql = Project.ransack({ number_present: 1 }).result.to_sql
64
+ puts sql
65
+ assert_equal "SELECT \"projects\".* FROM \"projects\" WHERE (\"projects\".\"number\" IS NOT NULL AND \"projects\".\"number\" != '')", sql
66
+
67
+ sql = Project.ransack({ name_present: 1 }).result.to_sql
68
+ puts sql
69
+ assert_equal "SELECT \"projects\".* FROM \"projects\" WHERE (projects.name IS NOT NULL AND projects.name != '')", sql
70
+ end
71
+ end
Binary file
@@ -0,0 +1,20 @@
1
+ ## Release Process
2
+
3
+ *For maintainers of Ransack.*
4
+
5
+ To release a new version of Ransack and publish it to RubyGems, take the following steps:
6
+
7
+ - Create a new release, marked `Prerelease`.
8
+ <<<<<<< Updated upstream
9
+ - Update the versions file to the new release, commit and push to `master`.
10
+ =======
11
+ - Update the [version.rb](../lib/ransack/version.rb) file to the new release, commit and push to `master`.
12
+ >>>>>>> Stashed changes
13
+ - From the terminal, run the following commands
14
+
15
+ ```bash
16
+ rake build
17
+ rake release
18
+ ```
19
+
20
+ ![Create a Release](img/create_release.png)
@@ -0,0 +1 @@
1
+ require 'polyamorous/activerecord_6.1_ruby_2/join_association'
@@ -0,0 +1 @@
1
+ require 'polyamorous/activerecord_6.1_ruby_2/join_dependency'
@@ -0,0 +1 @@
1
+ require 'polyamorous/activerecord_6.1_ruby_2/reflection'
@@ -11,7 +11,7 @@ if defined?(::ActiveRecord)
11
11
  require 'polyamorous/join'
12
12
  require 'polyamorous/swapping_reflection_class'
13
13
 
14
- ar_version = ::ActiveRecord::VERSION::STRING[0,3]
14
+ ar_version = ::ActiveRecord::VERSION::STRING[0, 3]
15
15
  %w(join_association join_dependency reflection).each do |file|
16
16
  require "polyamorous/activerecord_#{ar_version}_ruby_2/#{file}"
17
17
  end
data/lib/ransack.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  require 'active_support/core_ext'
2
2
  require 'ransack/configuration'
3
3
  require 'ransack/adapters'
4
- require 'polyamorous/polyamorous.rb'
4
+ require 'polyamorous/polyamorous'
5
5
 
6
6
  Ransack::Adapters.object_mapper.require_constants
7
7
 
8
8
  module Ransack
9
9
  extend Configuration
10
- class UntraversableAssociationError < StandardError; end;
10
+ class UntraversableAssociationError < StandardError; end
11
11
  end
12
12
 
13
13
  Ransack.configure do |config|
@@ -18,6 +18,10 @@ module Ransack
18
18
  Search.new(self, params, options)
19
19
  end
20
20
 
21
+ def ransack!(params = {}, options = {})
22
+ ransack(params, options.merge(ignore_unknown_conditions: false))
23
+ end
24
+
21
25
  def ransacker(name, opts = {}, &block)
22
26
  self._ransackers = _ransackers.merge name.to_s => Ransacker
23
27
  .new(self, name, opts, &block)
@@ -42,6 +42,13 @@ module Ransack
42
42
  if scope_or_sort.is_a?(Symbol)
43
43
  relation = relation.send(scope_or_sort)
44
44
  else
45
+ case Ransack.options[:postgres_fields_sort_option]
46
+ when :nulls_first
47
+ scope_or_sort = scope_or_sort.direction == :asc ? "#{scope_or_sort.to_sql} NULLS FIRST" : "#{scope_or_sort.to_sql} NULLS LAST"
48
+ when :nulls_last
49
+ scope_or_sort = scope_or_sort.direction == :asc ? "#{scope_or_sort.to_sql} NULLS LAST" : "#{scope_or_sort.to_sql} NULLS FIRST"
50
+ end
51
+
45
52
  relation = relation.order(scope_or_sort)
46
53
  end
47
54
  end
@@ -99,7 +106,7 @@ module Ransack
99
106
  def join_sources
100
107
  base, joins = begin
101
108
  alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, @object.table.name, [])
102
- constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1_ALPHA)
109
+ constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1)
103
110
  @join_dependency.join_constraints(@object.joins_values, alias_tracker, @object.references_values)
104
111
  elsif ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
105
112
  @join_dependency.join_constraints(@object.joins_values, alias_tracker)
@@ -327,7 +334,7 @@ module Ransack
327
334
  @join_dependency.instance_variable_get(:@join_root).children.push found_association
328
335
 
329
336
  # Builds the arel nodes properly for this association
330
- if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1_ALPHA)
337
+ if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1)
331
338
  @tables_pot[found_association] = @join_dependency.construct_tables_for_association!(jd.instance_variable_get(:@join_root), found_association)
332
339
  else
333
340
  @join_dependency.send(:construct_tables!, jd.instance_variable_get(:@join_root))
@@ -341,7 +348,7 @@ module Ransack
341
348
  def extract_joins(association)
342
349
  parent = @join_dependency.instance_variable_get(:@join_root)
343
350
  reflection = association.reflection
344
- join_constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1_ALPHA)
351
+ join_constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1)
345
352
  association.join_constraints_with_tables(
346
353
  parent.table,
347
354
  parent.base_klass,
@@ -97,7 +97,7 @@ module Ransack
97
97
  arel_predicate: proc { |v| v ? EQ : NOT_EQ },
98
98
  compounds: false,
99
99
  type: :boolean,
100
- validator: proc { |v| BOOLEAN_VALUES.include?(v)},
100
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
101
101
  formatter: proc { |v| nil }
102
102
  }
103
103
  ],
@@ -33,7 +33,8 @@ module Ransack
33
33
  :up_arrow => '&#9660;'.freeze,
34
34
  :down_arrow => '&#9650;'.freeze,
35
35
  :default_arrow => nil,
36
- :sanitize_scope_args => true
36
+ :sanitize_scope_args => true,
37
+ :postgres_fields_sort_option => nil
37
38
  }
38
39
 
39
40
  def configure
@@ -141,6 +142,21 @@ module Ransack
141
142
  self.options[:sanitize_scope_args] = boolean
142
143
  end
143
144
 
145
+ # The `NULLS FIRST` and `NULLS LAST` options can be used to determine
146
+ # whether nulls appear before or after non-null values in the sort ordering.
147
+ #
148
+ # User may want to configure it like this:
149
+ #
150
+ # Ransack.configure do |c|
151
+ # c.postgres_fields_sort_option = :nulls_first # or :nulls_last
152
+ # end
153
+ #
154
+ # See this feature: https://www.postgresql.org/docs/13/queries-order.html
155
+ #
156
+ def postgres_fields_sort_option=(setting)
157
+ self.options[:postgres_fields_sort_option] = setting
158
+ end
159
+
144
160
  # By default, Ransack displays sort order indicator arrows in sort links.
145
161
  # The default may be globally overridden in an initializer file like
146
162
  # `config/initializers/ransack.rb` as follows:
@@ -36,7 +36,7 @@ module Ransack
36
36
  'lt'.freeze, 'lteq'.freeze,
37
37
  'gt'.freeze, 'gteq'.freeze,
38
38
  'in'.freeze, 'not_in'.freeze
39
- ].freeze
39
+ ].freeze
40
40
  A_S_I = ['a'.freeze, 's'.freeze, 'i'.freeze].freeze
41
41
 
42
42
  EQ = 'eq'.freeze
@@ -46,10 +46,9 @@ module Ransack
46
46
  CONT = 'cont'.freeze
47
47
 
48
48
  RAILS_6_0 = '6.0.0'.freeze
49
- RAILS_6_1_ALPHA = '6.1.0.alpha'.freeze
49
+ RAILS_6_1 = '6.1.0'.freeze
50
50
 
51
51
  RANSACK_SLASH_SEARCHES = 'ransack/searches'.freeze
52
52
  RANSACK_SLASH_SEARCHES_SLASH_SEARCH = 'ransack/searches/search'.freeze
53
53
  end
54
54
  end
55
-
@@ -1,2 +1,2 @@
1
1
  require 'ransack/helpers/form_builder'
2
- require 'ransack/helpers/form_helper'
2
+ require 'ransack/helpers/form_helper'
@@ -45,9 +45,9 @@ module Ransack
45
45
  end
46
46
 
47
47
  def attribute_select(options = nil, html_options = nil, action = nil)
48
- options = options || {}
49
- html_options = html_options || {}
50
- action = action || Constants::SEARCH
48
+ options ||= {}
49
+ html_options ||= {}
50
+ action ||= Constants::SEARCH
51
51
  default = options.delete(:default)
52
52
  raise ArgumentError, formbuilder_error_message(
53
53
  "#{action}_select") unless object.respond_to?(:context)
@@ -30,7 +30,7 @@ module Ransack
30
30
 
31
31
  def type
32
32
  if ransacker
33
- return ransacker.type
33
+ ransacker.type
34
34
  else
35
35
  context.type_for(self)
36
36
  end
@@ -127,7 +127,6 @@ module Ransack
127
127
  alias :m= :combinator=
128
128
  alias :m :combinator
129
129
 
130
-
131
130
  # == build_attribute
132
131
  #
133
132
  # This method was originally called from Nodes::Grouping#new_condition
@@ -263,7 +262,6 @@ module Ransack
263
262
  attr.attr
264
263
  end
265
264
 
266
-
267
265
  def default_type
268
266
  predicate.type || (attributes.first && attributes.first.type)
269
267
  end
@@ -9,7 +9,7 @@ module Ransack
9
9
  class << self
10
10
  def extract(context, str)
11
11
  return unless str
12
- attr, direction = str.split(/\s+/,2)
12
+ attr, direction = str.split(/\s+/, 2)
13
13
  self.new(context).build(name: attr, dir: direction)
14
14
  end
15
15
  end
@@ -50,7 +50,7 @@ module Ransack
50
50
  y, m, d = *[val].flatten
51
51
  m ||= 1
52
52
  d ||= 1
53
- Date.new(y,m,d) rescue nil
53
+ Date.new(y, m, d) rescue nil
54
54
  end
55
55
  end
56
56
 
@@ -30,6 +30,7 @@ module Ransack
30
30
  )
31
31
  @scope_args = {}
32
32
  @sorts ||= []
33
+ @ignore_unknown_conditions = options[:ignore_unknown_conditions] == false ? false : true
33
34
  build(params.with_indifferent_access)
34
35
  end
35
36
 
@@ -45,7 +46,7 @@ module Ransack
45
46
  base.send("#{key}=", value)
46
47
  elsif @context.ransackable_scope?(key, @context.object)
47
48
  add_scope(key, value)
48
- elsif !Ransack.options[:ignore_unknown_conditions]
49
+ elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions
49
50
  raise ArgumentError, "Invalid search term #{key}"
50
51
  end
51
52
  end
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = '2.4.0'
2
+ VERSION = '2.4.2'
3
3
  end
data/ransack.gemspec CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  $:.push File.expand_path("../lib", __FILE__)
3
4
  require "ransack/version"
4
5
 
@@ -6,12 +7,12 @@ Gem::Specification.new do |s|
6
7
  s.name = "ransack"
7
8
  s.version = Ransack::VERSION
8
9
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack","Sean Carroll"]
10
- s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com","sfcarroll@gmail.com"]
10
+ s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Sean Carroll"]
11
+ s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com", "sfcarroll@gmail.com"]
11
12
  s.homepage = "https://github.com/activerecord-hackery/ransack"
12
13
  s.summary = %q{Object-based searching for Active Record and Mongoid (currently).}
13
14
  s.description = %q{Ransack is the successor to the MetaSearch gem. It improves and expands upon MetaSearch's functionality, but does not have a 100%-compatible API.}
14
- s.required_ruby_version = '>= 2.3'
15
+ s.required_ruby_version = '>= 2.6'
15
16
  s.license = 'MIT'
16
17
 
17
18
  s.add_dependency 'activerecord', '>= 5.2.4'
@@ -2,4 +2,4 @@ Article.blueprint do
2
2
  person
3
3
  title
4
4
  body
5
- end
5
+ end