ransack 2.4.2 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql.yml +72 -0
  3. data/.github/workflows/cronjob.yml +6 -9
  4. data/.github/workflows/deploy.yml +35 -0
  5. data/.github/workflows/rubocop.yml +1 -1
  6. data/.github/workflows/test-deploy.yml +29 -0
  7. data/.github/workflows/test.yml +22 -48
  8. data/.nojekyll +0 -0
  9. data/.rubocop.yml +3 -0
  10. data/CHANGELOG.md +208 -11
  11. data/CONTRIBUTING.md +41 -18
  12. data/Gemfile +10 -10
  13. data/README.md +44 -977
  14. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +4 -0
  15. data/docs/.gitignore +19 -0
  16. data/docs/.nojekyll +0 -0
  17. data/docs/babel.config.js +3 -0
  18. data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
  19. data/docs/docs/getting-started/_category_.json +4 -0
  20. data/docs/docs/getting-started/advanced-mode.md +46 -0
  21. data/docs/docs/getting-started/configuration.md +47 -0
  22. data/docs/docs/getting-started/search-matches.md +67 -0
  23. data/docs/docs/getting-started/simple-mode.md +288 -0
  24. data/docs/docs/getting-started/sorting.md +79 -0
  25. data/docs/docs/getting-started/using-predicates.md +282 -0
  26. data/docs/docs/going-further/_category_.json +4 -0
  27. data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
  28. data/docs/docs/going-further/associations.md +70 -0
  29. data/docs/docs/going-further/custom-predicates.md +52 -0
  30. data/docs/docs/going-further/documentation.md +43 -0
  31. data/docs/docs/going-further/exporting-to-csv.md +49 -0
  32. data/docs/docs/going-further/external-guides.md +57 -0
  33. data/docs/docs/going-further/form-customisation.md +63 -0
  34. data/docs/docs/going-further/i18n.md +53 -0
  35. data/docs/docs/going-further/merging-searches.md +41 -0
  36. data/docs/docs/going-further/other-notes.md +428 -0
  37. data/docs/docs/going-further/polymorphic-search.md +40 -0
  38. data/docs/docs/going-further/ransackers.md +331 -0
  39. data/docs/docs/going-further/release_process.md +36 -0
  40. data/docs/docs/going-further/saving-queries.md +82 -0
  41. data/docs/docs/going-further/searching-postgres.md +57 -0
  42. data/docs/docs/going-further/wiki-contributors.md +82 -0
  43. data/docs/docs/intro.md +99 -0
  44. data/docs/docusaurus.config.js +120 -0
  45. data/docs/package.json +42 -0
  46. data/docs/sidebars.js +31 -0
  47. data/docs/src/components/HomepageFeatures/index.js +64 -0
  48. data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  49. data/docs/src/css/custom.css +39 -0
  50. data/docs/src/pages/index.module.css +23 -0
  51. data/docs/src/pages/markdown-page.md +7 -0
  52. data/docs/static/.nojekyll +0 -0
  53. data/docs/static/img/docusaurus.png +0 -0
  54. data/docs/static/img/favicon.ico +0 -0
  55. data/docs/static/img/logo.svg +1 -0
  56. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  57. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  58. data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  59. data/docs/static/img/undraw_docusaurus_react.svg +170 -0
  60. data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  61. data/docs/yarn.lock +8790 -0
  62. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -4
  63. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -1
  64. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -1
  65. data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
  66. data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
  67. data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
  68. data/lib/ransack/adapters/active_record/base.rb +79 -10
  69. data/lib/ransack/adapters/active_record/context.rb +24 -51
  70. data/lib/ransack/configuration.rb +39 -12
  71. data/lib/ransack/constants.rb +125 -3
  72. data/lib/ransack/context.rb +34 -5
  73. data/lib/ransack/helpers/form_builder.rb +3 -3
  74. data/lib/ransack/helpers/form_helper.rb +14 -5
  75. data/lib/ransack/locale/sv.yml +70 -0
  76. data/lib/ransack/nodes/attribute.rb +2 -2
  77. data/lib/ransack/nodes/condition.rb +80 -7
  78. data/lib/ransack/nodes/grouping.rb +3 -3
  79. data/lib/ransack/nodes/node.rb +1 -1
  80. data/lib/ransack/nodes/sort.rb +2 -2
  81. data/lib/ransack/nodes/value.rb +2 -2
  82. data/lib/ransack/predicate.rb +1 -1
  83. data/lib/ransack/ransacker.rb +1 -1
  84. data/lib/ransack/search.rb +13 -7
  85. data/lib/ransack/translate.rb +3 -3
  86. data/lib/ransack/version.rb +1 -1
  87. data/lib/ransack/visitor.rb +38 -2
  88. data/lib/ransack.rb +3 -6
  89. data/ransack.gemspec +5 -5
  90. data/spec/helpers/polyamorous_helper.rb +2 -8
  91. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  92. data/spec/polyamorous/join_association_spec.rb +1 -6
  93. data/spec/polyamorous/join_dependency_spec.rb +0 -16
  94. data/spec/ransack/adapters/active_record/base_spec.rb +101 -11
  95. data/spec/ransack/configuration_spec.rb +23 -9
  96. data/spec/ransack/helpers/form_builder_spec.rb +8 -8
  97. data/spec/ransack/helpers/form_helper_spec.rb +93 -4
  98. data/spec/ransack/nodes/condition_spec.rb +37 -0
  99. data/spec/ransack/nodes/value_spec.rb +115 -0
  100. data/spec/ransack/predicate_spec.rb +36 -1
  101. data/spec/ransack/search_spec.rb +140 -27
  102. data/spec/ransack/translate_spec.rb +1 -1
  103. data/spec/support/schema.rb +75 -9
  104. metadata +83 -37
  105. data/docs/release_process.md +0 -20
  106. data/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +0 -24
  107. data/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -79
  108. data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -11
  109. data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -1
  110. data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +0 -80
  111. data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -1
  112. data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -128
  113. data/lib/ransack/adapters/active_record/ransack/context.rb +0 -56
  114. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +0 -68
  115. data/lib/ransack/adapters/active_record/ransack/translate.rb +0 -8
  116. data/lib/ransack/adapters/active_record/ransack/visitor.rb +0 -47
  117. data/lib/ransack/adapters.rb +0 -64
  118. data/lib/ransack/nodes.rb +0 -8
  119. /data/docs/{img → docs/going-further/img}/create_release.png +0 -0
  120. /data/{logo → docs/static/logo}/ransack-h.png +0 -0
  121. /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
  122. /data/{logo → docs/static/logo}/ransack-v.png +0 -0
  123. /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
  124. /data/{logo → docs/static/logo}/ransack.png +0 -0
  125. /data/{logo → docs/static/logo}/ransack.svg +0 -0
  126. /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_association.rb +0 -0
  127. /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_dependency.rb +0 -0
  128. /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/reflection.rb +0 -0
  129. /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
@@ -0,0 +1,331 @@
1
+ ---
2
+ sidebar_position: 6
3
+ title: Ransackers
4
+ ---
5
+
6
+ ## Add custom search functions
7
+
8
+ The main premise behind Ransack is to provide access to **Arel predicate methods**. Ransack provides special methods, called _ransackers_, for creating additional search functions via Arel.
9
+
10
+ A `ransacker` method can **return any Arel node that allows the usual predicate methods**. Custom `ransacker`s are an expert feature, and require a thorough understanding of Arel.
11
+
12
+ ## Arel
13
+
14
+ Here are some resources for more information about Arel:
15
+
16
+ * [Using Arel to Compose SQL Queries](https://robots.thoughtbot.com/using-arel-to-compose-sql-queries)
17
+ * [The definitive guide to Arel, the SQL manager for Ruby](http://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html)
18
+ * [Creating Advanced Active Record DB Queries with Arel](https://www.cloudbees.com/blog/creating-advanced-active-record-db-queries-arel)
19
+
20
+ Ransacker methods enable search customization and are placed in the model. Arguments may be passed to a ransacker method via `ransacker_args` (see Example #6 below).
21
+
22
+ Ransackers, like scopes, are not a cure-all. Many use cases can be better solved with a standard Ransack search on a dedicated database search field, which is faster, index-able, and scales better than converting/ransacking data on the fly.
23
+
24
+ ## Example Ransackers
25
+
26
+ ### Search on field
27
+
28
+ _Search on the `name` field reversed:_
29
+ ```ruby
30
+ # in the model:
31
+ ransacker :reversed_name, formatter: proc { |v| v.reverse } do |parent|
32
+ parent.table[:name]
33
+ end
34
+ ```
35
+ ### Search using Datetime
36
+
37
+ _Convert a user `string` input and a database `datetime` field to the same `date` format to find all records with a `datetime` field (`created_at` in this example) equal to that date :_
38
+
39
+ ```ruby
40
+ # in the model:
41
+ ransacker :created_at do
42
+ Arel.sql('date(created_at)')
43
+ end
44
+ ```
45
+ ```erb
46
+ in the view:
47
+ <%= f.search_field(
48
+ :created_at_date_equals, placeholder: t(:date_format)
49
+ ) %>
50
+ ...
51
+ <%= sort_link(@search, :created_at, default_order: :desc) %>
52
+ ```
53
+
54
+ ```ruby
55
+ # config/initializers/ransack.rb
56
+ Ransack.configure do |config|
57
+ config.add_predicate 'date_equals',
58
+ arel_predicate: 'eq',
59
+ formatter: proc { |v| v.to_date },
60
+ validator: proc { |v| v.present? },
61
+ type: :string
62
+ end
63
+ ```
64
+
65
+ #### 2.1
66
+ It seems to be enough to change the model only, but don't forget to define the type that will returned as well.
67
+
68
+ ```ruby
69
+ # in the model:
70
+ ransacker :created_at, type: :date do
71
+ Arel.sql('date(created_at)')
72
+ end
73
+ ```
74
+
75
+ #### 2.2. Postgresql with time zones
76
+
77
+ If you're using different time zones for Rails and Postgresql you should expect to have some problems using the above solution.
78
+ Example:
79
+ - Rails at GMT -03:00
80
+ - Postgresql at GMT -00:00 (UTC)
81
+
82
+ A timestamp like `2019-07-18 01:21:29.826484` will be truncated to `2019-07-18`.
83
+ But for your Rails application `2019-07-18 01:21:29.826484` is `2019-07-17 22:21:29.826484` at your time zone (GMT -03:00). So it should be truncated to `2019-07-17` instead.
84
+
85
+
86
+ So, you should convert the timestamp to your current Rails time zone before extracting the date.
87
+
88
+ ```ruby
89
+ # in the model:
90
+ ransacker :created_at, type: :date do
91
+ Arel.sql("date(created_at at time zone 'UTC' at time zone '#{Time.zone.name}')")
92
+ end
93
+ ```
94
+
95
+ Note that `Time.zone.name` should return a time zone string suitable for Postgresql.
96
+
97
+ ### Postgres columns
98
+
99
+ _Search on a fixed key in a jsonb / hstore column:_
100
+
101
+ In this example, we are searching a table with a column called `properties` for records containing a key called `link_type`.
102
+
103
+ For anything up to and including Rails 4.1, add this to your model
104
+ ```ruby
105
+ ransacker :link_type do |parent|
106
+ Arel::Nodes::InfixOperation.new('->>', parent.table[:properties], 'link_type')
107
+ end
108
+ ```
109
+ When using Rails 4.2+ (Arel 6.0+), wrap the value in a `build_quoted` call
110
+ ```ruby
111
+ ransacker :link_type do |parent|
112
+ Arel::Nodes::InfixOperation.new('->>', parent.table[:properties], Arel::Nodes.build_quoted('link_type'))
113
+ end
114
+ ```
115
+ In the view, with a search on `link_type_eq` using a collection select (for example with options like 'twitter', 'facebook', etc.), if the user selects 'twitter', Ransack will run a query like:
116
+ ```
117
+ SELECT * FROM "foos" WHERE "foos"."properties" ->> 'link_type' = 'twitter';
118
+ ```
119
+
120
+ To use the JSONB contains operator @> see here: [[PostgreSQL JSONB searches]].
121
+
122
+ ### Type conversions
123
+
124
+ _Convert an `integer` database field to a `string` in order to be able to use a `cont` predicate (instead of the usual `eq` which works out of the box with integers) to find all records where an integer field (`id` in this example) **contains** an input string:_
125
+
126
+ Simple version, using PostgreSQL:
127
+ ```ruby
128
+ # in the model:
129
+ ransacker :id do
130
+ Arel.sql("to_char(id, '9999999')")
131
+ end
132
+ ```
133
+ and the same, using MySQL:
134
+ ```ruby
135
+ ransacker :id do
136
+ Arel.sql("CONVERT(#{table_name}.id, CHAR(8))")
137
+ end
138
+ ```
139
+ A more complete version (using PostgreSQL) that adds the table name to avoid ambiguity and strips spaces from the input:
140
+ ```ruby
141
+ ransacker :id do
142
+ Arel.sql(
143
+ "regexp_replace(
144
+ to_char(\"#{table_name}\".\"id\", '9999999'), ' ', '', 'g')"
145
+ )
146
+ end
147
+ ```
148
+ In the view, for all 3 versions:
149
+ ```erb
150
+ <%= f.search_field :id_cont, placeholder: 'Id' %>
151
+ ...
152
+ <%= sort_link(@search, :id) %>
153
+ ```
154
+
155
+ ### Concatenated fields
156
+
157
+ _Search on a concatenated full name from `first_name` and `last_name` (several examples):_
158
+ ```ruby
159
+ # in the model:
160
+ ransacker :full_name do |parent|
161
+ Arel::Nodes::InfixOperation.new('||',
162
+ parent.table[:first_name], parent.table[:last_name])
163
+ end
164
+
165
+ # or, to insert a space between `first_name` and `last_name`:
166
+ ransacker :full_name do |parent|
167
+ Arel::Nodes::InfixOperation.new('||',
168
+ Arel::Nodes::InfixOperation.new('||',
169
+ parent.table[:first_name], ' '
170
+ ),
171
+ parent.table[:last_name]
172
+ )
173
+ end
174
+ # Caveat: with Arel >= 6 the separator ' ' string in the
175
+ # preceding example needs to be quoted as follows:
176
+ ransacker :full_name do |parent|
177
+ Arel::Nodes::InfixOperation.new('||',
178
+ Arel::Nodes::InfixOperation.new('||',
179
+ parent.table[:first_name], Arel::Nodes.build_quoted(' ')
180
+ ),
181
+ parent.table[:last_name]
182
+ )
183
+ end
184
+
185
+ # works also in mariadb
186
+ ransacker :full_name do |parent|
187
+ Arel::Nodes::NamedFunction.new('concat_ws',
188
+ [Arel::Nodes::SqlLiteral.new("' '"), parent.table[:first_name], parent.table[:last_name]])
189
+ end
190
+
191
+ # case insensitive lookup
192
+ ransacker :full_name, formatter: proc { |v| v.mb_chars.downcase.to_s } do |parent|
193
+ Arel::Nodes::NamedFunction.new('LOWER',
194
+ [Arel::Nodes::NamedFunction.new('concat_ws',
195
+ [Arel::Nodes::SqlLiteral.new("' '"), parent.table[:first_name], parent.table[:last_name]])])
196
+ end
197
+ ```
198
+
199
+ ### Passing arguments
200
+
201
+ _Passing arguments to a ransacker:_
202
+ Arguments may be passed to a ransacker method via `ransacker_args`:
203
+ ```ruby
204
+
205
+ class Person
206
+ ransacker :author_max_title_of_article_where_body_length_between,
207
+ args: [:parent, :ransacker_args] do |parent, args|
208
+ min, max = args
209
+ query = <<-SQL
210
+ (SELECT MAX(articles.title)
211
+ FROM articles
212
+ WHERE articles.person_id = people.id
213
+ AND CHAR_LENGTH(articles.body) BETWEEN #{min.to_i} AND #{max.to_i}
214
+ GROUP BY articles.person_id
215
+ )
216
+ SQL
217
+ Arel.sql(query)
218
+ end
219
+ end
220
+
221
+ # Usage
222
+ Person.ransack(
223
+ conditions: [{
224
+ attributes: {
225
+ '0' => {
226
+ name: 'author_max_title_of_article_where_body_length_between',
227
+ ransacker_args: [10, 100]
228
+ }
229
+ },
230
+ predicate_name: 'cont',
231
+ values: ['Ransackers can take arguments']
232
+ }]
233
+ )
234
+
235
+ => SELECT "people".* FROM "people" WHERE (
236
+ (SELECT MAX(articles.title)
237
+ FROM articles
238
+ WHERE articles.person_id = people.id
239
+ AND CHAR_LENGTH(articles.body) BETWEEN 10 AND 100
240
+ GROUP BY articles.person_id
241
+ )
242
+ LIKE '%Ransackers can take arguments%')
243
+ ORDER BY "people"."id" DESC
244
+ ```
245
+
246
+ ### Dropdowns
247
+
248
+ _Adding the attribute values associated with a column name to a searchable attribute in a dropdown options (instead of a traditional column name coming from a table). This is useful if using an associated table which is acting as a join table between a parent table and domain table. This will cache the data as the selections:_
249
+
250
+ ```ruby
251
+ # in the model:
252
+ Model.pluck(:name).each do |ground|
253
+ ransacker ground.to_sym do |parent|
254
+ Arel::Nodes::InfixOperation.new('AND',
255
+ Arel::Nodes::InfixOperation.new('=', parent.table[:gor_name], ground),
256
+ parent.table[:status]
257
+ )
258
+ end
259
+ end
260
+
261
+ # This will not include the column names in the dropdown
262
+ def self.ransackable_attributes(auth_object = nil)
263
+ %w() + _ransackers.keys
264
+ end
265
+ ```
266
+
267
+ ### Testing for existence
268
+
269
+ _Testing for the existence of a row in another table via a join:_
270
+
271
+ ```ruby
272
+ # in the model:
273
+ ransacker :price_exists do |parent|
274
+ # SQL syntax for PostgreSQL -- others may differ
275
+ # This returns boolean true or false
276
+ Arel.sql("(select exists (select 1 from prices where prices.book_id = books.id))")
277
+ end
278
+ ```
279
+
280
+ In the view
281
+ ```haml
282
+ %td= f.select :price_exists_true, [["Any", 2], ["No", 0], ["Yes", 1]]
283
+ ```
284
+
285
+ ### Associations
286
+
287
+ _Performing a query on an association with a differing class name:_
288
+
289
+ Say we have a model "SalesAccount", which represents a relationship between two users,
290
+ one being designated as a "sales_rep". We want to query SalesAccounts by
291
+ the name of the sales_rep:
292
+
293
+ ```ruby
294
+ # in the model:
295
+ class SalesAccount < ActiveRecord::Base
296
+ belongs_to :user
297
+ belongs_to :sales_rep, class_name: :User
298
+
299
+ # in the controller:
300
+ # The line below would lead to errors thrown later if not for the
301
+ # "joins(:sales_reps)".
302
+ @q = SalesAccount.includes(:user).joins(:sales_rep).ransack(params[:q])
303
+ @sales_accounts = @q.result(distinct: true)
304
+ ```
305
+
306
+ In the view:
307
+ ```erb
308
+ <%= f.search_field :sales_rep_name_start %>
309
+ ```
310
+
311
+ ### Search on translations
312
+
313
+ _Search for a translated value in a jsonb column:_
314
+
315
+ _Note: There is also a gem, [Mobility Ransack](https://github.com/shioyama/mobility-ransack), which allows you to search on translated attributes independent of their storage backend._
316
+
317
+ This will work with any `jsonb` data type. In this case I have a column translated with [Mobility](https://github.com/shioyama/mobility) called `name` with the value `{'en': "Hello", 'es': "Hola"}`.
318
+
319
+ ```ruby
320
+ ransacker :name do |parent|
321
+ Arel::Nodes::InfixOperation.new('->>', parent.table[:name], Arel::Nodes.build_quoted(Mobility.locale))
322
+ end
323
+ ```
324
+
325
+ _If using Rails 4.1 or under, remove the `build_quoted` call._
326
+
327
+ You can then search for `name_eq` or `name_cont` and it will do the proper SQL.
328
+
329
+ ***
330
+
331
+ Please feel free to contribute further code examples!
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: Versions and Releases
3
+ sidebar_position: 11
4
+ ---
5
+
6
+
7
+ ## Semantic Versioning
8
+
9
+ Ransack attempts to follow semantic versioning in the format of `x.y.z`, where:
10
+
11
+ `x` stands for a major version (new features that are not backward-compatible).
12
+
13
+ `y` stands for a minor version (new features that are backward-compatible).
14
+
15
+ `z` stands for a patch (bug fixes).
16
+
17
+ In other words: `Major.Minor.Patch`.
18
+
19
+
20
+ ## Release Process
21
+
22
+ *For the maintainers of Ransack.*
23
+
24
+ To release a new version of Ransack and publish it to RubyGems, take the following steps:
25
+
26
+ - Create a new release, marked `Prerelease`.
27
+ - Update the versions file to the new release, commit and push to `master`.
28
+ - Update the [`version.rb`](https://github.com/activerecord-hackery/ransack/lib/ransack/version.rb) file to the new release, commit and push to `master`.
29
+ - From the terminal, run the following commands:
30
+
31
+ ```bash
32
+ rake build
33
+ rake release
34
+ ```
35
+
36
+ ![Create a Release](img/create_release.png)
@@ -0,0 +1,82 @@
1
+ ---
2
+ sidebar_position: 7
3
+ title: Saving queries
4
+ ---
5
+
6
+ ## Ransack Memory Gem
7
+
8
+ The [Ransack Memory](https://github.com/richardrails/ransack_memory) gem accomplishes this.
9
+
10
+ ## Custom solution
11
+
12
+ If you want a custom solution, you can build it yourself. My ransack AJAX searching doesn’t save your search parameters across transactions. In this post I’ll show you how to easily add this capability in a generic way.
13
+
14
+ In this example I added AJAX search ability to index pages.
15
+
16
+ ```ruby
17
+ def index
18
+ @search = ComponentDefinition.search(search_params)
19
+ # make name the default sort column
20
+ @search.sorts = 'name' if @search.sorts.empty?
21
+ @component_definitions = @search.result().page(params[:page])
22
+ end
23
+ ```
24
+
25
+ I added methods(search_params, clear_search_index) in the ApplicationController to add a level of abstraction from the search gem I was using. Turns out this made things super easy, especially considering I won’t have to update my code generation tools for index pages.
26
+
27
+ ```ruby
28
+ class ApplicationController < ActionController::Base
29
+ def search_params
30
+ params[:q]
31
+ end
32
+ def clear_search_index
33
+ if params[:search_cancel]
34
+ params.delete(:search_cancel)
35
+ if(!search_params.nil?)
36
+ search_params.each do |key, param|
37
+ search_params[key] = nil
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ ```
44
+
45
+ I decided to store the ransack search parameters, params[:q], in the session. To make the session parameter unique I used a key creed from the controllers name and “_search”.
46
+
47
+ ```ruby
48
+ class ApplicationController < ActionController::Base
49
+
50
+ # CHECK THE SESSION FOR SEARCH PARAMETERS IS THEY AREN'T IN THE REQUEST
51
+ def search_params
52
+ if params[:q] == nil
53
+ params[:q] = session[search_key]
54
+ end
55
+ if params[:q]
56
+ session[search_key] = params[:q]
57
+ end
58
+ params[:q]
59
+ end
60
+ # DELETE SEARCH PARAMETERS FROM THE SESSION
61
+ def clear_search_index
62
+ if params[:search_cancel]
63
+ params.delete(:search_cancel)
64
+ if(!search_params.nil?)
65
+ search_params.each do |key, param|
66
+ search_params[key] = nil
67
+ end
68
+ end
69
+ # REMOVE FROM SESSION
70
+ session.delete(search_key)
71
+ end
72
+ end
73
+
74
+ protected
75
+ # GENERATE A GENERIC SESSION KEY BASED ON THE CONTROLLER NAME
76
+ def search_key
77
+ "#{controller_name}_search".to_sym
78
+ end
79
+ end
80
+ ```
81
+
82
+ Based on [Saving queries](https://techbrownbags.wordpress.com/2015/02/18/rails-save-ransack-search-queries/)
@@ -0,0 +1,57 @@
1
+ ---
2
+ sidebar_position: 8
3
+ title: Postgres searches
4
+ ---
5
+
6
+ Searching on Postgres-specific column types.
7
+
8
+ ## Postgres Array searches
9
+
10
+ See [this issue](https://github.com/activerecord-hackery/ransack/issues/321) for details.
11
+
12
+ ## PostgreSQL JSONB searches
13
+
14
+ ### Using a fixed key
15
+
16
+ See here for searching on a fixed key in a JSONB column: https://activerecord-hackery.github.io/ransack/going-further/ransackers/#postgres-columns
17
+
18
+ ### Using the JSONB contains operator
19
+
20
+ To fully use the power of the JSONB column you may want to filter on any key though:
21
+
22
+ Install the [ActiveRecordExtended](https://github.com/GeorgeKaraszi/ActiveRecordExtended) gem to add the `contains` arel predicate to your project. It let's you use the [Postgres contains operator @>](https://www.postgresql.org/docs/12/functions-json.html#FUNCTIONS-JSONB-OP-TABLE).
23
+
24
+ Add a custom predicate in the `config/initializers/ransack.rb` file:
25
+ ```ruby
26
+ Ransack.configure do |config|
27
+ config.add_predicate 'jcont', arel_predicate: 'contains', formatter: proc { |v| JSON.parse(v) }
28
+ end
29
+ ```
30
+
31
+ Now you can ransack the JSONB columns using the _jcont predicate. For example the Person model has a `data` JSONB column, find entries where the column contains the {"group": "experts"} key-value pair:
32
+
33
+ Person.ransack(data_jcont: '{"group": "experts"}').result.to_sql
34
+
35
+ SELECT "persons".* FROM "persons" WHERE "persons"."data" @> '"{\"group\": \"experts\"}"'
36
+
37
+ If you have a GIN index on that column, the database will quickly be able to find that result.
38
+
39
+ ### Treating the column as a string
40
+
41
+ Warning: This method converts the column to a string and matches the given string to the result. This will be slow on large data_sets and does not make good use of the JSONB capabilities of Postgres, such as indexes.
42
+
43
+ ```ruby
44
+ class Contact < ApplicationRecord
45
+ ransacker :within_json do |parent|
46
+ Arel.sql("table.jsonb_data::text")
47
+ end
48
+ end
49
+
50
+ Contact.all.ransack("within_json_cont" => "my")
51
+ ```
52
+
53
+ Will generate
54
+
55
+ `SELECT "contacts".* FROM "contacts" WHERE contacts.json_data ILIKE '%my%'`
56
+
57
+ Note that this search treats the entire JSON as string, including parens, etc. i.e. you can search for e.g.: `Contact.all.ransack("within_json_cont" => '{"key": "value"}')`
@@ -0,0 +1,82 @@
1
+ ---
2
+ title: Wiki Contributors
3
+ sidebar_position: 20
4
+ ---
5
+
6
+ Ransack previously had documentation contained in a GitHub Wiki, and this content has been merged into this documentation website. The following long list of _amazing_ people all made contributions to the Wiki:
7
+
8
+ * Abinoam P. Marques Jr
9
+ * Alex Stophel
10
+ * Andrea Schiavini
11
+ * Andrew Vit
12
+ * Ben Koshy
13
+ * Brainkurv
14
+ * Brandan Lennox
15
+ * Brendon Muir
16
+ * Chris Salzberg
17
+ * Colleen McGuckin
18
+ * David Aldridge
19
+ * Davidson Mohanty
20
+ * Denis Tataurov
21
+ * Drew Moore
22
+ * Eike Send
23
+ * Feodor Cherashev
24
+ * Glauco Custódio
25
+ * Grey Baker
26
+ * Harold.Luo
27
+ * Herman Singh
28
+ * Ian Smith
29
+ * Jake Haber
30
+ * Jan Klimo
31
+ * Jared Beck
32
+ * Jon Atack
33
+ * Juanito Fatas
34
+ * JungaJk
35
+ * Leo Chen
36
+ * Leon Miller-Out
37
+ * Luca F
38
+ * Marc Poris
39
+ * Matt Oakley
40
+ * Michael Kopchick
41
+ * Nathan Colgate
42
+ * Nguyen Phi Viet(Sun*)
43
+ * Nguyễn Đức Long
44
+ * NielsKSchjoedt
45
+ * Patrick Copeland
46
+ * Pedro Chambino
47
+ * Rene Hopf
48
+ * Richa Arora
49
+ * Rob Jones
50
+ * Roman Sokhan
51
+ * Ryan Bates
52
+ * Ryan Bigg
53
+ * Sean
54
+ * Sean Linsley
55
+ * Sergey
56
+ * Sunny Ripert
57
+ * Tanbir Hasan
58
+ * ThuyNguyen97
59
+ * Vanda
60
+ * Yana Agun Siswanto
61
+ * bonyiii
62
+ * charly
63
+ * chifung7
64
+ * colorfulberry
65
+ * ddonahue99
66
+ * ernie
67
+ * gaaady
68
+ * gingerlime
69
+ * grumpit
70
+ * itsalongstory
71
+ * jonatack
72
+ * kogre
73
+ * nguyentrungson97
74
+ * nslocum
75
+ * omitter
76
+ * radar
77
+ * rilian
78
+ * terraplane
79
+ * tyronewilson
80
+ * vansy61
81
+ * willnet
82
+ * wzcolon
@@ -0,0 +1,99 @@
1
+ ---
2
+ sidebar_position: 1
3
+ slug: '/'
4
+ ---
5
+
6
+ # Introduction
7
+
8
+ Ransack will help you easily add **searching to your Rails application**, without any additional dependencies.
9
+
10
+ There are advanced searching solutions around, like ElasticSearch or Algolia. **Ransack** will do the job for many Rails websites, without the need to run additional infrastructure or work in a different language. With Ransack you do it all with standard Ruby and ERB.
11
+
12
+ Ready to move beyond the basics? Use **advanced features** like i18n and extensive configuration options.
13
+
14
+ Ransack is supported for Rails 7.0, 6.x on Ruby 2.6.6 and later.
15
+
16
+ ## Installation
17
+
18
+ To install `ransack` and add it to your Gemfile, run
19
+
20
+ ```ruby title='Gemfile'
21
+ gem 'ransack'
22
+ ```
23
+
24
+ ### Bleeding edge
25
+
26
+ If you would like to use the latest updates not yet published to RubyGems, use the `main` branch:
27
+
28
+ ```ruby title='Gemfile'
29
+ gem 'ransack', :github => 'activerecord-hackery/ransack', :branch => 'main'
30
+ ```
31
+
32
+ ### Demo application
33
+
34
+ The [Ransack Demo application](https://github.com/activerecord-hackery/ransack_demo) shows how to create [simple](http://ransack-demo.herokuapp.com) and
35
+ [advanced](http://ransack-demo.herokuapp.com/users/advanced_search) search forms for your Ruby on Rails application.
36
+
37
+
38
+ ## Issues tracker
39
+
40
+ * Before filing an issue, please read the [Contributing Guide](https://github.com/activerecord-hackery/ransack/CONTRIBUTING.md).
41
+ * File an issue if a bug is caused by Ransack, is new (has not already been reported), and _can be reproduced from the information you provide_.
42
+ * Please consider adding a branch with a failing spec describing the problem.
43
+ * Contributions are welcome. :smiley:
44
+ * Please do not use the issue tracker for personal support requests. Stack Overflow is a better place for that where a wider community can help you!
45
+
46
+
47
+ ## Contributions
48
+
49
+ To support the project:
50
+
51
+ * Consider supporting us via [Open Collective](https://opencollective.com/ransack/backers/badge.svg)
52
+ * Use Ransack in your apps, and let us know if you encounter anything that's
53
+ broken or missing. A failing spec to demonstrate the issue is awesome. A pull
54
+ request with passing tests is even better!
55
+ * Before filing an issue or pull request, be sure to read and follow the
56
+ [Contributing Guide](https://github.com/activerecord-hackery/ransack/CONTRIBUTING.md).
57
+ * Please use Stack Overflow or other sites for questions or discussion not
58
+ directly related to bug reports, pull requests, or documentation improvements.
59
+ * Spread the word on Twitter, Facebook, and elsewhere if Ransack's been useful
60
+ to you. The more people who are using the project, the quicker we can find and
61
+ fix bugs!
62
+
63
+ ## Contributors
64
+
65
+ Ransack was created by [Ernie Miller](http://twitter.com/erniemiller) and is developed and maintained by:
66
+ * [Sean Carroll](https://github.com/scarroll32)
67
+ * [Deivid Rodriguez](https://github.com/deivid-rodriguez)
68
+ * [Greg Molnar](https://github.com/gregmolnar)
69
+ * [A great group of contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors).
70
+ - Ransack's logo is designed by [Anıl Kılıç](https://github.com/anilkilic).
71
+
72
+ Alumni Maintainers
73
+ - [Jon Atack](http://twitter.com/jonatack)
74
+ - [Ryan Bigg](http://twitter.com/ryanbigg)
75
+
76
+ This project exists thanks to all the people who contribute. <img src="https://opencollective.com/ransack/contributors.svg?width=890&button=false" />
77
+
78
+
79
+ ## Backers
80
+
81
+ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/ransack#backer)]
82
+
83
+ <a href="https://opencollective.com/ransack#backers" target="_blank"><img src="https://opencollective.com/ransack/backers.svg?width=890" /></a>
84
+
85
+
86
+ ## Sponsors
87
+
88
+ Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/ransack#sponsor)]
89
+
90
+ <a href="https://opencollective.com/ransack/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/0/avatar.svg" /></a>
91
+ <a href="https://opencollective.com/ransack/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/1/avatar.svg" /></a>
92
+ <a href="https://opencollective.com/ransack/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/2/avatar.svg" /></a>
93
+ <a href="https://opencollective.com/ransack/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/3/avatar.svg" /></a>
94
+ <a href="https://opencollective.com/ransack/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/4/avatar.svg"/ ></a>
95
+ <a href="https://opencollective.com/ransack/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/5/avatar.svg" /></a>
96
+ <a href="https://opencollective.com/ransack/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/6/avatar.svg" /></a>
97
+ <a href="https://opencollective.com/ransack/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/7/avatar.svg" /></a>
98
+ <a href="https://opencollective.com/ransack/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/8/avatar.svg" /></a>
99
+ <a href="https://opencollective.com/ransack/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/9/avatar.svg" /></a>