├── .github
├── FUNDING.yml
├── SECURITY.md
└── workflows
│ ├── codeql.yml
│ ├── cronjob.yml
│ ├── deploy.yml
│ ├── rubocop.yml
│ ├── test-deploy.yml
│ └── test.yml
├── .gitignore
├── .nojekyll
├── .rubocop.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── bug_report_templates
├── test-ransack-scope-and-column-same-name.rb
└── test-ransacker-arel-present-predicate.rb
├── docs
├── .gitignore
├── .nojekyll
├── babel.config.js
├── blog
│ └── 2022-03-27-ransack-3.0.0.md
├── docs
│ ├── getting-started
│ │ ├── _category_.json
│ │ ├── advanced-mode.md
│ │ ├── configuration.md
│ │ ├── search-matches.md
│ │ ├── simple-mode.md
│ │ ├── sorting.md
│ │ └── using-predicates.md
│ ├── going-further
│ │ ├── _category_.json
│ │ ├── acts-as-taggable-on.md
│ │ ├── associations.md
│ │ ├── custom-predicates.md
│ │ ├── documentation.md
│ │ ├── exporting-to-csv.md
│ │ ├── external-guides.md
│ │ ├── form-customisation.md
│ │ ├── i18n.md
│ │ ├── img
│ │ │ └── create_release.png
│ │ ├── merging-searches.md
│ │ ├── other-notes.md
│ │ ├── polymorphic-search.md
│ │ ├── ransackers.md
│ │ ├── release_process.md
│ │ ├── saving-queries.md
│ │ ├── searching-postgres.md
│ │ └── wiki-contributors.md
│ └── intro.md
├── docusaurus.config.js
├── package.json
├── sidebars.js
├── src
│ ├── components
│ │ └── HomepageFeatures
│ │ │ ├── index.js
│ │ │ └── styles.module.css
│ ├── css
│ │ └── custom.css
│ └── pages
│ │ ├── index.module.css
│ │ └── markdown-page.md
├── static
│ ├── .nojekyll
│ ├── img
│ │ ├── docusaurus.png
│ │ ├── favicon.ico
│ │ ├── logo.svg
│ │ ├── tutorial
│ │ │ ├── docsVersionDropdown.png
│ │ │ └── localeDropdown.png
│ │ ├── undraw_docusaurus_mountain.svg
│ │ ├── undraw_docusaurus_react.svg
│ │ └── undraw_docusaurus_tree.svg
│ └── logo
│ │ ├── ransack-h.png
│ │ ├── ransack-h.svg
│ │ ├── ransack-v.png
│ │ ├── ransack-v.svg
│ │ ├── ransack.png
│ │ └── ransack.svg
└── yarn.lock
├── lib
├── polyamorous
│ ├── activerecord
│ │ ├── join_association.rb
│ │ ├── join_association_7_2.rb
│ │ ├── join_dependency.rb
│ │ └── reflection.rb
│ ├── join.rb
│ ├── polyamorous.rb
│ ├── swapping_reflection_class.rb
│ └── tree_node.rb
├── ransack.rb
└── ransack
│ ├── active_record.rb
│ ├── adapters
│ └── active_record
│ │ ├── base.rb
│ │ └── context.rb
│ ├── configuration.rb
│ ├── constants.rb
│ ├── context.rb
│ ├── helpers.rb
│ ├── helpers
│ ├── form_builder.rb
│ └── form_helper.rb
│ ├── invalid_search_error.rb
│ ├── locale
│ ├── ar.yml
│ ├── az.yml
│ ├── bg.yml
│ ├── ca.yml
│ ├── cs.yml
│ ├── da.yml
│ ├── de.yml
│ ├── el.yml
│ ├── en.yml
│ ├── es.yml
│ ├── fa.yml
│ ├── fi.yml
│ ├── fr.yml
│ ├── hu.yml
│ ├── id.yml
│ ├── it.yml
│ ├── ja.yml
│ ├── ko.yml
│ ├── nl.yml
│ ├── pt-BR.yml
│ ├── ro.yml
│ ├── ru.yml
│ ├── sk.yml
│ ├── sv.yml
│ ├── tr.yml
│ ├── zh-CN.yml
│ └── zh-TW.yml
│ ├── naming.rb
│ ├── nodes
│ ├── attribute.rb
│ ├── bindable.rb
│ ├── condition.rb
│ ├── grouping.rb
│ ├── node.rb
│ ├── sort.rb
│ └── value.rb
│ ├── predicate.rb
│ ├── ransacker.rb
│ ├── search.rb
│ ├── translate.rb
│ ├── version.rb
│ └── visitor.rb
├── ransack.gemspec
└── spec
├── blueprints
├── articles.rb
├── comments.rb
├── notes.rb
├── people.rb
└── tags.rb
├── console.rb
├── helpers
├── polyamorous_helper.rb
└── ransack_helper.rb
├── polyamorous
├── activerecord_compatibility_spec.rb
├── join_association_spec.rb
├── join_dependency_spec.rb
└── join_spec.rb
├── ransack
├── adapters
│ └── active_record
│ │ ├── base_spec.rb
│ │ └── context_spec.rb
├── configuration_spec.rb
├── helpers
│ ├── form_builder_spec.rb
│ └── form_helper_spec.rb
├── nodes
│ ├── condition_spec.rb
│ ├── grouping_spec.rb
│ └── value_spec.rb
├── predicate_spec.rb
├── search_spec.rb
└── translate_spec.rb
├── spec_helper.rb
└── support
├── en.yml
└── schema.rb
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | tidelift: rubygems/ransack
4 |
--------------------------------------------------------------------------------
/.github/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | At the moment, only the latest major.minor release stream is supported with
6 | security updates.
7 |
8 | ## Reporting a Vulnerability
9 |
10 | Please use the Tidelift security contact to [report a security
11 | vulnerability](https://tidelift.com/security). Tidelift will coordinate the fix
12 | and disclosure.
13 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '43 11 * * 3'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'ruby' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v3
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v2
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 |
52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
53 | # queries: security-extended,security-and-quality
54 |
55 |
56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
57 | # If this step fails, then you should remove it and run the build manually (see below)
58 | - name: Autobuild
59 | uses: github/codeql-action/autobuild@v2
60 |
61 | # ℹ️ Command-line programs to run using the OS shell.
62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
63 |
64 | # If the Autobuild fails above, remove it and uncomment the following three lines.
65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
66 |
67 | # - run: |
68 | # echo "Run, Build Application using script"
69 | # ./location_of_script_within_repo/buildscript.sh
70 |
71 | - name: Perform CodeQL Analysis
72 | uses: github/codeql-action/analyze@v2
73 |
--------------------------------------------------------------------------------
/.github/workflows/cronjob.yml:
--------------------------------------------------------------------------------
1 | name: cronjob
2 |
3 | on:
4 | schedule:
5 | - cron: "0 0 * * *"
6 |
7 | jobs:
8 | sqlite3:
9 | runs-on: ubuntu-22.04
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | ruby:
14 | - 3.2.2
15 | env:
16 | DB: sqlite3
17 | RAILS: main
18 | steps:
19 | - uses: actions/checkout@v2
20 | - name: Set up Ruby
21 | uses: ruby/setup-ruby@v1
22 | with:
23 | ruby-version: ${{ matrix.ruby }}
24 | - name: Install dependencies
25 | run: bundle install
26 | - name: Run tests
27 | run: bundle exec rspec
28 |
29 | mysql:
30 | runs-on: ubuntu-22.04
31 | strategy:
32 | fail-fast: false
33 | matrix:
34 | ruby:
35 | - 3.2.2
36 | env:
37 | DB: mysql
38 | RAILS: main
39 | MYSQL_USERNAME: root
40 | MYSQL_PASSWORD: root
41 | steps:
42 | - uses: actions/checkout@v2
43 | - name: Set up Ruby
44 | uses: ruby/setup-ruby@v1
45 | with:
46 | ruby-version: ${{ matrix.ruby }}
47 | - name: Startup MySQL
48 | run: |
49 | sudo systemctl start mysql.service
50 | - name: Setup databases
51 | run: |
52 | mysql --user=root --password=root --host=127.0.0.1 -e 'create database ransack collate utf8_general_ci;';
53 | mysql --user=root --password=root --host=127.0.0.1 -e 'use ransack;show variables like "%character%";show variables like "%collation%";';
54 | - name: Install dependencies
55 | run: bundle install
56 | - name: Run tests
57 | run: bundle exec rspec
58 |
59 | postgis:
60 | runs-on: ubuntu-22.04
61 | strategy:
62 | fail-fast: false
63 | matrix:
64 | ruby:
65 | - 3.2.2
66 | env:
67 | DB: postgis
68 | RAILS: main
69 | DATABASE_USERNAME: postgres
70 | DATABASE_PASSWORD: postgres
71 | DATABASE_HOST: 127.0.0.1
72 | services:
73 | postgres:
74 | image: postgres
75 | ports:
76 | - 5432:5432
77 | env:
78 | POSTGRES_PASSWORD: postgres
79 | POSTGRES_HOST_AUTH_METHOD: trust
80 | # Set health checks to wait until postgres has started
81 | options: >-
82 | --health-cmd pg_isready
83 | --health-interval 10s
84 | --health-timeout 5s
85 | --health-retries 5
86 |
87 | steps:
88 | - uses: actions/checkout@v2
89 | - name: Set up Ruby
90 | uses: ruby/setup-ruby@v1
91 | with:
92 | ruby-version: ${{ matrix.ruby }}
93 | - name: Setup databases
94 | run: |
95 | psql -h localhost -p 5432 -W postgres -c 'create database ransack;' -U postgres;
96 | - name: Install dependencies
97 | run: bundle install
98 | - name: Run tests
99 | run: bundle exec rspec
100 |
101 | postgres:
102 | runs-on: ubuntu-22.04
103 | strategy:
104 | fail-fast: false
105 | matrix:
106 | ruby:
107 | - 3.2.2
108 | env:
109 | DB: postgres
110 | RAILS: main
111 | DATABASE_USERNAME: postgres
112 | DATABASE_PASSWORD: postgres
113 | DATABASE_HOST: 127.0.0.1
114 | services:
115 | postgres:
116 | image: postgres
117 | ports:
118 | - 5432:5432
119 | env:
120 | POSTGRES_PASSWORD: postgres
121 | POSTGRES_HOST_AUTH_METHOD: trust
122 | # Set health checks to wait until postgres has started
123 | options: >-
124 | --health-cmd pg_isready
125 | --health-interval 10s
126 | --health-timeout 5s
127 | --health-retries 5
128 |
129 | steps:
130 | - uses: actions/checkout@v2
131 | - name: Set up Ruby
132 | uses: ruby/setup-ruby@v1
133 | with:
134 | ruby-version: ${{ matrix.ruby }}
135 | - name: Setup databases
136 | run: |
137 | psql -h localhost -p 5432 -W postgres -c 'create database ransack;' -U postgres;
138 | - name: Install dependencies
139 | run: bundle install
140 | - name: Run tests
141 | run: bundle exec rspec
142 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | deploy:
10 | name: Deploy to GitHub Pages
11 |
12 | runs-on: ubuntu-22.04
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 |
17 | - uses: actions/setup-node@v3
18 | with:
19 | node-version: 16
20 | cache: yarn
21 | cache-dependency-path: docs/yarn.lock
22 |
23 | - name: Install dependencies
24 | run: yarn install --frozen-lockfile
25 | working-directory: docs
26 |
27 | - name: Build website
28 | run: yarn build
29 | working-directory: docs
30 |
31 | - name: Deploy to GitHub Pages
32 | uses: peaceiris/actions-gh-pages@v3
33 | with:
34 | github_token: ${{ secrets.GITHUB_TOKEN }}
35 | publish_dir: docs/build
36 |
--------------------------------------------------------------------------------
/.github/workflows/rubocop.yml:
--------------------------------------------------------------------------------
1 | name: rubocop
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-22.04
10 |
11 | steps:
12 | - uses: actions/checkout@v2
13 | - name: Set up Ruby
14 | uses: ruby/setup-ruby@v1
15 | with:
16 | ruby-version: 3.2.2
17 | - name: Install gems
18 | run: bundle install --jobs 4 --retry 3
19 | - name: Run RuboCop
20 | run: bundle exec rubocop --parallel
21 |
--------------------------------------------------------------------------------
/.github/workflows/test-deploy.yml:
--------------------------------------------------------------------------------
1 | name: Test deploy to GitHub Pages
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | test-deploy:
10 | name: Test deploy to GitHub Pages
11 |
12 | runs-on: ubuntu-22.04
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 |
17 | - uses: actions/setup-node@v3
18 | with:
19 | node-version: 16
20 | cache: yarn
21 | cache-dependency-path: docs/yarn.lock
22 |
23 | - name: Install dependencies
24 | run: yarn install --frozen-lockfile
25 | working-directory: docs
26 |
27 | - name: Test build website
28 | run: yarn build
29 | working-directory: docs
30 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | sqlite3:
11 | runs-on: ubuntu-latest
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | rails:
16 | - 8-0-stable
17 | - v7.2.2
18 | - v7.1.0
19 | ruby:
20 | - 3.2.2
21 | - 3.1.4
22 | exclude:
23 | - rails: 8-0-stable
24 | ruby: 3.1.4
25 | env:
26 | DB: sqlite3
27 | RAILS: ${{ matrix.rails }}
28 | steps:
29 | - uses: actions/checkout@v2
30 | - name: Set up Ruby
31 | uses: ruby/setup-ruby@v1
32 | with:
33 | ruby-version: ${{ matrix.ruby }}
34 | bundler-cache: true
35 | - name: Run tests
36 | run: bundle exec rspec
37 |
38 | mysql:
39 | runs-on: ubuntu-latest
40 | strategy:
41 | fail-fast: false
42 | matrix:
43 | rails:
44 | - 8-0-stable
45 | - 7-2-stable
46 | - v7.1.0
47 | ruby:
48 | - 3.2.2
49 | - 3.1.4
50 | exclude:
51 | - rails: 8-0-stable
52 | ruby: 3.1.4
53 |
54 | env:
55 | DB: mysql
56 | RAILS: ${{ matrix.rails }}
57 | MYSQL_USERNAME: root
58 | MYSQL_PASSWORD: root
59 | steps:
60 | - uses: actions/checkout@v2
61 | - name: Set up Ruby
62 | uses: ruby/setup-ruby@v1
63 | with:
64 | ruby-version: ${{ matrix.ruby }}
65 | bundler-cache: true
66 | - name: Startup MySQL
67 | run: |
68 | sudo systemctl start mysql.service
69 | - name: Setup databases
70 | run: |
71 | mysql --user=root --password=root --host=127.0.0.1 -e 'create database ransack collate utf8_general_ci;';
72 | mysql --user=root --password=root --host=127.0.0.1 -e 'use ransack;show variables like "%character%";show variables like "%collation%";';
73 | - name: Run tests
74 | run: bundle exec rspec
75 |
76 | postgis:
77 | runs-on: ubuntu-22.04
78 | strategy:
79 | fail-fast: false
80 | matrix:
81 | rails:
82 | - 7-2-stable
83 | - v7.1.0
84 | ruby:
85 | - 3.2.2
86 | - 3.1.4
87 | env:
88 | DB: postgis
89 | RAILS: ${{ matrix.rails }}
90 | DATABASE_USERNAME: postgres
91 | DATABASE_PASSWORD: postgres
92 | DATABASE_HOST: 127.0.0.1
93 | services:
94 | postgres:
95 | image: postgres
96 | ports:
97 | - 5432:5432
98 | env:
99 | POSTGRES_PASSWORD: postgres
100 | POSTGRES_HOST_AUTH_METHOD: trust
101 | # Set health checks to wait until postgres has started
102 | options: >-
103 | --health-cmd pg_isready
104 | --health-interval 10s
105 | --health-timeout 5s
106 | --health-retries 5
107 |
108 | steps:
109 | - uses: actions/checkout@v2
110 | - name: Set up Ruby
111 | uses: ruby/setup-ruby@v1
112 | with:
113 | ruby-version: ${{ matrix.ruby }}
114 | bundler-cache: true
115 | - name: Setup databases
116 | run: |
117 | psql -h localhost -p 5432 -W postgres -c 'create database ransack;' -U postgres;
118 | - name: Run tests
119 | run: bundle exec rspec
120 |
121 | postgres:
122 | runs-on: ubuntu-latest
123 | strategy:
124 | fail-fast: false
125 | matrix:
126 | rails:
127 | - 8-0-stable
128 | - 7-2-stable
129 | - v7.1.0
130 | ruby:
131 | - 3.2.2
132 | - 3.1.4
133 | exclude:
134 | - rails: 8-0-stable
135 | ruby: 3.1.4
136 | env:
137 | DB: postgres
138 | RAILS: ${{ matrix.rails }}
139 | DATABASE_USERNAME: postgres
140 | DATABASE_PASSWORD: postgres
141 | DATABASE_HOST: 127.0.0.1
142 | services:
143 | postgres:
144 | image: postgres
145 | ports:
146 | - 5432:5432
147 | env:
148 | POSTGRES_PASSWORD: postgres
149 | POSTGRES_HOST_AUTH_METHOD: trust
150 | # Set health checks to wait until postgres has started
151 | options: >-
152 | --health-cmd pg_isready
153 | --health-interval 10s
154 | --health-timeout 5s
155 | --health-retries 5
156 |
157 | steps:
158 | - uses: actions/checkout@v2
159 | - name: Set up Ruby
160 | uses: ruby/setup-ruby@v1
161 | with:
162 | ruby-version: ${{ matrix.ruby }}
163 | bundler-cache: true
164 | - name: Setup databases
165 | run: |
166 | psql -h localhost -p 5432 -W postgres -c 'create database ransack;' -U postgres;
167 | - name: Run tests
168 | run: bundle exec rspec
169 |
170 | bug-report-templates:
171 | runs-on: ubuntu-latest
172 | steps:
173 | - uses: actions/checkout@v2
174 | - name: Set up Ruby
175 | uses: ruby/setup-ruby@v1
176 | with:
177 | ruby-version: 3.2.2
178 | bundler-cache: true
179 | - name: Run bug report templates
180 | run: |
181 | ruby bug_report_templates/test-ransacker-arel-present-predicate.rb
182 | ruby bug_report_templates/test-ransack-scope-and-column-same-name.rb
183 | rm Gemfile Gemfile.lock
184 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | .bundle
3 | Gemfile.lock
4 | pkg/*
5 | coverage/*
6 | .DS_Store
7 | .byebug_history
8 |
--------------------------------------------------------------------------------
/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/.nojekyll
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | plugins:
2 | - rubocop-rspec
3 |
4 | AllCops:
5 | TargetRubyVersion: 3.0
6 |
7 | DisabledByDefault: true
8 |
9 | Exclude:
10 | - "docs/**/*"
11 |
12 | Layout/EmptyLineAfterMagicComment:
13 | Enabled: true
14 |
15 | Layout/EmptyLineBetweenDefs:
16 | Enabled: true
17 |
18 | Layout/EmptyLines:
19 | Enabled: true
20 |
21 | Layout/EmptyLinesAroundBlockBody:
22 | Enabled: true
23 |
24 | Layout/FirstArrayElementIndentation:
25 | EnforcedStyle: consistent
26 |
27 | Layout/SpaceAfterComma:
28 | Enabled: true
29 |
30 | Layout/SpaceInsideBlockBraces:
31 | Enabled: true
32 |
33 | Layout/SpaceInsideHashLiteralBraces:
34 | Enabled: true
35 |
36 | Layout/SpaceInsideParens:
37 | Enabled: true
38 |
39 | Layout/TrailingEmptyLines:
40 | Enabled: true
41 |
42 | RSpec/EmptyLineAfterFinalLet:
43 | Enabled: true
44 |
45 | Style/HashSyntax:
46 | Enabled: true
47 |
48 | Style/RedundantFileExtensionInRequire:
49 | Enabled: true
50 |
51 | Style/RedundantReturn:
52 | Enabled: true
53 |
54 | Style/SelfAssignment:
55 | Enabled: true
56 |
57 | Style/Semicolon:
58 | Enabled: true
59 |
60 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | gemspec
3 |
4 | gem 'rake'
5 |
6 | rails = ENV['RAILS'] || '7-2-stable'
7 |
8 | rails_version = case rails
9 | when /\// # A path
10 | File.read(File.join(rails, "RAILS_VERSION"))
11 | when /^v/ # A tagged version
12 | rails.gsub(/^v/, '')
13 | else
14 | rails
15 | end
16 |
17 | gem 'faker'
18 | if ::Gem::Version.new(rails_version) > ::Gem::Version.new('7.3')
19 | gem 'sqlite3', '>= 2.1'
20 | else
21 | gem 'sqlite3', '~> 1.4'
22 | end
23 | gem 'pg'
24 | gem 'activerecord-postgis-adapter'
25 | gem 'pry'
26 | gem 'byebug'
27 |
28 | case rails
29 | when /\// # A path
30 | gem 'activesupport', path: "#{rails}/activesupport"
31 | gem 'activemodel', path: "#{rails}/activemodel"
32 | gem 'activerecord', path: "#{rails}/activerecord", require: false
33 | gem 'actionpack', path: "#{rails}/actionpack"
34 | gem 'actionview', path: "#{rails}/actionview"
35 | when /^v/ # A tagged version
36 | git 'https://github.com/rails/rails.git', tag: rails do
37 | gem 'activesupport'
38 | gem 'activemodel'
39 | gem 'activerecord', require: false
40 | gem 'actionpack'
41 | end
42 | else
43 | git 'https://github.com/rails/rails.git', branch: rails do
44 | gem 'activesupport'
45 | gem 'activemodel'
46 | gem 'activerecord', require: false
47 | gem 'actionpack'
48 | end
49 | end
50 | gem 'mysql2'
51 |
52 | group :test do
53 | gem 'machinist', '~> 1.0.6'
54 | gem 'rspec'
55 | gem 'simplecov', require: false
56 | end
57 |
58 | gem 'rubocop', require: false
59 | gem 'rubocop-rspec', require: false
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010 Ernie Miller
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler'
2 | require 'rspec/core/rake_task'
3 |
4 | Bundler::GemHelper.install_tasks
5 |
6 | RSpec::Core::RakeTask.new(:spec) do |rspec|
7 | ENV['SPEC'] = 'spec/ransack/**/*_spec.rb'
8 | # With Rails 3, using `--backtrace` raises 'invalid option' when testing.
9 | # With Rails 4 and 5 it can be uncommented to see the backtrace:
10 | #
11 | # rspec.rspec_opts = ['--backtrace']
12 | end
13 |
14 | task :default do
15 | Rake::Task["spec"].invoke
16 | end
17 |
18 | desc "Open an irb session with Ransack and the sample data used in specs"
19 | task :console do
20 | require 'pry'
21 | require File.expand_path('../spec/console.rb', __FILE__)
22 | ARGV.clear
23 | Pry.start
24 | end
25 |
--------------------------------------------------------------------------------
/bug_report_templates/test-ransack-scope-and-column-same-name.rb:
--------------------------------------------------------------------------------
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: '7-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
79 |
--------------------------------------------------------------------------------
/bug_report_templates/test-ransacker-arel-present-predicate.rb:
--------------------------------------------------------------------------------
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: '7-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 |
60 | def self.ransackable_attributes(_auth_object = nil)
61 | ["name", "number"]
62 | end
63 | end
64 |
65 | class BugTest < Minitest::Test
66 | def test_ransackers
67 | sql = Project.ransack({ number_present: 1 }).result.to_sql
68 | puts sql
69 | assert_equal "SELECT \"projects\".* FROM \"projects\" WHERE (\"projects\".\"number\" IS NOT NULL AND \"projects\".\"number\" != '')", sql
70 |
71 | sql = Project.ransack({ name_present: 1 }).result.to_sql
72 | puts sql
73 | assert_equal "SELECT \"projects\".* FROM \"projects\" WHERE (projects.name IS NOT NULL AND projects.name != '')", sql
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | yarn-debug.log*
19 | yarn-error.log*
20 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/blog/2022-03-27-ransack-3.0.0.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: ransack-3-0-0
3 | title: Ransack 3.0.0
4 | authors:
5 | name: Sean Carroll
6 | title: Ransack Core Team
7 | tags: [ransack, release]
8 | ---
9 |
10 | Ransack has been a part of many Rubyists toolboxes for years and 3.0.0 is a major release. We have a number of new features and one breaking change. As part of 3.0.0, we decided to launch this documentation website, merging in the Wiki and the content from the README.
11 |
12 | With 3.0.0 we are hoping to re-energise the community, we need help on closing out old issues, refactoring the codebase and even some design work.
13 |
14 | I also wanted to let you know that Ernie Miller (creator of Ransack) has decided to leave the project completely, he has this message for the community:
15 |
16 | > While my own personal development efforts have been spent elsewhere as of late, I'm keenly aware of how many people still depend on some of the software I originally wrote all those years ago.
17 |
18 | > That's why I'm grateful to be able to step away from the ActiveRecord Hackery organization (and, specifically, maintenance of Ransack) without impacting those users. I'm thankful that Sean, David, Greg, and others will continue to support users, and wish them the best as they move forward without me!
19 |
20 | Please join me in thanking Ernie for bringing Ransack to life, I personally think it is one of the most amazing Rails libraries out there.
21 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Getting started",
3 | "position": 2
4 | }
5 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/advanced-mode.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: Advanced Mode
4 | ---
5 |
6 |
7 | "Advanced" searches Rails's nested attributes functionality in order to
8 | generate complex queries with nested AND/OR groupings, etc. This takes a bit
9 | more work but can generate some pretty cool search interfaces that put a lot of
10 | power in the hands of your users.
11 |
12 | A notable drawback with these searches is
13 | that the increased size of the parameter string will typically force you to use
14 | the HTTP POST method instead of GET.
15 |
16 |
17 | ## Tweak your routes
18 |
19 | ```ruby
20 | resources :people do
21 | collection do
22 | match 'search' => 'people#search', via: [:get, :post], as: :search
23 | end
24 | end
25 | ```
26 |
27 | ## Add a controller action
28 |
29 | ```ruby
30 | def search
31 | index
32 | render :index
33 | end
34 | ```
35 |
36 | ## Update your form
37 |
38 | ```erb
39 | <%= search_form_for @q, url: search_people_path,
40 | html: { method: :post } do |f| %>
41 | ```
42 |
43 | Once you've done so, you can make use of the helpers in [Ransack::Helpers::FormBuilder](https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/helpers/form_builder.rb) to
44 | construct much more complex search forms, such as the one on the
45 | [demo app](http://ransack-demo.herokuapp.com/users/advanced_search)
46 | (source code [here](https://github.com/activerecord-hackery/ransack_demo)).
47 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/configuration.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | title: Configuration
4 | ---
5 |
6 |
7 |
8 | Ransack may be easily configured. The best place to put configuration is in an initializer file at `config/initializers/ransack.rb`, containing code such as:
9 |
10 | ```ruby
11 | Ransack.configure do |config|
12 |
13 | # Change default search parameter key name.
14 | # Default key name is :q
15 | config.search_key = :query
16 |
17 | # Raise errors if a query contains an unknown predicate or attribute.
18 | # Default is true (do not raise error on unknown conditions).
19 | config.ignore_unknown_conditions = false
20 |
21 | # Globally display sort links without the order indicator arrow.
22 | # Default is false (sort order indicators are displayed).
23 | # This can also be configured individually in each sort link (see the README).
24 | config.hide_sort_order_indicators = true
25 |
26 | end
27 | ```
28 |
29 | ## Custom search parameter key name
30 |
31 | Sometimes there are situations when the default search parameter name cannot be used, for instance,
32 | if there are two searches on one page. Another name may be set using the `search_key` option in the `ransack` or `search` methods in the controller, and in the `@search_form_for` method in the view.
33 |
34 | ### In the controller
35 |
36 | ```ruby
37 | @search = Log.ransack(params[:log_search], search_key: :log_search)
38 | # or
39 | @search = Log.search(params[:log_search], search_key: :log_search)
40 | ```
41 |
42 | ### In the view
43 |
44 | ```erb
45 | <%= f.search_form_for @search, as: :log_search %>
46 | <%= sort_link(@search) %>
47 | ```
48 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/search-matches.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Search Matchers
3 | ---
4 |
5 | ### Search Matchers
6 |
7 | List of all possible predicates
8 |
9 |
10 | | Predicate | Description | Notes |
11 | | ------------- | ------------- |-------- |
12 | | `*_eq` | equal | |
13 | | `*_not_eq` | not equal | |
14 | | `*_matches` | matches with `LIKE` | e.g. `q[email_matches]=%@gmail.com`|
15 | | `*_does_not_match` | does not match with `LIKE` | |
16 | | `*_matches_any` | Matches any | |
17 | | `*_matches_all` | Matches all | |
18 | | `*_does_not_match_any` | Does not match any | |
19 | | `*_does_not_match_all` | Does not match all | |
20 | | `*_lt` | less than | |
21 | | `*_lteq` | less than or equal | |
22 | | `*_gt` | greater than | |
23 | | `*_gteq` | greater than or equal | |
24 | | `*_present` | not null and not empty | Only compatible with string columns. Example: `q[name_present]=1` (SQL: `col is not null AND col != ''`) |
25 | | `*_blank` | is null or empty. | (SQL: `col is null OR col = ''`) |
26 | | `*_null` | is null | |
27 | | `*_not_null` | is not null | |
28 | | `*_in` | match any values in array | e.g. `q[name_in][]=Alice&q[name_in][]=Bob` |
29 | | `*_not_in` | match none of values in array | |
30 | | `*_lt_any` | Less than any | SQL: `col < value1 OR col < value2` |
31 | | `*_lteq_any` | Less than or equal to any | |
32 | | `*_gt_any` | Greater than any | |
33 | | `*_gteq_any` | Greater than or equal to any | |
34 | | `*_lt_all` | Less than all | SQL: `col < value1 AND col < value2` |
35 | | `*_lteq_all` | Less than or equal to all | |
36 | | `*_gt_all` | Greater than all | |
37 | | `*_gteq_all` | Greater than or equal to all | |
38 | | `*_not_eq_all` | none of values in a set | |
39 | | `*_start` | Starts with | SQL: `col LIKE 'value%'` |
40 | | `*_not_start` | Does not start with | |
41 | | `*_start_any` | Starts with any of | |
42 | | `*_start_all` | Starts with all of | |
43 | | `*_not_start_any` | Does not start with any of | |
44 | | `*_not_start_all` | Does not start with all of | |
45 | | `*_end` | Ends with | SQL: `col LIKE '%value'` |
46 | | `*_not_end` | Does not end with | |
47 | | `*_end_any` | Ends with any of | |
48 | | `*_end_all` | Ends with all of | |
49 | | `*_not_end_any` | | |
50 | | `*_not_end_all` | | |
51 | | `*_cont` | Contains value | uses `LIKE` |
52 | | `*_cont_any` | Contains any of | |
53 | | `*_cont_all` | Contains all of | |
54 | | `*_not_cont` | Does not contain |
55 | | `*_not_cont_any` | Does not contain any of | |
56 | | `*_not_cont_all` | Does not contain all of | |
57 | | `*_i_cont` | Contains value with case insensitive | uses `ILIKE` |
58 | | `*_i_cont_any` | Contains any of values with case insensitive | |
59 | | `*_i_cont_all` | Contains all of values with case insensitive | |
60 | | `*_not_i_cont` | Does not contain with case insensitive |
61 | | `*_not_i_cont_any` | Does not contain any of values with case insensitive | |
62 | | `*_not_i_cont_all` | Does not contain all of values with case insensitive | |
63 | | `*_true` | is true | |
64 | | `*_false` | is false | |
65 |
66 |
67 | See full list: https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/locale/en.yml#L16
68 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/sorting.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Sorting
3 | ---
4 |
5 |
6 | # Sorting
7 |
8 | ## Sorting in the View
9 |
10 | You can add a form to capture sorting and filtering options together.
11 |
12 | ```erb
13 | # app/views/posts/index.html.erb
14 |
15 | <%= search_form_for @q do |f| %>
16 | <%= f.label :title_cont %>
17 | <%= f.search_field :title_cont %>
18 |
19 | <%= f.submit "Search" %>
20 | <% end %>
21 |
22 |
23 |
24 |
25 | <%= sort_link(@q, :title, "Title") %>
26 | <%= sort_link(@q, :category, "Category") %>
27 | <%= sort_link(@q, :created_at, "Created at") %>
28 |
29 |
30 |
31 |
32 | <% @posts.each do |post| %>
33 |
34 | <%= post.title %>
35 | <%= post.category %>
36 | <%= post.created_at.to_s(:long) %>
37 |
38 | <% end %>
39 |
40 |
41 | ```
42 |
43 | ## Sorting in the Controller
44 |
45 | To specify a default search sort field + order in the controller `index`:
46 |
47 | ```ruby
48 | # app/controllers/posts_controller.rb
49 | class PostsController < ActionController::Base
50 | def index
51 | @q = Post.ransack(params[:q])
52 | @q.sorts = 'title asc' if @q.sorts.empty?
53 |
54 | @posts = @q.result(distinct: true)
55 | end
56 | end
57 | ```
58 |
59 | Multiple sorts can be set by:
60 |
61 | ```ruby
62 | # app/controllers/posts_controller.rb
63 | class PostsController < ActionController::Base
64 | def index
65 | @q = Post.ransack(params[:q])
66 | @q.sorts = ['title asc', 'created_at desc'] if @q.sorts.empty?
67 |
68 | @posts = @q.result(distinct: true)
69 | end
70 | end
71 | ```
72 |
--------------------------------------------------------------------------------
/docs/docs/going-further/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Going further",
3 | "position": 2
4 | }
5 |
--------------------------------------------------------------------------------
/docs/docs/going-further/acts-as-taggable-on.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Acts-as-taggable-on
3 | sidebar_position: 13
4 | ---
5 |
6 | ## Using Acts As Taggable On
7 |
8 | If you have an `ActiveRecord` model and you're using [acts-as-taggable-on](https://github.com/mbleigh/acts-as-taggable-on),
9 | chances are you might want to search on tagged fields. Follow the instructions to install the gem and then set up your project files.
10 |
11 | ### Configure the model
12 |
13 | `app/models/tasks.rb`
14 |
15 | You can call the tagging field anything you like, it just needs to be plural. No migration is needed as this is stored in the internal ActsAsTaggable tables (`tags` and `taggings`).
16 |
17 | ```ruby
18 | class Task < ApplicationRecord
19 | acts_as_taggable_on :projects
20 | end
21 | ```
22 |
23 | ### Controller
24 |
25 | Add a field to strong params in the controller. Use the singular name with `_list`.
26 |
27 | `app/controllers/tasks_controller.rb`
28 |
29 | ```ruby
30 | def strong_params
31 | params
32 | .require(:tasks)
33 | .permit(:task, :example_field, :project_list)
34 | ```
35 |
36 | ### Form
37 |
38 | We need to `send` the tag fieldname to our model, also using the singular naming.
39 |
40 | ```erb
41 |
42 | <%= f.label :project_list %>
43 | <%= f.text_field :project_list, value: @task.send(:project_list).to_s %>
44 |
45 | ```
46 |
47 | Now we can collect our data via the form, with tags separated by commas.
48 |
49 | ## Ransack Search
50 |
51 | Imagine you have the following two instances of `Task`:
52 |
53 | ```ruby
54 | { id: 1, name: 'Clean up my room', projects: [ 'Home', 'Personal' ] }
55 | { id: 2, name: 'Complete math exercises', projects: [ 'Homework', 'Study' ] }
56 | ```
57 |
58 | When you're writing a `Ransack` search form, you can choose any of the following options:
59 |
60 | ```erb
61 | <%= search_form_for @search do |f| %>
62 | <%= f.text_field :projects_name_in %>
63 | <%= f.text_field :projects_name_eq %>
64 | <%= f.text_field :projects_name_cont %>
65 | <% end %>
66 | ```
67 |
68 | ### Option A - Match keys exactly
69 |
70 | Option `A` will match keys exactly. This is the solution to choose if you want to distinguish 'Home' from 'Homework': searching for 'Home' will return just the `Task` with id 1. It also allows searching for more than one tag at once (comma separated):
71 | - `Home, Personal` will return task 1
72 | - `Home, Homework` will return task 1 and 2
73 |
74 | ### Option B - match key combinations
75 |
76 | Option `B` will match all keys exactly. This is the solution if you wanna search for specific combinations of tags:
77 | - `Home` will return nothing, as there is no Task with just the `Home` tag
78 | - `Home, Personal` will return task 1
79 |
80 | ### Option C - match substrings
81 |
82 | Option `C` is used to match substrings. This is useful when you don't care for the exact tag, but only for part of it:
83 | - `Home` will return task 1 and 2 (`/Home/` matches both `"Home"` and `"Homework"`)
84 |
85 | ### Option D - select from a list of tags
86 |
87 | In Option `D` we allow the user to select a list of valid tags and then search against them. We use the plural name here.
88 |
89 | ```erb
90 |
91 | <%= f.label :projects_name, 'Project' %>
92 | <%= f.select :projects_name_in, ActsAsTaggableOn::Tag.distinct.order(:name).pluck(:name) %>
93 |
94 | ```
95 |
96 | ## Multitenancy
97 |
98 | ActsAsTaggableOn allows scoping of tags based on another field on the model. Suppose we have a `language` field on the model, as an effective second level key. We would adjust our model to look like this:
99 |
100 | ```ruby
101 | class Task < ApplicationRecord
102 | acts_as_taggable_on :projects
103 | acts_as_taggable_tenant :language
104 | end
105 | ```
106 |
107 | The Ransack search is then filtered using the `for_tenant` method
108 |
109 | ```erb
110 |
111 | <%= f.label :projects_name, 'Project' %>
112 | <%= f.select :projects_name_in, ActsAsTaggableOn::Tag.for_tenant('fr').distinct.order(:name).pluck(:name) %>
113 |
114 |
115 |
--------------------------------------------------------------------------------
/docs/docs/going-further/associations.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: Associations
4 | ---
5 |
6 | ### Associations
7 |
8 | You can easily use Ransack to search for objects in `has_many` and `belongs_to`
9 | associations.
10 |
11 | Given these associations...
12 |
13 | ```ruby
14 | class Employee < ActiveRecord::Base
15 | belongs_to :supervisor
16 |
17 | # has attributes first_name:string and last_name:string
18 | end
19 |
20 | class Department < ActiveRecord::Base
21 | has_many :supervisors
22 |
23 | # has attribute title:string
24 | end
25 |
26 | class Supervisor < ActiveRecord::Base
27 | belongs_to :department
28 | has_many :employees
29 |
30 | # has attribute last_name:string
31 | end
32 | ```
33 |
34 | ... and a controller...
35 |
36 | ```ruby
37 | class SupervisorsController < ApplicationController
38 | def index
39 | @q = Supervisor.ransack(params[:q])
40 | @supervisors = @q.result.includes(:department, :employees)
41 | end
42 | end
43 | ```
44 |
45 | ... you might set up your form like this...
46 |
47 | ```erb
48 | <%= search_form_for @q do |f| %>
49 | <%= f.label :last_name_cont %>
50 | <%= f.search_field :last_name_cont %>
51 |
52 | <%= f.label :department_title_cont %>
53 | <%= f.search_field :department_title_cont %>
54 |
55 | <%= f.label :employees_first_name_or_employees_last_name_cont %>
56 | <%= f.search_field :employees_first_name_or_employees_last_name_cont %>
57 |
58 | <%= f.submit "search" %>
59 | <% end %>
60 | ...
61 | <%= content_tag :table do %>
62 | <%= content_tag :th, sort_link(@q, :last_name) %>
63 | <%= content_tag :th, sort_link(@q, :department_title) %>
64 | <%= content_tag :th, sort_link(@q, :employees_last_name) %>
65 | <% end %>
66 | ```
67 |
68 | If you have trouble sorting on associations, try using an SQL string with the
69 | pluralized table (`'departments.title'`,`'employees.last_name'`) instead of the
70 | symbolized association (`:department_title)`, `:employees_last_name`).
71 |
--------------------------------------------------------------------------------
/docs/docs/going-further/custom-predicates.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: Custom predicates
4 | ---
5 |
6 | If you'd like to add your own custom Ransack predicates:
7 |
8 | ```ruby
9 | # config/initializers/ransack.rb
10 |
11 | Ransack.configure do |config|
12 | config.add_predicate 'equals_diddly', # Name your predicate
13 | # What non-compound ARel predicate will it use? (eq, matches, etc)
14 | arel_predicate: 'eq',
15 | # Format incoming values as you see fit. (Default: Don't do formatting)
16 | formatter: proc { |v| "#{v}-diddly" },
17 | # Validate a value. An "invalid" value won't be used in a search.
18 | # Below is default.
19 | validator: proc { |v| v.present? },
20 | # Should compounds be created? Will use the compound (any/all) version
21 | # of the arel_predicate to create a corresponding any/all version of
22 | # your predicate. (Default: true)
23 | compounds: true,
24 | # Force a specific column type for type-casting of supplied values.
25 | # (Default: use type from DB column)
26 | type: :string,
27 | # Use LOWER(column on database).
28 | # (Default: false)
29 | case_insensitive: true
30 | end
31 | ```
32 | You can check all Arel predicates [here](https://github.com/rails/rails/blob/main/activerecord/lib/arel/predications.rb).
33 |
34 | If Arel does not have the predicate you are looking for, consider monkey patching it:
35 |
36 | ```ruby
37 | # config/initializers/ransack.rb
38 |
39 | module Arel
40 | module Predications
41 | def gteq_or_null(other)
42 | left = gteq(other)
43 | right = eq(nil)
44 | left.or(right)
45 | end
46 | end
47 | end
48 |
49 | Ransack.configure do |config|
50 | config.add_predicate 'gteq_or_null', arel_predicate: 'gteq_or_null'
51 | end
52 | ```
53 |
--------------------------------------------------------------------------------
/docs/docs/going-further/documentation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 11
3 | title: Documentation
4 | ---
5 |
6 | Ransack uses [Docusaurus](https://docusaurus.io/) for documentation. To contribute to the docs simply use the "Edit this page" link from any page to directly edit, or else pull the repo and edit locally.
7 |
8 | ### Local Development
9 |
10 | Switch to docs folder
11 |
12 | ```
13 | cd docs
14 | ```
15 |
16 | Install docusaurus and other dependencies
17 |
18 | ```
19 | yarn install
20 | ```
21 |
22 |
23 | Start a local development server and open up a browser window. Most changes are reflected live without having to restart the server.
24 |
25 | ```
26 | yarn start
27 | ```
28 |
29 | ### Build
30 |
31 | ```
32 | yarn build
33 | ```
34 |
35 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
36 |
37 | ### Deployment
38 |
39 | Using SSH:
40 |
41 | ```
42 | USE_SSH=true yarn deploy
43 | ```
44 |
--------------------------------------------------------------------------------
/docs/docs/going-further/exporting-to-csv.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: CSV Export
4 | ---
5 |
6 | Exporting to CSV
7 |
8 | Example downloading a csv file preserving ransack search, based on [this gist](https://gist.github.com/pama/adff25ed1f4b796ce088ea362a08e1c5)
9 |
10 | ```ruby title='index.html.erb'
11 | Users
12 |
13 | <%= search_form_for @q, url: dashboard_index_path do |f| %>
14 | <%= f.label :name_cont %>
15 | <%= f.search_field :name_cont %>
16 |
17 | <%= f.submit %>
18 | <% end %>
19 |
20 |
21 | <% @users.each do |user| %>
22 | <%= user.name %> [<%= user.devices.map {|device| device.name }.join(', ') %>]
23 | <% end %>
24 |
25 |
26 | <% if params[:q] %>
27 | <%= link_to 'Export 1', dashboard_index_path({name: params[:q][:name_cont]}.merge({format: :csv})) %>
28 | <% else %>
29 | <%= link_to 'Export 2', dashboard_index_path(format: 'csv') %>
30 | <% end %>
31 | ```
32 |
33 | ```ruby title='user.rb'
34 | require 'csv'
35 |
36 | class User < ApplicationRecord
37 | has_many :devices
38 |
39 | def self.get_csv(users)
40 | CSV.generate do |csv|
41 | csv << ["Name", "Devices"]
42 |
43 | users.each do |user|
44 | csv << [user.name, user.devices.map{|device| device.name}.join(', ')]
45 | end
46 | end
47 | end
48 | end
49 | ```
50 |
--------------------------------------------------------------------------------
/docs/docs/going-further/external-guides.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 9
3 | title: External resources
4 | ---
5 |
6 | There is a plethora of material on Ransack around the internet. We've collected some here for your convenience.
7 |
8 | Do you want to have a blog post or other content on Ransack highlighted? Please just edit the page, add your content and a Pull Request will be sent to Ransack maintainers for approval.
9 |
10 | # Screencasts
11 |
12 | - [DriftingRuby: Ransack Search and Hotwire](https://www.driftingruby.com/episodes/ransack-search-and-hotwire)
13 | - [GoRails: Forum Series Part 6: Search with Ransack](https://gorails.com/episodes/forum-search-with-ransack)
14 | - [Railscast 370 - Ransack](http://railscasts.com/episodes/370-ransack)
15 | - [Search And Sort Ransack Associations With The Rails Ransack Gem | Ruby On Rails 6 Ransack Tutorial](https://www.youtube.com/watch?v=rtg-5EXwpbg)
16 |
17 |
18 | # Gems
19 |
20 | - [ActiveAdmin](https://activeadmin.info/) The Administration Framework for Rails **_uses Ransack internally_**
21 | - [Ransack Memory](https://github.com/richardrails/ransack_memory) Automatically save and load Ransack's filtered params into the Rail's session
22 | - [Mobility Ransack](https://github.com/shioyama/mobility-ransack) Search attributes translated by Mobility with Ransack.
23 | - [Ransack UI](https://github.com/ndbroadbent/ransack_ui) Framework for building a search UI with Ransack **_seems abandoned_**
24 |
25 | # Blogs
26 |
27 | - [Search And Sort In Ruby On Rails 6 With The Ransack Gem](https://deanin.com/blog/ransack/)
28 | - [Implement Ransack Gem in Ruby on Rails](https://www.botreetechnologies.com/blog/implementing-advanced-search-in-ruby-on-rails-with-ransack/)
29 | - [Searching and Sorting with Ransack](https://jaspercurry.medium.com/searching-and-sorting-on-rails-with-ransack-560e862e650a)
30 | - [How to Build Your Own ActiveAdmin Filters with Ransack](https://www.viget.com/articles/how-to-build-your-own-filters-with-ransack/)
31 | - [Avoid Ransack's N+1 Pitfall!](https://dev.to/husteadrobert/avoid-ransacks-n1-pitfall-33of)
32 | - [Filter and paging with Kaminari](https://gist.github.com/MyklClason/e4dc96fd0e009b7b3a9c84ddbb1e11d2)
33 | - [Pagination for Ransack Forms](https://nicholaide.github.io/ransack/2016/11/26/ransack-pagination.html)
34 | - [AJAX Search, Sort, Paginate with Ransack and Kaminari](https://techbrownbags.wordpress.com/2014/01/17/rails-ajax-search-sort-paginate-with-ransack-kaminari/)
35 | - [Searching with Ransack in Ruby on Rails](http://blog.magmalabs.io/2019/03/12/searching-with-ransack-in-ruby-on-rails.html)
36 | - [Role scopes with gem Ransack](https://blog.corsego.com/rolify-scopes)
37 | - [Searching and Sorting with Ransack](https://www.mintbit.com/blog/searching-and-sorting-with-ransack)
38 | - [Using custom scopes with Ransack gem in Rails](https://profilehunt.net/blog/using-custom-scopes-with-ransack-in-rails)
39 | - [Query Date Range With Ransack](https://lingceng.github.io/blog/2015/12/28/query-date-range-with-ransack/)
40 | - [ransack vs searchkick: Building a search feature in Rails](https://www.cookieshq.co.uk/posts/ransack-vs-searchkick-building-a-search-feature-in-rails)
41 | - [Using ransack and delegate in Rails](https://huangwenwei.com/blogs/using-ransack-and-delegate-in-rails)
42 | - [Using Ransack as a Search Engine](https://medium.com/@jelaniwoods/using-ransack-as-a-search-engine-92e002a68da)
43 | - [Advanced Search with Ransack](https://www.sitepoint.com/advanced-search-ransack/)
44 | - [Sort a table of records in Rails with Ransack](https://alankydd.wordpress.com/2012/03/12/sort-a-table-of-records-in-rails-with-ransack/)
45 | - [Ransack: Search with Multiple Checkboxes (Rails)](https://iamjosh.wordpress.com/2014/03/07/ransack-search-with-multiple-checkboxes/)
46 | - [Rails : Ransack : Sorting data by ratings](https://cbabhusal.wordpress.com/2017/01/03/rails-ransack-sorting-data-by-ratings/)
47 | - [Setting Up Rails 5 API Only App with ActiveAdmin enabled](https://rrott.com/blog/ror/rails-5-api-with-activeadmin-integration/)
48 | - [Ransack, the library formerly known as MetaSearch 2.0](https://ernie.io/2011/04/01/ransack-the-library-formerly-known-as-metasearch-2-0/) **_some Ransack history_**
49 |
50 | ## In French
51 |
52 | - [Faciliter les recherches avec Ransack](https://www.synbioz.com/blog/tech/faciliter-les-recherches-avec-ransack)
53 |
54 | ## In Vietnamese
55 |
56 | - [Ransack - công cụ tuyệt vời giúp tìm kiếm và sắp xếp dữ liệu đơn giản hơn
57 | ](https://nddblog.com/posts/ransack-cong-cu-tuyet-voi-giup-tim-kiem-va-sap-xep-du-lieu-don-gian-hon)
58 |
--------------------------------------------------------------------------------
/docs/docs/going-further/form-customisation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | title: Form customisation
4 | ---
5 |
6 | Predicate and attribute labels in forms may be specified with I18n in a translation file (see the locale files in [Ransack::Locale](https://github.com/activerecord-hackery/ransack/tree/main/lib/ransack/locale) for more examples):
7 |
8 | ```yml
9 | # locales/en.yml
10 | en:
11 | ransack:
12 | asc: ascending
13 | desc: descending
14 | predicates:
15 | cont: contains
16 | not_cont: not contains
17 | start: starts with
18 | end: ends with
19 | gt: greater than
20 | lt: less than
21 | attributes:
22 | person:
23 | name: Full Name
24 | article:
25 | title: Article Title
26 | body: Main Content
27 | ```
28 | The names of attribute fields may also be changed globally or under activerecord:
29 |
30 | ```yml
31 | # locales/en.yml
32 | en:
33 | attributes:
34 | model_name:
35 | model_field1: field name1
36 | model_field2: field name2
37 | activerecord:
38 | attributes:
39 | namespace/article:
40 | title: AR Namespaced Title
41 | namespace_article:
42 | title: Old Ransack Namespaced Title
43 | ```
44 |
45 | To limit the predicates in the `predicate_select` form helper in a view template, pass an array of permitted predicates with `only`:
46 |
47 | ```erb
48 | <%= f.predicate_select only: %i(cont not_cont eq not_eq blank null) %>
49 | ```
50 |
51 | Compound predicates (`_any` & `_all`) may be removed by passing the option `compounds: false`.
52 |
53 | ```erb
54 | <%= f.predicate_select compounds: false %>
55 | ```
56 |
57 | Searchable attributes versus non-searchable ones may be specified as follows:
58 |
59 | ```ruby
60 | def self.ransackable_attributes(auth_object = nil)
61 | %w(searchable_attribute_1 searchable_attribute_2 ...) + _ransackers.keys
62 | end
63 | ```
64 |
--------------------------------------------------------------------------------
/docs/docs/going-further/i18n.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | title: i18n
4 | ---
5 |
6 | # i18n and Ransack
7 |
8 | Ransack translation files are available in
9 | [Ransack::Locale](https://github.com/activerecord-hackery/ransack/tree/main/lib/ransack/locale). You may also be interested in one of the
10 | many translations for Ransack available at
11 | http://www.localeapp.com/projects/2999.
12 |
13 | Predicate and attribute translations in forms may be specified as follows (see
14 | the translation files in [Ransack::Locale](https://github.com/activerecord-hackery/ransack/tree/main/lib/ransack/locale) for more examples):
15 |
16 | locales/en.yml:
17 | ```yml
18 | en:
19 | ransack:
20 | asc: ascending
21 | desc: descending
22 | predicates:
23 | cont: contains
24 | not_cont: not contains
25 | start: starts with
26 | end: ends with
27 | gt: greater than
28 | lt: less than
29 | models:
30 | person: Passenger
31 | attributes:
32 | person:
33 | name: Full Name
34 | article:
35 | title: Article Title
36 | body: Main Content
37 | ```
38 |
39 | Attribute names may also be changed globally, or under `activerecord`:
40 |
41 | ```yml
42 | en:
43 | attributes:
44 | model_name:
45 | model_field1: field name1
46 | model_field2: field name2
47 | activerecord:
48 | attributes:
49 | namespace/article:
50 | title: AR Namespaced Title
51 | namespace_article:
52 | title: Old Ransack Namespaced Title
53 | ```
54 |
--------------------------------------------------------------------------------
/docs/docs/going-further/img/create_release.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/docs/going-further/img/create_release.png
--------------------------------------------------------------------------------
/docs/docs/going-further/merging-searches.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 5
3 | title: Merging searches
4 | ---
5 |
6 | To find records that match multiple searches, it's possible to merge all the ransack search conditions into an ActiveRecord relation to perform a single query. In order to avoid conflicts between joined table names it's necessary to set up a shared context to track table aliases used across all the conditions before initializing the searches:
7 |
8 | ```ruby
9 | shared_context = Ransack::Context.for(Person)
10 |
11 | search_parents = Person.ransack(
12 | { parent_name_eq: "A" }, context: shared_context
13 | )
14 |
15 | search_children = Person.ransack(
16 | { children_name_eq: "B" }, context: shared_context
17 | )
18 |
19 | shared_conditions = [search_parents, search_children].map { |search|
20 | Ransack::Visitor.new.accept(search.base)
21 | }
22 |
23 | Person.joins(shared_context.join_sources)
24 | .where(shared_conditions.reduce(&:or))
25 | .to_sql
26 | ```
27 | Produces:
28 | ```sql
29 | SELECT "people".*
30 | FROM "people"
31 | LEFT OUTER JOIN "people" "parents_people"
32 | ON "parents_people"."id" = "people"."parent_id"
33 | LEFT OUTER JOIN "people" "children_people"
34 | ON "children_people"."parent_id" = "people"."id"
35 | WHERE (
36 | ("parents_people"."name" = 'A' OR "children_people"."name" = 'B')
37 | )
38 | ORDER BY "people"."id" DESC
39 | ```
40 |
41 | Admittedly this is not as simple as it should be, but it's workable for now. (Implementing [issue 417](https://github.com/activerecord-hackery/ransack/issues/417) could make this more straightforward.)
42 |
--------------------------------------------------------------------------------
/docs/docs/going-further/polymorphic-search.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Polymorphic Searches
3 | sidebar_position: 14
4 | ---
5 |
6 | When making searches from polymorphic models it is necessary to specify the type of model you are searching.
7 |
8 | For example:
9 |
10 | Given two models
11 |
12 | ```ruby
13 | class House < ActiveRecord::Base
14 | has_one :location, as: :locatable
15 | end
16 |
17 | class Location < ActiveRecord::Base
18 | belongs_to :locatable, polymorphic: true
19 | end
20 | ```
21 |
22 | Normally (without polymorphic relationship) you would be able to search as per below:
23 |
24 | ```ruby
25 | Location.ransack(locatable_number_eq: 100).result
26 | ```
27 |
28 | However when this is searched you will get the following error
29 |
30 | ```ruby
31 | ActiveRecord::EagerLoadPolymorphicError: Can not eagerly load the polymorphic association :locatable
32 | ```
33 |
34 | In order to search for locations by house number when the relationship is polymorphic you have to specify the type of records you will be searching and construct your search as below:
35 |
36 | ```ruby
37 | Location.ransack(locatable_of_House_type_number_eq: 100).result
38 | ```
39 |
40 | note the `_of_House_type_` added to the search key. This allows Ransack to correctly specify the table names in SQL join queries.
41 |
42 | For namespaced models you should use a quoted string containing the standard Ruby module notation
43 |
44 | ```ruby
45 | Location.ransack('locatable_of_Residences::House_type_number_eq' => 100).result
46 | ```
47 |
--------------------------------------------------------------------------------
/docs/docs/going-further/release_process.md:
--------------------------------------------------------------------------------
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 | 
37 |
--------------------------------------------------------------------------------
/docs/docs/going-further/saving-queries.md:
--------------------------------------------------------------------------------
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/)
83 |
--------------------------------------------------------------------------------
/docs/docs/going-further/searching-postgres.md:
--------------------------------------------------------------------------------
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"}')`
58 |
--------------------------------------------------------------------------------
/docs/docs/going-further/wiki-contributors.md:
--------------------------------------------------------------------------------
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
83 |
--------------------------------------------------------------------------------
/docs/docs/intro.md:
--------------------------------------------------------------------------------
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.
77 |
78 |
79 | ## Backers
80 |
81 | Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/ransack#backer)]
82 |
83 |
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 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/docs/docusaurus.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Note: type annotations allow type checking and IDEs autocompletion
3 |
4 | const lightCodeTheme = require('prism-react-renderer/themes/github');
5 | const darkCodeTheme = require('prism-react-renderer/themes/dracula');
6 |
7 | /** @type {import('@docusaurus/types').Config} */
8 | const config = {
9 | title: 'Ransack documentation',
10 | tagline: 'Object-based searching',
11 | url: 'https://activerecord-hackery.github.io',
12 | baseUrl: '/ransack/',
13 | //onBrokenLinks: 'throw',
14 | onBrokenMarkdownLinks: 'warn',
15 | favicon: 'img/favicon.ico',
16 | organizationName: 'activerecord-hackery',
17 | projectName: 'ransack',
18 | trailingSlash: true,
19 |
20 | presets: [
21 | [
22 | 'classic',
23 | /** @type {import('@docusaurus/preset-classic').Options} */
24 | ({
25 | docs: {
26 | routeBasePath: '/',
27 | sidebarPath: require.resolve('./sidebars.js'),
28 | editUrl: 'https://github.com/activerecord-hackery/ransack/edit/main/docs/',
29 | },
30 | blog: {
31 | showReadingTime: true,
32 | editUrl: 'https://github.com/activerecord-hackery/ransack/edit/main/blog/',
33 | },
34 | theme: {
35 | customCss: require.resolve('./src/css/custom.css'),
36 | },
37 | }),
38 | ],
39 | ],
40 |
41 | themeConfig:
42 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */
43 | ({
44 | navbar: {
45 | logo: {
46 | alt: 'Ransack Logo',
47 | src: './logo/ransack-h.png',
48 | },
49 | items: [
50 | {
51 | type: 'doc',
52 | docId: 'intro',
53 | position: 'left',
54 | label: 'Documentation',
55 | },
56 | {to: '/blog', label: 'Blog', position: 'left'},
57 | {
58 | href: 'https://github.com/activerecord-hackery/ransack',
59 | label: 'GitHub',
60 | position: 'right',
61 | },
62 | ],
63 | },
64 | footer: {
65 | style: 'dark',
66 | links: [
67 | {
68 | title: 'Docs',
69 | items: [
70 | {
71 | label: 'Documentation',
72 | to: '/',
73 | },
74 | ],
75 | },
76 | {
77 | title: 'Community',
78 | items: [
79 | {
80 | label: 'Stack Overflow',
81 | href: 'https://stackoverflow.com/questions/tagged/ransack',
82 | },
83 | ],
84 | },
85 | {
86 | title: 'More',
87 | items: [
88 | {
89 | label: 'Blog',
90 | to: '/blog',
91 | },
92 | {
93 | label: 'GitHub',
94 | href: 'https://github.com/activerecord-hackery/ransack',
95 | },
96 | ],
97 | },
98 | ],
99 | },
100 | prism: {
101 | theme: lightCodeTheme,
102 | darkTheme: darkCodeTheme,
103 | additionalLanguages: ['ruby', 'erb'],
104 | },
105 | }),
106 |
107 | themes: [
108 | [
109 | require.resolve("@easyops-cn/docusaurus-search-local"),
110 | {
111 | // `hashed` is recommended as long-term-cache of index file is possible.
112 | hashed: true,
113 | // needs to be the same as routeBasePath in @docusaurus/preset-classic config
114 | docsRouteBasePath: '/'
115 | },
116 | ]
117 | ]
118 | };
119 |
120 | module.exports = config;
121 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs-website",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle",
10 | "deploy": "docusaurus deploy",
11 | "clear": "docusaurus clear",
12 | "serve": "docusaurus serve",
13 | "write-translations": "docusaurus write-translations",
14 | "write-heading-ids": "docusaurus write-heading-ids"
15 | },
16 | "dependencies": {
17 | "@docusaurus/core": "^2.2.0",
18 | "@docusaurus/preset-classic": "^2.2.0",
19 | "@easyops-cn/docusaurus-search-local": "^0.33.5",
20 | "@mdx-js/react": "^1.6.22",
21 | "clsx": "^1.1.1",
22 | "prism-react-renderer": "^1.3.1",
23 | "react": "^17.0.2",
24 | "react-dom": "^17.0.2"
25 | },
26 | "resolutions": {
27 | "trim": "^0.0.3",
28 | "got": "^11.8.5"
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.5%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/docs/sidebars.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Creating a sidebar enables you to:
3 | - create an ordered group of docs
4 | - render a sidebar for each doc of that group
5 | - provide next/previous navigation
6 |
7 | The sidebars can be generated from the filesystem, or explicitly defined here.
8 |
9 | Create as many sidebars as you want.
10 | */
11 |
12 | // @ts-check
13 |
14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
15 | const sidebars = {
16 | // By default, Docusaurus generates a sidebar from the docs folder structure
17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
18 |
19 | // But you can create a sidebar manually
20 | /*
21 | tutorialSidebar: [
22 | {
23 | type: 'category',
24 | label: 'Tutorial',
25 | items: ['hello'],
26 | },
27 | ],
28 | */
29 | };
30 |
31 | module.exports = sidebars;
32 |
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import styles from './styles.module.css';
4 |
5 | const FeatureList = [
6 | {
7 | title: 'Easy to Use',
8 | Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
9 | description: (
10 | <>
11 | Docusaurus was designed from the ground up to be easily installed and
12 | used to get your website up and running quickly.
13 | >
14 | ),
15 | },
16 | {
17 | title: 'Focus on What Matters',
18 | Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
19 | description: (
20 | <>
21 | Docusaurus lets you focus on your docs, and we'll do the chores. Go
22 | ahead and move your docs into the docs
directory.
23 | >
24 | ),
25 | },
26 | {
27 | title: 'Powered by React',
28 | Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
29 | description: (
30 | <>
31 | Extend or customize your website layout by reusing React. Docusaurus can
32 | be extended while reusing the same header and footer.
33 | >
34 | ),
35 | },
36 | ];
37 |
38 | function Feature({Svg, title, description}) {
39 | return (
40 |
41 |
42 |
43 |
44 |
45 |
{title}
46 |
{description}
47 |
48 |
49 | );
50 | }
51 |
52 | export default function HomepageFeatures() {
53 | return (
54 |
55 |
56 |
57 | {FeatureList.map((props, idx) => (
58 |
59 | ))}
60 |
61 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures/styles.module.css:
--------------------------------------------------------------------------------
1 | .features {
2 | display: flex;
3 | align-items: center;
4 | padding: 2rem 0;
5 | width: 100%;
6 | }
7 |
8 | .featureSvg {
9 | height: 200px;
10 | width: 200px;
11 | }
12 |
--------------------------------------------------------------------------------
/docs/src/css/custom.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Any CSS included here will be global. The classic template
3 | * bundles Infima by default. Infima is a CSS framework designed to
4 | * work well for content-centric websites.
5 | */
6 |
7 | /* You can override the default Infima variables here. */
8 | :root {
9 | --ifm-color-primary: #2e8555;
10 | --ifm-color-primary-dark: #29784c;
11 | --ifm-color-primary-darker: #277148;
12 | --ifm-color-primary-darkest: #205d3b;
13 | --ifm-color-primary-light: #33925d;
14 | --ifm-color-primary-lighter: #359962;
15 | --ifm-color-primary-lightest: #3cad6e;
16 | --ifm-code-font-size: 95%;
17 | }
18 |
19 | /* For readability concerns, you should choose a lighter palette in dark mode. */
20 | [data-theme='dark'] {
21 | --ifm-color-primary: #25c2a0;
22 | --ifm-color-primary-dark: #21af90;
23 | --ifm-color-primary-darker: #1fa588;
24 | --ifm-color-primary-darkest: #1a8870;
25 | --ifm-color-primary-light: #29d5b0;
26 | --ifm-color-primary-lighter: #32d8b4;
27 | --ifm-color-primary-lightest: #4fddbf;
28 | }
29 |
30 | .docusaurus-highlight-code-line {
31 | background-color: rgba(0, 0, 0, 0.1);
32 | display: block;
33 | margin: 0 calc(-1 * var(--ifm-pre-padding));
34 | padding: 0 var(--ifm-pre-padding);
35 | }
36 |
37 | [data-theme='dark'] .docusaurus-highlight-code-line {
38 | background-color: rgba(0, 0, 0, 0.3);
39 | }
40 |
--------------------------------------------------------------------------------
/docs/src/pages/index.module.css:
--------------------------------------------------------------------------------
1 | /**
2 | * CSS files with the .module.css suffix will be treated as CSS modules
3 | * and scoped locally.
4 | */
5 |
6 | .heroBanner {
7 | padding: 4rem 0;
8 | text-align: center;
9 | position: relative;
10 | overflow: hidden;
11 | }
12 |
13 | @media screen and (max-width: 996px) {
14 | .heroBanner {
15 | padding: 2rem;
16 | }
17 | }
18 |
19 | .buttons {
20 | display: flex;
21 | align-items: center;
22 | justify-content: center;
23 | }
24 |
--------------------------------------------------------------------------------
/docs/src/pages/markdown-page.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Markdown page example
3 | ---
4 |
5 | # Markdown page example
6 |
7 | You don't need React to write simple standalone pages.
8 |
--------------------------------------------------------------------------------
/docs/static/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/static/.nojekyll
--------------------------------------------------------------------------------
/docs/static/img/docusaurus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/static/img/docusaurus.png
--------------------------------------------------------------------------------
/docs/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/static/img/favicon.ico
--------------------------------------------------------------------------------
/docs/static/img/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/static/img/tutorial/docsVersionDropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/static/img/tutorial/docsVersionDropdown.png
--------------------------------------------------------------------------------
/docs/static/img/tutorial/localeDropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/static/img/tutorial/localeDropdown.png
--------------------------------------------------------------------------------
/docs/static/logo/ransack-h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/static/logo/ransack-h.png
--------------------------------------------------------------------------------
/docs/static/logo/ransack-v.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/static/logo/ransack-v.png
--------------------------------------------------------------------------------
/docs/static/logo/ransack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activerecord-hackery/ransack/e06a27ded233166362fdd9e6ca3996cecbef8265/docs/static/logo/ransack.png
--------------------------------------------------------------------------------
/docs/static/logo/ransack.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/lib/polyamorous/activerecord/join_association.rb:
--------------------------------------------------------------------------------
1 | module Polyamorous
2 | module JoinAssociationExtensions
3 | include SwappingReflectionClass
4 | def self.prepended(base)
5 | base.class_eval { attr_reader :join_type }
6 | end
7 |
8 | def initialize(reflection, children, polymorphic_class = nil, join_type = Arel::Nodes::InnerJoin)
9 | @join_type = join_type
10 | if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
11 | swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
12 | super(reflection, children)
13 | self.reflection.options[:polymorphic] = true
14 | end
15 | else
16 | super(reflection, children)
17 | end
18 | end
19 |
20 | # Same as #join_constraints, but instead of constructing tables from the
21 | # given block, uses the ones passed
22 | def join_constraints_with_tables(foreign_table, foreign_klass, join_type, alias_tracker, tables)
23 | joins = []
24 | chain = []
25 |
26 | reflection.chain.each.with_index do |reflection, i|
27 | table = tables[i]
28 |
29 | @table ||= table
30 | chain << [reflection, table]
31 | end
32 |
33 | # The chain starts with the target table, but we want to end with it here (makes
34 | # more sense in this context), so we reverse
35 | chain.reverse_each do |reflection, table|
36 | klass = reflection.klass
37 |
38 | join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
39 |
40 | unless join_scope.references_values.empty?
41 | join_dependency = join_scope.construct_join_dependency(
42 | join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin
43 | )
44 | join_scope.joins!(join_dependency)
45 | end
46 |
47 | arel = join_scope.arel(alias_tracker.aliases)
48 | nodes = arel.constraints.first
49 |
50 | if nodes.is_a?(Arel::Nodes::And)
51 | others = nodes.children.extract! do |node|
52 | !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
53 | end
54 | end
55 |
56 | joins << table.create_join(table, table.create_on(nodes), join_type)
57 |
58 | if others && !others.empty?
59 | joins.concat arel.join_sources
60 | append_constraints(joins.last, others)
61 | end
62 |
63 | # The current table in this iteration becomes the foreign table in the next
64 | foreign_table, foreign_klass = table, klass
65 | end
66 |
67 | joins
68 | end
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/lib/polyamorous/activerecord/join_association_7_2.rb:
--------------------------------------------------------------------------------
1 | module Polyamorous
2 | module JoinAssociationExtensions
3 | # Same as #join_constraints, but instead of constructing tables from the
4 | # given block, uses the ones passed
5 | def join_constraints_with_tables(foreign_table, foreign_klass, join_type, alias_tracker, tables)
6 | joins = []
7 | chain = []
8 |
9 | reflection.chain.each.with_index do |reflection, i|
10 | table = tables[i]
11 |
12 | @table ||= table
13 | chain << [reflection, table]
14 | end
15 |
16 | base_klass.with_connection do |connection|
17 | # The chain starts with the target table, but we want to end with it here (makes
18 | # more sense in this context), so we reverse
19 | chain.reverse_each do |reflection, table|
20 | klass = reflection.klass
21 |
22 | join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
23 |
24 | unless join_scope.references_values.empty?
25 | join_dependency = join_scope.construct_join_dependency(
26 | join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin
27 | )
28 | join_scope.joins!(join_dependency)
29 | end
30 |
31 | arel = join_scope.arel(alias_tracker.aliases)
32 | nodes = arel.constraints.first
33 |
34 | if nodes.is_a?(Arel::Nodes::And)
35 | others = nodes.children.extract! do |node|
36 | !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
37 | end
38 | end
39 |
40 | joins << table.create_join(table, table.create_on(nodes), join_type)
41 |
42 | if others && !others.empty?
43 | joins.concat arel.join_sources
44 | append_constraints(connection, joins.last, others)
45 | end
46 |
47 | # The current table in this iteration becomes the foreign table in the next
48 | foreign_table, foreign_klass = table, klass
49 | end
50 |
51 | joins
52 | end
53 | end
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/lib/polyamorous/activerecord/join_dependency.rb:
--------------------------------------------------------------------------------
1 | module Polyamorous
2 | module JoinDependencyExtensions
3 | # Replaces ActiveRecord::Associations::JoinDependency#build
4 | def build(associations, base_klass)
5 | associations.map do |name, right|
6 | if name.is_a? Join
7 | reflection = find_reflection base_klass, name.name
8 | reflection.check_validity!
9 | reflection.check_eager_loadable!
10 |
11 | klass = if reflection.polymorphic?
12 | name.klass || base_klass
13 | else
14 | reflection.klass
15 | end
16 | JoinAssociation.new(reflection, build(right, klass), name.klass, name.type)
17 | else
18 | reflection = find_reflection base_klass, name
19 | reflection.check_validity!
20 | reflection.check_eager_loadable!
21 |
22 | if reflection.polymorphic?
23 | raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
24 | end
25 | JoinAssociation.new(reflection, build(right, reflection.klass))
26 | end
27 | end
28 | end
29 |
30 | def join_constraints(joins_to_add, alias_tracker, references)
31 | @alias_tracker = alias_tracker
32 | @joined_tables = {}
33 | @references = {}
34 |
35 | references.each do |table_name|
36 | @references[table_name.to_sym] = table_name if table_name.is_a?(String)
37 | end
38 |
39 | joins = make_join_constraints(join_root, join_type)
40 |
41 | joins.concat joins_to_add.flat_map { |oj|
42 | if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
43 | walk join_root, oj.join_root, oj.join_type
44 | else
45 | make_join_constraints(oj.join_root, oj.join_type)
46 | end
47 | }
48 | end
49 |
50 | def construct_tables_for_association!(join_root, association)
51 | tables = table_aliases_for(join_root, association)
52 | association.table = tables.first
53 | tables
54 | end
55 |
56 | private
57 |
58 | def table_aliases_for(parent, node)
59 | @joined_tables ||= {}
60 | node.reflection.chain.map { |reflection|
61 | table, terminated = @joined_tables[reflection]
62 | root = reflection == node.reflection
63 |
64 | if table && (!root || !terminated)
65 | @joined_tables[reflection] = [table, true] if root
66 | table
67 | else
68 | table = alias_tracker.aliased_table_for(reflection.klass.arel_table) do
69 | name = reflection.alias_candidate(parent.table_name)
70 | root ? name : "#{name}_join"
71 | end
72 | @joined_tables[reflection] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
73 | table
74 | end
75 | }
76 | end
77 |
78 | module ClassMethods
79 | # Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
80 | #
81 | def walk_tree(associations, hash)
82 | case associations
83 | when TreeNode
84 | associations.add_to_tree(hash)
85 | when Hash
86 | associations.each do |k, v|
87 | cache =
88 | if TreeNode === k
89 | k.add_to_tree(hash)
90 | else
91 | hash[k] ||= {}
92 | end
93 | walk_tree(v, cache)
94 | end
95 | else
96 | super(associations, hash)
97 | end
98 | end
99 | end
100 |
101 | end
102 | end
103 |
--------------------------------------------------------------------------------
/lib/polyamorous/activerecord/reflection.rb:
--------------------------------------------------------------------------------
1 | module Polyamorous
2 | module ReflectionExtensions
3 | def join_scope(table, foreign_table, foreign_klass)
4 | if respond_to?(:polymorphic?) && polymorphic?
5 | super.where!(foreign_table[foreign_type].eq(klass.name))
6 | else
7 | super
8 | end
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/polyamorous/join.rb:
--------------------------------------------------------------------------------
1 | module Polyamorous
2 | class Join
3 | include TreeNode
4 |
5 | attr_accessor :name
6 | attr_reader :type, :klass
7 |
8 | def initialize(name, type = InnerJoin, klass = nil)
9 | @name = name
10 | @type = convert_to_arel_join_type(type)
11 | @klass = convert_to_class(klass) if klass
12 | end
13 |
14 | def klass=(klass)
15 | @klass = convert_to_class(klass) if klass
16 | end
17 |
18 | def type=(type)
19 | @type = convert_to_arel_join_type(type) if type
20 | end
21 |
22 | def hash
23 | [@name, @type, @klass].hash
24 | end
25 |
26 | def eql?(other)
27 | self.class == other.class &&
28 | self.name == other.name &&
29 | self.type == other.type &&
30 | self.klass == other.klass
31 | end
32 |
33 | alias :== :eql?
34 |
35 | def add_to_tree(hash)
36 | hash[self] ||= {}
37 | end
38 |
39 | private
40 |
41 | def convert_to_arel_join_type(type)
42 | case type
43 | when 'inner', :inner
44 | InnerJoin
45 | when 'outer', :outer
46 | OuterJoin
47 | when Class
48 | if [InnerJoin, OuterJoin].include? type
49 | type
50 | else
51 | raise ArgumentError, "#{type} cannot be converted to an ARel join type"
52 | end
53 | else
54 | raise ArgumentError, "#{type} cannot be converted to an ARel join type"
55 | end
56 | end
57 |
58 | def convert_to_class(value)
59 | case value
60 | when String, Symbol
61 | Kernel.const_get(value)
62 | when Class
63 | value
64 | else
65 | raise ArgumentError, "#{value} cannot be converted to a Class"
66 | end
67 | end
68 |
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/lib/polyamorous/polyamorous.rb:
--------------------------------------------------------------------------------
1 | ActiveSupport.on_load(:active_record) do
2 | module Polyamorous
3 | InnerJoin = Arel::Nodes::InnerJoin
4 | OuterJoin = Arel::Nodes::OuterJoin
5 |
6 | JoinDependency = ::ActiveRecord::Associations::JoinDependency
7 | JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
8 | end
9 |
10 | require 'polyamorous/tree_node'
11 | require 'polyamorous/join'
12 | require 'polyamorous/swapping_reflection_class'
13 |
14 | require 'polyamorous/activerecord/join_association'
15 | require 'polyamorous/activerecord/join_dependency'
16 | require 'polyamorous/activerecord/reflection'
17 |
18 | if ::ActiveRecord.version >= ::Gem::Version.new("7.2") && ::ActiveRecord.version < ::Gem::Version.new("7.2.2.1")
19 | require "polyamorous/activerecord/join_association_7_2"
20 | end
21 |
22 | ActiveRecord::Reflection::AbstractReflection.send(:prepend, Polyamorous::ReflectionExtensions)
23 |
24 | Polyamorous::JoinDependency.send(:prepend, Polyamorous::JoinDependencyExtensions)
25 | Polyamorous::JoinDependency.singleton_class.send(:prepend, Polyamorous::JoinDependencyExtensions::ClassMethods)
26 | Polyamorous::JoinAssociation.send(:prepend, Polyamorous::JoinAssociationExtensions)
27 | end
28 |
--------------------------------------------------------------------------------
/lib/polyamorous/swapping_reflection_class.rb:
--------------------------------------------------------------------------------
1 | module Polyamorous
2 | module SwappingReflectionClass
3 | def swapping_reflection_klass(reflection, klass)
4 | new_reflection = reflection.clone
5 | new_reflection.instance_variable_set(:@options, reflection.options.clone)
6 | new_reflection.options.delete(:polymorphic)
7 | new_reflection.instance_variable_set(:@klass, klass)
8 | yield new_reflection
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/polyamorous/tree_node.rb:
--------------------------------------------------------------------------------
1 | module Polyamorous
2 | module TreeNode
3 | def add_to_tree(hash)
4 | raise NotImplementedError
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/ransack.rb:
--------------------------------------------------------------------------------
1 | require 'active_support/dependencies/autoload'
2 | require 'active_support/deprecation'
3 | require 'active_support/version'
4 |
5 | if ::ActiveSupport.version >= ::Gem::Version.new("7.1")
6 | require 'active_support/deprecator'
7 | end
8 |
9 | require 'active_support/core_ext'
10 | require 'ransack/configuration'
11 | require 'polyamorous/polyamorous'
12 |
13 | module Ransack
14 | extend Configuration
15 | class UntraversableAssociationError < StandardError; end
16 | end
17 |
18 | Ransack.configure do |config|
19 | Ransack::Constants::AREL_PREDICATES.each do |name|
20 | config.add_predicate name, arel_predicate: name
21 | end
22 | Ransack::Constants::DERIVED_PREDICATES.each do |args|
23 | config.add_predicate(*args)
24 | end
25 | end
26 |
27 | require 'ransack/search'
28 | require 'ransack/ransacker'
29 | require 'ransack/translate'
30 | require 'ransack/active_record'
31 | require 'ransack/context'
32 | require 'ransack/version'
33 |
34 | ActiveSupport.on_load(:action_controller) do
35 | require 'ransack/helpers'
36 | ActionController::Base.helper Ransack::Helpers::FormHelper
37 | end
38 |
--------------------------------------------------------------------------------
/lib/ransack/active_record.rb:
--------------------------------------------------------------------------------
1 | require 'ransack/adapters/active_record/base'
2 |
3 | ActiveSupport.on_load(:active_record) do
4 | extend Ransack::Adapters::ActiveRecord::Base
5 |
6 | Ransack::SUPPORTS_ATTRIBUTE_ALIAS =
7 | begin
8 | ActiveRecord::Base.respond_to?(:attribute_aliases)
9 | rescue NameError
10 | false
11 | end
12 | end
13 |
14 | require 'ransack/adapters/active_record/context'
15 |
--------------------------------------------------------------------------------
/lib/ransack/helpers.rb:
--------------------------------------------------------------------------------
1 | require 'ransack/helpers/form_builder'
2 | require 'ransack/helpers/form_helper'
3 |
--------------------------------------------------------------------------------
/lib/ransack/invalid_search_error.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | class InvalidSearchError < ArgumentError; end
3 | end
4 |
--------------------------------------------------------------------------------
/lib/ransack/locale/ar.yml:
--------------------------------------------------------------------------------
1 | ar:
2 | ransack:
3 | search: "بحث"
4 | predicate: "فاعل"
5 | and: "و"
6 | or: "أو"
7 | any: "أيُّ"
8 | all: "كل"
9 | combinator: "دالة توافقية"
10 | attribute: "خاصية"
11 | value: "قيمة"
12 | condition: "شرط"
13 | sort: "ترتيب"
14 | asc: "تصاعدي"
15 | desc: "تنازلي"
16 | predicates:
17 | eq: "معادل"
18 | eq_any: "معادل على اﻷقل لواحد"
19 | eq_all: "معادل للجميع"
20 | not_eq: "ليس معادلا لـ"
21 | not_eq_any: "ليس معادلا على اﻷقل لواحد"
22 | not_eq_all: "ليس معادلا للجميع"
23 | matches: "موائم"
24 | matches_any: "موائم لواحد على اﻷقل"
25 | matches_all: "موائم للجميع"
26 | does_not_match: "لا يتواءم"
27 | does_not_match_any: "لا يتواءم مع واحد على اﻷقل"
28 | does_not_match_all: "لا يتواءم مع الجميع"
29 | lt: "أصغر من"
30 | lt_any: "أصغر لواحد على اﻷقل"
31 | lt_all: "أصغر من الجميع"
32 | lteq: "أصغر أو مساو لـ"
33 | lteq_any: "أصغر أو مساو لواحد على اﻷقل"
34 | lteq_all: "أصغر أو مساو للجميع"
35 | gt: "أكبر من"
36 | gt_any: "أكبر من واحد على اﻷقل"
37 | gt_all: "أكبر من الجميع"
38 | gteq: "أكبر أو مساو لـ"
39 | gteq_any: "أكبر أو مساو لواحد على اﻷقل"
40 | gteq_all: "أكبر أو مساو للجميع"
41 | in: "متضمن لـ"
42 | in_any: "متضمن لواحد على اﻷقل"
43 | in_all: "متضمن للجميع"
44 | not_in: "غير متضمن"
45 | not_in_any: "غير متضمن لواحد على اﻷقل"
46 | not_in_all: "غير متضمن للجميع"
47 | cont: "محتو"
48 | cont_any: "محتو لواحد على اﻷقل"
49 | cont_all: "محتو لجميع"
50 | not_cont: "غير محتو"
51 | not_cont_any: "غير محتو لواحد على اﻷقل"
52 | not_cont_all: "غير محتو للجميع"
53 | start: "يبدأ بـ"
54 | start_any: "يبدأ بواحد على اﻷقل"
55 | start_all: "يبدأ بالجميع"
56 | not_start: "لا يبدأ بـ"
57 | not_start_any: "لا يبدأ بواحد على اﻷقل"
58 | not_start_all: "لا يبدأ بالجميع"
59 | end: "ينتهي بـ"
60 | end_any: "ينتهي بواحد على اﻷقل"
61 | end_all: "ينتهي بالجميع"
62 | not_end: "لا ينتهي بـ"
63 | not_end_any: "لا ينتهي بواحد على اﻷقل"
64 | not_end_all: "لا ينتهي بالجميع"
65 | 'true': "صحيح"
66 | 'false': "خطأ"
67 | present: "مستقبل"
68 | blank: "فراغ"
69 | 'null': "عدم"
70 | not_null: "غير مساو لقيمة عدم"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/az.yml:
--------------------------------------------------------------------------------
1 | az:
2 | ransack:
3 | search: "axtar"
4 | predicate: "təsdiqlə"
5 | and: "və"
6 | or: "və ya"
7 | any: "hər hansı"
8 | all: "hamısı"
9 | combinator: "birləşdirici"
10 | attribute: "xüsusiyyət"
11 | value: "dəyər"
12 | condition: "şərt"
13 | sort: "sırala"
14 | asc: "artan"
15 | desc: "azalan"
16 | predicates:
17 | eq: "bərabər"
18 | eq_any: "hər hansı birinə bərabər"
19 | eq_all: "hamısına bərabər"
20 | not_eq: "bərabər deyil"
21 | not_eq_any: "hər hansı birinə bərabər deyil"
22 | not_eq_all: "heç birinə bərabər deyil"
23 | matches: "uyğunlaşan"
24 | matches_any: "hər hansı biri ilə uyğunlaşan"
25 | matches_all: "hamısı ilə uyğunlaşan"
26 | does_not_match: "uyğunlaşmayan"
27 | does_not_match_any: "hər hansı biri ilə uyğunlaşmayan"
28 | does_not_match_all: "heç biri ilə uyğunlaşmayan"
29 | lt: "daha kiçik"
30 | lt_any: "hər hansı birindən kiçik"
31 | lt_all: "hamısından kiçik"
32 | lteq: "daha kiçik və ya bərabər"
33 | lteq_any: "daha kiçik və ya hər hansı birinə bərabər"
34 | lteq_all: "daha kiçik və ya hamısına bərabər"
35 | gt: "daha böyük "
36 | gt_any: "hər hansı birindən daha böyük"
37 | gt_all: "hamısından daha böyük"
38 | gteq: "daha böyük və ya bərabər"
39 | gteq_any: "daha böyük və ya hər hansı birine bərabər"
40 | gteq_all: "daha böyük və ya hamısıne bərabər"
41 | in: "içində"
42 | in_any: "hər hansı birində"
43 | in_all: "hamısında"
44 | not_in: "içində deyil"
45 | not_in_any: "hər hansı birində deyil"
46 | not_in_all: "heç birində deyil"
47 | cont: "ehtiva edən"
48 | cont_any: "hər hansı birini ehtiva edən"
49 | cont_all: "hamısını ehtiva edən"
50 | not_cont: "ehtiva etməyən"
51 | not_cont_any: "hər hansı birini ehtiva etməyən"
52 | not_cont_all: "heç birini birini ehtiva etməyən"
53 | start: "ilə başlayan"
54 | start_any: "hər hansı biri ilə başlayan"
55 | start_all: "hamısı ilə başlayan"
56 | not_start: "ilə başlamayan"
57 | not_start_any: "hər hansı biri ilə başlamayan"
58 | not_start_all: "hiçbiri ilə başlamayan"
59 | end: "ilə bitən"
60 | end_any: "hər hansı biri ilə bitən"
61 | end_all: "hamısı ilə bitən"
62 | not_end: "ilə bitməyən"
63 | not_end_any: "hər hansı biri ilə bitməyən"
64 | not_end_all: "heç biri ilə bitməyən"
65 | 'true': "doğru"
66 | 'false': "yanlış"
67 | present: "mövcüd"
68 | blank: "boş"
69 | 'null': "keçərsiz"
70 | not_null: "keçərli"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/bg.yml:
--------------------------------------------------------------------------------
1 | bg:
2 | ransack:
3 | search: "търсене"
4 | predicate: "предикат"
5 | and: "и"
6 | or: "или"
7 | any: "всякакво"
8 | all: "всички"
9 | combinator: "комбинатор"
10 | attribute: "атрибут"
11 | value: "стойност"
12 | condition: "условие"
13 | sort: "сортиране"
14 | asc: "възходящ"
15 | desc: "низходящ"
16 | predicates:
17 | eq: "се равнява на"
18 | eq_any: "се равнява на всяко"
19 | eq_all: "се равнява на всички"
20 | not_eq: "не е равно на"
21 | not_eq_any: "не е равно на никое от"
22 | not_eq_all: "не е равно на всички"
23 | matches: "съвпада"
24 | matches_any: "съвпада с някое"
25 | matches_all: "съвпада с всички"
26 | does_not_match: "не съвпада"
27 | does_not_match_any: "не съвпада с никое"
28 | does_not_match_all: "не съвпада с всички"
29 | lt: "по-малко от"
30 | lt_any: "по-малко от някое"
31 | lt_all: "по-малко от всички"
32 | lteq: "по-малко или равно на"
33 | lteq_any: "по-малко или равно на някое"
34 | lteq_all: "по-малко или равно на всички"
35 | gt: "по-голямо от"
36 | gt_any: "по-голямо от всякое"
37 | gt_all: "по-голямо от всички"
38 | gteq: "по-голямо или равно на"
39 | gteq_any: "по-голямо или равно на всякое"
40 | gteq_all: "по-голямо или равно на всички"
41 | in: "в"
42 | in_any: "във всякое"
43 | in_all: "във всички"
44 | not_in: "не в"
45 | not_in_any: "не в никое"
46 | not_in_all: "не във всички"
47 | cont: "съдържа"
48 | cont_any: "съдържа всякое"
49 | cont_all: "съдържа всички"
50 | not_cont: "не съдържа"
51 | not_cont_any: "не съдържа никое"
52 | not_cont_all: "не съдържа всички"
53 | start: "започва с"
54 | start_any: "започва с всякое"
55 | start_all: "започва с всички"
56 | not_start: "не започва с"
57 | not_start_any: "не започва с никое"
58 | not_start_all: "не започва с всички"
59 | end: "завършва с"
60 | end_any: "завършва с всякое"
61 | end_all: "завършва с всички"
62 | not_end: "не завършва с"
63 | not_end_any: "не завършва с никое"
64 | not_end_all: "не завършва с всички"
65 | 'true': "истина"
66 | 'false': "неистина"
67 | present: "присъства"
68 | blank: "е празно"
69 | 'null': "е нула"
70 | not_null: "не е нула"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/ca.yml:
--------------------------------------------------------------------------------
1 | ca:
2 | ransack:
3 | search: "cercar"
4 | predicate: "predicat"
5 | and: "i"
6 | or: "o"
7 | any: "qualsevol"
8 | all: "tots"
9 | combinator: "combinador"
10 | attribute: "atribut"
11 | value: "valor"
12 | condition: "condició"
13 | sort: "ordenar"
14 | asc: "ascendent"
15 | desc: "descendent"
16 | predicates:
17 | eq: "és igual a"
18 | eq_any: "és igual a qualsevol"
19 | eq_all: "és igual a tots"
20 | not_eq: "no és igual a"
21 | not_eq_any: "no és igual a qualsevol"
22 | not_eq_all: "no és igual a tots"
23 | matches: "coincideix"
24 | matches_any: "coincideix a qualsevol"
25 | matches_all: "coincideix a tots"
26 | does_not_match: "no coincideix"
27 | does_not_match_any: "no coincideix amb cap"
28 | does_not_match_all: "no coincideix amb tots"
29 | lt: "menor que"
30 | lt_any: "menor que qualsevol"
31 | lt_all: "menor o igual a"
32 | lteq: "menor que o igual a"
33 | lteq_any: "menor o igual a qualsevol"
34 | lteq_all: "menor o igual a tots"
35 | gt: "major que"
36 | gt_any: "major que qualsevol"
37 | gt_all: "major que tots"
38 | gteq: "major que o igual a"
39 | gteq_any: "major que o igual a qualsevol"
40 | gteq_all: "major que o igual a tots"
41 | in: "en"
42 | in_any: "en qualsevol"
43 | in_all: "en tots"
44 | not_in: "no en"
45 | not_in_any: "no en qualsevol"
46 | not_in_all: "no en tots"
47 | cont: "conté"
48 | cont_any: "conté qualsevol"
49 | cont_all: "conté tots"
50 | not_cont: "no conté"
51 | not_cont_any: "no conté cap"
52 | not_cont_all: "no conté tota"
53 | start: "comença per"
54 | start_any: "comença per qualsevol"
55 | start_all: "comença per tot"
56 | not_start: "no comença per"
57 | not_start_any: "no comença per qualsevol"
58 | not_start_all: "no comença per tot"
59 | end: "acaba en"
60 | end_any: "acaba en qualsevol"
61 | end_all: "acaba en tot"
62 | not_end: "no acaba en"
63 | not_end_any: "no acaba en qualsevol"
64 | not_end_all: "no acaba en tot"
65 | 'true': "és verdader"
66 | 'false': "és fals"
67 | present: "és present"
68 | blank: "està en blanc"
69 | 'null': "és nul"
70 | not_null: "no és nul"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/cs.yml:
--------------------------------------------------------------------------------
1 | cs:
2 | ransack:
3 | search: "vyhledávání"
4 | predicate: "predikát"
5 | and: "a"
6 | or: "nebo"
7 | any: "kteroukoliv"
8 | all: "každou"
9 | combinator: "kombinátor"
10 | attribute: "atribut"
11 | value: "hodnota"
12 | condition: "podmínka"
13 | sort: "řazení"
14 | asc: "vzestupné"
15 | desc: "sestupné"
16 | predicates:
17 | eq: "rovno"
18 | eq_any: "rovno kterékoliv"
19 | eq_all: "rovno všem"
20 | not_eq: "nerovno"
21 | not_eq_any: "nerovno kterékoliv"
22 | not_eq_all: "nerovno všem"
23 | matches: "odpovídá"
24 | matches_any: "odpovídá kterékoliv"
25 | matches_all: "odpovídá všem"
26 | does_not_match: "neodpovídá"
27 | does_not_match_any: "neodpovídá kterékoliv"
28 | does_not_match_all: "neodpovídá všem"
29 | lt: "menší než"
30 | lt_any: "menší než kterákoliv"
31 | lt_all: "menší než všechny"
32 | lteq: "menší nebo rovno než"
33 | lteq_any: "menší nebo rovno než kterákoliv"
34 | lteq_all: "menší nebo rovno než všechny"
35 | gt: "větší než"
36 | gt_any: "větší než kterákoliv"
37 | gt_all: "větší než všechny"
38 | gteq: "větší nebo rovno než"
39 | gteq_any: "větší nebo rovno než kterákoliv"
40 | gteq_all: "větší nebo rovno než všechny"
41 | in: "v"
42 | in_any: "v kterékoliv"
43 | in_all: "ve všech"
44 | not_in: "není v"
45 | not_in_any: "není v kterékoliv"
46 | not_in_all: "není ve všech"
47 | cont: "obsahuje"
48 | cont_any: "obsahuje kterékoliv"
49 | cont_all: "obsahuje všechny"
50 | not_cont: "neobsahuje"
51 | not_cont_any: "neobsahuje kteroukoliv"
52 | not_cont_all: "neobsahuje všechny"
53 | start: "začíná s"
54 | start_any: "začíná s kteroukoliv"
55 | start_all: "začíná se všemi"
56 | not_start: "nezačíná s"
57 | not_start_any: "nezačíná s kteroukoliv"
58 | not_start_all: "nezačíná se všemi"
59 | end: "končí s"
60 | end_any: "končí s kteroukoliv"
61 | end_all: "končí se všemi"
62 | not_end: "nekončí s"
63 | not_end_any: "nekončí s kteroukoliv"
64 | not_end_all: "nekončí se všemi"
65 | 'true': "je pravdivé"
66 | 'false': "není pravdivé"
67 | present: "je vyplněné"
68 | blank: "je prázdné"
69 | 'null': "je null"
70 | not_null: "není null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/da.yml:
--------------------------------------------------------------------------------
1 | da:
2 | ransack:
3 | search: "søg"
4 | predicate: "predicate"
5 | and: "og"
6 | or: "eller"
7 | any: "anhver"
8 | all: "alle"
9 | combinator: "kombinering"
10 | attribute: "attribut"
11 | value: "værdi"
12 | condition: "betingelse"
13 | sort: "sorter"
14 | asc: "opstigende"
15 | desc: "faldende"
16 | predicates:
17 | eq: "lig med"
18 | eq_any: "lig med enhver"
19 | eq_all: "lig med alle"
20 | not_eq: "ikke lig med"
21 | not_eq_any: "ikke lig med nogen"
22 | not_eq_all: "ikke lig med alle"
23 | matches: "matcher"
24 | matches_any: "matcher enhver"
25 | matches_all: "matcher alle"
26 | does_not_match: "matcher ikke"
27 | does_not_match_any: "matcher ikke nogen"
28 | does_not_match_all: "matcher ikke alle"
29 | lt: "mindre end"
30 | lt_any: "mindre end nogen"
31 | lt_all: "mindre end alle"
32 | lteq: "mindre end eller lig med"
33 | lteq_any: "mindre end eller lig med nogen"
34 | lteq_all: "mindre end eller lig med alle"
35 | gt: "større end"
36 | gt_any: "større end nogen"
37 | gt_all: "større end alle"
38 | gteq: "større end eller lig med"
39 | gteq_any: "større end eller lig med nogen"
40 | gteq_all: "større end eller lig med alle"
41 | in: "i"
42 | in_any: "i nogen"
43 | in_all: "i alle"
44 | not_in: "ikke i"
45 | not_in_any: "ikke i nogen"
46 | not_in_all: "ikke i alle"
47 | cont: "indeholder"
48 | cont_any: "indeholder nogen"
49 | cont_all: "indeholder alle"
50 | not_cont: "indeholder ikke"
51 | not_cont_any: "indeholder ikke nogen"
52 | not_cont_all: "indeholder ikke alle"
53 | start: "starter med"
54 | start_any: "starter med nogen"
55 | start_all: "starter med alle"
56 | not_start: "starter ikke med"
57 | not_start_any: "starter ikke med nogen"
58 | not_start_all: "starter ikke med alle"
59 | end: "slutter med"
60 | end_any: "slutter med nogen"
61 | end_all: "slutter med alle"
62 | not_end: "slutter ikke med"
63 | not_end_any: "slutter ikke med nogen"
64 | not_end_all: "slutter ikke med alle"
65 | 'true': "er sand"
66 | 'false': "er falsk"
67 | present: "er til stede"
68 | blank: "er blank"
69 | 'null': "er nul"
70 | not_null: "er ikke nul"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/de.yml:
--------------------------------------------------------------------------------
1 | de:
2 | ransack:
3 | search: "suchen"
4 | predicate: "Eigenschaft"
5 | and: "und"
6 | or: "oder"
7 | any: "beliebige"
8 | all: "alle"
9 | combinator: "Kombinator"
10 | attribute: "Attribut"
11 | value: "Wert"
12 | condition: "Bedingung"
13 | sort: "sortieren"
14 | asc: "aufsteigend"
15 | desc: "absteigend"
16 | predicates:
17 | eq: "gleicht"
18 | eq_any: "gleicht beliebigen"
19 | eq_all: "gleicht allen"
20 | not_eq: "ungleich"
21 | not_eq_any: "ungleich beliebigen"
22 | not_eq_all: "ungleich allen"
23 | matches: "entspricht"
24 | matches_any: "stimmt überein mit einem beliebigen"
25 | matches_all: "stimmt mit allen überein"
26 | does_not_match: "stimmt nicht überein"
27 | does_not_match_any: "erfüllt ein beliebiger/s nicht"
28 | does_not_match_all: "stimmt nicht mit allen überein"
29 | lt: "kleiner als"
30 | lt_any: "kleiner als ein beliebiger/s"
31 | lt_all: "kleiner als alle als alle"
32 | lteq: "kleiner oder gleich"
33 | lteq_any: "kleiner oder gleich beliebige"
34 | lteq_all: "kleiner oder gleich allen"
35 | gt: "größer als"
36 | gt_any: "größer als ein beliebiger/s"
37 | gt_all: "größer als alle"
38 | gteq: "größer oder gleich"
39 | gteq_any: "größer oder gleich als ein beliebiger/s"
40 | gteq_all: "größer oder gleich alle"
41 | in: "in"
42 | in_any: "ist nicht in einem beliebigen"
43 | in_all: "in allen"
44 | not_in: "nicht in"
45 | not_in_any: "nicht in beliebige"
46 | not_in_all: "nicht in allen"
47 | cont: "enthält"
48 | cont_any: "enthält beliebige"
49 | cont_all: "enthält alle"
50 | not_cont: "enthält nicht"
51 | not_cont_any: "enthält ein beliebiger/s nicht"
52 | not_cont_all: "enthält keine/s"
53 | start: "beginnt mit"
54 | start_any: "beginnt mit beliebigen"
55 | start_all: "beginnt mit allen"
56 | not_start: "beginnt nicht mit"
57 | not_start_any: "beginnt nicht mit beliebigen"
58 | not_start_all: "beginnt nicht mit allen"
59 | end: "endet mit"
60 | end_any: "endet mit beliebigen"
61 | end_all: "endet mit allen"
62 | not_end: "endet nicht mit"
63 | not_end_any: "endet nicht mit beliebigen"
64 | not_end_all: "endet nicht mit allen"
65 | 'true': "ist wahr"
66 | 'false': "ist falsch"
67 | present: "ist vorhanden"
68 | blank: "ist leer"
69 | 'null': "ist null"
70 | not_null: "ist nicht null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/el.yml:
--------------------------------------------------------------------------------
1 | el:
2 | ransack:
3 | search: "αναζήτηση"
4 | predicate: "κατηγορούμενο"
5 | and: "και"
6 | or: "ή"
7 | any: "οποιοδήποτε"
8 | all: "όλα"
9 | combinator: "συνδυαστής"
10 | attribute: "ιδιότητα"
11 | value: "τιμή"
12 | condition: "συνθήκη"
13 | sort: "ταξινόμηση"
14 | asc: "αύξουσα"
15 | desc: "φθίνουσα"
16 | predicates:
17 | eq: "ίσον"
18 | eq_any: "ισούται με οποιοδήποτε"
19 | eq_all: "ισούται με όλα"
20 | not_eq: "άνισο"
21 | not_eq_any: "άνισο με οποιοδήποτε"
22 | not_eq_all: "άνισο με όλα"
23 | matches: "αντιστοιχεί"
24 | matches_any: "αντιστοιχεί με οποιοδήποτε"
25 | matches_all: "αντιστοιχεί με όλα"
26 | does_not_match: "δεν αντιστοιχεί"
27 | does_not_match_any: "δεν αντιστοιχεί με οποιοδήποτε"
28 | does_not_match_all: "δεν αντιστοιχεί με όλα"
29 | lt: "μικρότερο από"
30 | lt_any: "μικρότερο από οποιοδήποτε"
31 | lt_all: "μικρότερο από όλα"
32 | lteq: "μικρότερο ή ίσον"
33 | lteq_any: "μικρότερο ή ίσον από οποιοδήποτε"
34 | lteq_all: "μικρότερο ή ίσον από όλα"
35 | gt: "μεγαλύτερο από"
36 | gt_any: "μεγαλύτερο από οποιοδήποτε"
37 | gt_all: μεγαλύτερο από όλα"
38 | gteq: "μεγαλύτερο ή ίσον από"
39 | gteq_any: "μεγαλύτερο ή ίσον από οποιοδήποτε"
40 | gteq_all: "μεγαλύτερο ή ίσον από όλα"
41 | in: "περιέχεται"
42 | in_any: "περιέχεται σε οποιοδήποτε"
43 | in_all: "περιέχεται σε όλα"
44 | not_in: "δεν περιέχεται"
45 | not_in_any: "δεν περιέχεται σε οποιοδήποτε"
46 | not_in_all: "δεν περιέχεται σε όλα"
47 | cont: "περιέχει"
48 | cont_any: "περιέχει οποιοδήποτε"
49 | cont_all: "περιέχει όλα"
50 | not_cont: "δεν περιέχει"
51 | not_cont_any: "δεν περιέχει οποιοδήποτε"
52 | not_cont_all: "δεν περιέχει όλα"
53 | start: "αρχίζει με"
54 | start_any: "αρχίζει με οποιοδήποτε"
55 | start_all: "αρχίζει με όλα"
56 | not_start: "δεν αρχίζει με"
57 | not_start_any: "δεν αρχίζει με οποιοδήποτε"
58 | not_start_all: "δεν αρχίζει με όλα"
59 | end: "τελειώνει με"
60 | end_any: "τελειώνει οποιοδήποτε"
61 | end_all: "τελειώνει με όλα"
62 | not_end: "δεν τελειώνει με"
63 | not_end_any: "δεν τελειώνει οποιοδήποτε"
64 | not_end_all: "δεν τελειώνει με όλα"
65 | 'true': "αληθές"
66 | 'false': "ψευδές"
67 | present: "υπάρχει"
68 | blank: "είναι κενό"
69 | 'null': "άκυρο"
70 | not_null: "δεν είναι άκυρο"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | ransack:
3 | search: "search"
4 | predicate: "predicate"
5 | and: "and"
6 | or: "or"
7 | any: "any"
8 | all: "all"
9 | combinator: "combinator"
10 | attribute: "attribute"
11 | value: "value"
12 | condition: "condition"
13 | sort: "sort"
14 | asc: "ascending"
15 | desc: "descending"
16 | predicates:
17 | eq: "equals"
18 | eq_any: "equals any"
19 | eq_all: "equals all"
20 | not_eq: "not equal to"
21 | not_eq_any: "not equal to any"
22 | not_eq_all: "not equal to all"
23 | matches: "matches"
24 | matches_any: "matches any"
25 | matches_all: "matches all"
26 | does_not_match: "doesn't match"
27 | does_not_match_any: "doesn't match any"
28 | does_not_match_all: "doesn't match all"
29 | lt: "less than"
30 | lt_any: "less than any"
31 | lt_all: "less than all"
32 | lteq: "less than or equal to"
33 | lteq_any: "less than or equal to any"
34 | lteq_all: "less than or equal to all"
35 | gt: "greater than"
36 | gt_any: "greater than any"
37 | gt_all: "greater than all"
38 | gteq: "greater than or equal to"
39 | gteq_any: "greater than or equal to any"
40 | gteq_all: "greater than or equal to all"
41 | in: "in"
42 | in_any: "in any"
43 | in_all: "in all"
44 | not_in: "not in"
45 | not_in_any: "not in any"
46 | not_in_all: "not in all"
47 | cont: "contains"
48 | cont_any: "contains any"
49 | cont_all: "contains all"
50 | not_cont: "doesn't contain"
51 | not_cont_any: "doesn't contain any"
52 | not_cont_all: "doesn't contain all"
53 | start: "starts with"
54 | start_any: "starts with any"
55 | start_all: "starts with all"
56 | not_start: "doesn't start with"
57 | not_start_any: "doesn't start with any"
58 | not_start_all: "doesn't start with all"
59 | end: "ends with"
60 | end_any: "ends with any"
61 | end_all: "ends with all"
62 | not_end: "doesn't end with"
63 | not_end_any: "doesn't end with any"
64 | not_end_all: "doesn't end with all"
65 | 'true': "is true"
66 | 'false': "is false"
67 | present: "is present"
68 | blank: "is blank"
69 | 'null': "is null"
70 | not_null: "is not null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/es.yml:
--------------------------------------------------------------------------------
1 | es:
2 | ransack:
3 | search: "buscar"
4 | predicate: "predicado"
5 | and: "y"
6 | or: "o"
7 | any: "cualquier"
8 | all: "todos"
9 | combinator: "combinador"
10 | attribute: "atributo"
11 | value: "valor"
12 | condition: "condición"
13 | sort: "ordenar"
14 | asc: "ascendente"
15 | desc: "descendente"
16 | predicates:
17 | eq: "es igual a"
18 | eq_any: "es igual a cualquier"
19 | eq_all: "es igual a todos"
20 | not_eq: "no es igual a"
21 | not_eq_any: "no es igual a cualquier"
22 | not_eq_all: "no es igual a todos"
23 | matches: "coincide con"
24 | matches_any: "coincide con cualquier"
25 | matches_all: "coincide con todos"
26 | does_not_match: "no coincide con"
27 | does_not_match_any: "no coincide con ninguno"
28 | does_not_match_all: "no coincide con todos"
29 | lt: "menor que"
30 | lt_any: "menor que cualquier"
31 | lt_all: "menor o igual a"
32 | lteq: "menor que o igual a"
33 | lteq_any: "menor o igual a cualquier"
34 | lteq_all: "menor o igual a todos"
35 | gt: "mayor que"
36 | gt_any: "mayor que cualquier"
37 | gt_all: "mayor que todos"
38 | gteq: "mayor que o igual a"
39 | gteq_any: "mayor que o igual a cualquier"
40 | gteq_all: "mayor que o igual a todos"
41 | in: "en"
42 | in_any: "en cualquier"
43 | in_all: "en todos"
44 | not_in: "no en"
45 | not_in_any: "no en cualquier"
46 | not_in_all: "no en todos"
47 | cont: "contiene"
48 | cont_any: "contiene cualquier"
49 | cont_all: "contiene todos"
50 | not_cont: "no contiene"
51 | not_cont_any: "no contiene ninguno"
52 | not_cont_all: "no contiene todos"
53 | start: "comienza con"
54 | start_any: "comienza con cualquier"
55 | start_all: "comienza con todos"
56 | not_start: "no comienza con"
57 | not_start_any: "no comienza con cualquier"
58 | not_start_all: "no comienza con todos"
59 | end: "termina en"
60 | end_any: "termina en cualquier"
61 | end_all: "termina en todos"
62 | not_end: "no termina en"
63 | not_end_any: "no termina en cualquier"
64 | not_end_all: "no termina en todos"
65 | 'true': "es verdadero"
66 | 'false': "es falso"
67 | present: "está presente"
68 | blank: "está en blanco"
69 | 'null': "es nulo"
70 | not_null: "no es nulo"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/fa.yml:
--------------------------------------------------------------------------------
1 | fa:
2 | ransack:
3 | search: "جستجو"
4 | predicate: "پیش فرض"
5 | and: "و"
6 | or: "یا"
7 | any: "هر کدام"
8 | all: "همه"
9 | combinator: "ترکیب کننده"
10 | attribute: "ویژگی"
11 | value: "مقدار"
12 | condition: "شرایط"
13 | sort: "مرتب سازی"
14 | asc: "صعودی"
15 | desc: "نزولی"
16 | predicates:
17 | eq: "مساوی با"
18 | eq_any: "مساوی با حداقل یکی"
19 | eq_all: "مساوی با همه"
20 | not_eq: "غیر مساوی با"
21 | not_eq_any: "غیر مساوی با هیچ کدام"
22 | not_eq_all: "غیر مساوی با همه"
23 | matches: "مشابهات"
24 | matches_any: "شبیه حداقل یکی"
25 | matches_all: "شبیه همه"
26 | does_not_match: "بدون شباهت"
27 | does_not_match_any: "شبیه یکی هم نیست"
28 | does_not_match_all: "شبیه هیچ کدام نیست"
29 | lt: "کمتر از"
30 | lt_any: "کمتر از حداقل یکی"
31 | lt_all: "کمتر از همه"
32 | lteq: "کمتر یا مساوی با"
33 | lteq_any: "کمتر یا مساوی با حداقل یکی"
34 | lteq_all: "کمتر یا مساوی با همه"
35 | gt: "بیشتر از"
36 | gt_any: "بیشتر از حداقل یکی"
37 | gt_all: "بیشتر از همه"
38 | gteq: "بیشتر یا مساوی با"
39 | gteq_any: "بیشتر یا مساوی با حداقل یکی"
40 | gteq_all: "بیشتر یا مساوی با همه"
41 | in: "در"
42 | in_any: "در حداقل یکی"
43 | in_all: "در همه"
44 | not_in: "نه در"
45 | not_in_any: "نه حتی در یکی"
46 | not_in_all: "در هیچ کدام"
47 | cont: "حاوی"
48 | cont_any: "حاوی حداقل یکی"
49 | cont_all: "حاوی همه"
50 | not_cont: "حاوی این نمیشود"
51 | not_cont_any: "حاوی حتی یکی هم نمیشود"
52 | not_cont_all: "حاوی هیچ کدام نمیشود"
53 | start: "شروع می شود با"
54 | start_any: "شروع می شود با حداقل یکی"
55 | start_all: "شروع می شود با همه"
56 | not_start: "شروع نمی شود با"
57 | not_start_any: "شروع نمی شود با حتی یکی"
58 | not_start_all: "شروع نمی شود با هیچ کدام"
59 | end: "به پایان می رسد با"
60 | end_any: "به پایان می رسد با حداقل یکی"
61 | end_all: "به پایان می رسد با همه"
62 | not_end: "پایان نمی یابد با"
63 | not_end_any: "پایان نمی یابد با حتی یکی"
64 | not_end_all: "پایان نمی یابد با هیچ کدام"
65 | 'true': "درست است"
66 | 'false': "نادرست است"
67 | present: "موجود است"
68 | blank: "خالی است"
69 | 'null': "صفر است"
70 | not_null: "صفر نیست"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/fi.yml:
--------------------------------------------------------------------------------
1 | fi:
2 | ransack:
3 | search: "haku"
4 | predicate: "predikaatti"
5 | and: "ja"
6 | or: "tai"
7 | any: "jokin"
8 | all: "kaikki"
9 | combinator: "kombinaattori"
10 | attribute: "attribuutti"
11 | value: "arvo"
12 | condition: "ehto"
13 | sort: "järjestys"
14 | asc: "nouseva"
15 | desc: "laskeva"
16 | predicates:
17 | eq: "sama kuin"
18 | eq_any: "sama kuin jokin"
19 | eq_all: "sama kuin kaikki"
20 | not_eq: "eri kuin"
21 | not_eq_any: "eri kuin jokin"
22 | not_eq_all: "eri kuin kaikki"
23 | matches: "täsmää"
24 | matches_any: "täsmää jonkun kanssa"
25 | matches_all: "täsmää kaikkien kanssa"
26 | does_not_match: "ei täsmää"
27 | does_not_match_any: "ei täsmää minkään kanssa"
28 | does_not_match_all: "ei täsmää kaikkien kanssa"
29 | lt: "vähemmän kuin"
30 | lt_any: "vähemmän kuin jokin"
31 | lt_all: "vähemmän kuin mikään"
32 | lteq: "vähemmän tai sama kuin"
33 | lteq_any: "vähemmän tai sama kuin jokin"
34 | lteq_all: "vähemmän tai sama kuin mikään"
35 | gt: "suurempi kuin"
36 | gt_any: "suurempi kuin jokin"
37 | gt_all: "suurempi kuin mikään"
38 | gteq: "suurempi tai yhtä suuri kuin"
39 | gteq_any: "suurempi tai yhtä suuri kuin jokin"
40 | gteq_all: "suurempi tai yhtä suuri kuin mikään"
41 | in: "kohteessa"
42 | in_any: "jossakin kohteessa"
43 | in_all: "kaikissa kohteissa"
44 | not_in: "ei kohteessa"
45 | not_in_any: "ei jossakin kohteista"
46 | not_in_all: "ei missään kohteista"
47 | cont: "sisältää"
48 | cont_any: "sisältää jonkin"
49 | cont_all: "sisältää kaikki"
50 | not_cont: "ei sisällä"
51 | not_cont_any: "ei sisällä jotakin"
52 | not_cont_all: "ei sisällä mitään"
53 | start: "alkaa"
54 | start_any: "alkaa jollakin"
55 | start_all: "alkaa kaikilla"
56 | not_start: "ei ala"
57 | not_start_any: "ei ala jollakin"
58 | not_start_all: "ei ala millään"
59 | end: "päättyy"
60 | end_any: "päättyy jollakin"
61 | end_all: "päättyy millään"
62 | not_end: "ei pääty"
63 | not_end_any: "ei pääty jollakin"
64 | not_end_all: "ei pääty millään"
65 | 'true': "on tosi"
66 | 'false': "ei ole tosi"
67 | present: "on läsnä"
68 | blank: "on tyhjä"
69 | 'null': "on määrittämätön"
70 | not_null: "on määritetty"
71 |
72 |
--------------------------------------------------------------------------------
/lib/ransack/locale/fr.yml:
--------------------------------------------------------------------------------
1 | fr:
2 | ransack:
3 | search: "recherche"
4 | predicate: "prédicat"
5 | and: "et"
6 | or: "ou"
7 | any: "au moins un"
8 | all: "tous"
9 | combinator: "combinateur"
10 | attribute: "attribut"
11 | value: "valeur"
12 | condition: "condition"
13 | sort: "tri"
14 | asc: "ascendant"
15 | desc: "descendant"
16 | predicates:
17 | eq: "égal à"
18 | eq_any: "égal à au moins un"
19 | eq_all: "égal à tous"
20 | not_eq: "différent de"
21 | not_eq_any: "différent d'au moins un"
22 | not_eq_all: "différent de tous"
23 | matches: "correspond à"
24 | matches_any: "correspond à au moins un"
25 | matches_all: "correspond à tous"
26 | does_not_match: "ne correspond pas à"
27 | does_not_match_any: "ne correspond pas à au moins un"
28 | does_not_match_all: "ne correspond à aucun"
29 | lt: "inférieur à"
30 | lt_any: "inférieur à au moins un"
31 | lt_all: "inférieur à tous"
32 | lteq: "inférieur ou égal à"
33 | lteq_any: "inférieur ou égal à au moins un"
34 | lteq_all: "inférieur ou égal à tous"
35 | gt: "supérieur à"
36 | gt_any: "supérieur à au moins un"
37 | gt_all: "supérieur à tous"
38 | gteq: "supérieur ou égal à"
39 | gteq_any: "supérieur ou égal à au moins un"
40 | gteq_all: "supérieur ou égal à tous"
41 | in: "inclus dans"
42 | in_any: "inclus dans au moins un"
43 | in_all: "inclus dans tous"
44 | not_in: "non inclus dans"
45 | not_in_any: "non inclus dans au moins un"
46 | not_in_all: "non inclus dans tous"
47 | cont: "contient"
48 | cont_any: "contient au moins un"
49 | cont_all: "contient tous"
50 | not_cont: "ne contient pas"
51 | not_cont_any: "ne contient pas au moins un"
52 | not_cont_all: "ne contient pas tous"
53 | start: "commence par"
54 | start_any: "commence par au moins un"
55 | start_all: "commence par tous"
56 | not_start: "ne commence pas par"
57 | not_start_any: "ne commence pas par au moins un"
58 | not_start_all: "ne commence pas par tous"
59 | end: "finit par"
60 | end_any: "finit par au moins un"
61 | end_all: "finit par tous"
62 | not_end: "ne finit pas par"
63 | not_end_any: "ne finit pas par au moins un"
64 | not_end_all: "ne finit pas par tous"
65 | 'true': "est vrai"
66 | 'false': "est faux"
67 | present: "est présent"
68 | blank: "est blanc"
69 | 'null': "est null"
70 | not_null: "n'est pas null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/hu.yml:
--------------------------------------------------------------------------------
1 | hu:
2 | ransack:
3 | search: "keresés"
4 | predicate: "állítás"
5 | and: "és"
6 | or: "vagy"
7 | any: "bármely"
8 | all: "mindegyik"
9 | combinator: "combinator"
10 | attribute: "attribute"
11 | value: "érték"
12 | condition: "feltétel"
13 | sort: "rendezés"
14 | asc: "növekvő"
15 | desc: "csökkenő"
16 | predicates:
17 | eq: "egyenlő"
18 | eq_any: "bármelyikkel egyenlő"
19 | eq_all: "minddel egyenlő"
20 | not_eq: "nem egyenlő"
21 | not_eq_any: "nem egyenlő bármelyikkel"
22 | not_eq_all: "nem egyenlő egyikkel sem"
23 | matches: "egyezik"
24 | matches_any: "bármelyikkel egyezik"
25 | matches_all: "minddel egyezik"
26 | does_not_match: "nem egyezik"
27 | does_not_match_any: "nem egyezik semelyikkel"
28 | does_not_match_all: "nem egyezik az összessel"
29 | lt: "kisebb, mint"
30 | lt_any: "bármelyiknél kisebb"
31 | lt_all: "mindegyiknél kisebb"
32 | lteq: "kisebb vagy egyenlő, mint"
33 | lteq_any: "bármelyiknél kisebb vagy egyenlő"
34 | lteq_all: "mindegyiknél kisebb vagy egyenlő"
35 | gt: "nagyobb, mint"
36 | gt_any: "bármelyiknél nagyobb"
37 | gt_all: "mindegyiknél nagyobb"
38 | gteq: "nagyobb vagy egyenlő, mint"
39 | gteq_any: "bármelyiknél nagyobb vagy egyenlő"
40 | gteq_all: "mindegyiknél nagyobb vagy egyenlő"
41 | in: "értéke"
42 | in_any: "értéke bármelyik"
43 | in_all: "értéke mindegyik"
44 | not_in: "nem ez az értéke"
45 | not_in_any: "értéke egyik sem"
46 | not_in_all: "értéke nem ezek az elemek"
47 | cont: "tartalmazza"
48 | cont_any: "bármelyiket tartalmazza"
49 | cont_all: "mindet tartalmazza"
50 | not_cont: "nem tartalmazza"
51 | not_cont_any: "egyiket sem tartalmazza"
52 | not_cont_all: "nem tartalmazza mindet"
53 | start: "így kezdődik"
54 | start_any: "bármelyikkel kezdődik"
55 | start_all: "ezekkel kezdődik"
56 | not_start: "nem így kezdődik"
57 | not_start_any: "nem ezek egyikével kezdődik"
58 | not_start_all: "nem ezekkel kezdődik"
59 | end: "így végződik"
60 | end_any: "bármelyikkel végződik"
61 | end_all: "ezekkel végződik"
62 | not_end: "nem úgy végződik"
63 | not_end_any: "nem ezek egyikével végződik"
64 | not_end_all: "nem ezekkel végződik"
65 | 'true': "igaz"
66 | 'false': "hamis"
67 | present: "létezik"
68 | blank: "üres"
69 | 'null': "null"
70 | not_null: "nem null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/id.yml:
--------------------------------------------------------------------------------
1 | id:
2 | ransack:
3 | search: "cari"
4 | predicate: "predikat"
5 | and: "dan"
6 | or: "atau"
7 | any: "apapun"
8 | all: "semua"
9 | combinator: "kombinasi"
10 | attribute: "atribut"
11 | value: "data"
12 | condition: "kondisi"
13 | sort: "urutan"
14 | asc: "ascending"
15 | desc: "descending"
16 | predicates:
17 | eq: "sama dengan"
18 | eq_any: "sama beberapa dengan"
19 | eq_all: "sama seluruhnya dengan"
20 | not_eq: "tidak sama dengan"
21 | not_eq_any: "tidak sama beberapa dengan"
22 | not_eq_all: "tidak semua seluruhnya dengan"
23 | matches: "mirip"
24 | matches_any: "mirip beberapa dengan"
25 | matches_all: "mirip semua dengan"
26 | does_not_match: "tidak mirip dengan"
27 | does_not_match_any: "tidak mirip beberapa dengan"
28 | does_not_match_all: "tidak mirip semua dengan"
29 | lt: "kurang dari"
30 | lt_any: "kurang beberapa dengan"
31 | lt_all: "kurang seluruhnya dengan"
32 | lteq: "kurang lebih"
33 | lteq_any: "kurang lebih beberapa dengan"
34 | lteq_all: "kurang lebih semua dengan"
35 | gt: "lebih besar daripada"
36 | gt_any: "lebih besar beberapa dengan"
37 | gt_all: "lebih besar semua dengan"
38 | gteq: "lebih besar atau sama dengan"
39 | gteq_any: "beberapa lebih besar atau sama dengan"
40 | gteq_all: "semua lebih besar atau sama dengan"
41 | in: "di"
42 | in_any: "di beberapa"
43 | in_all: "di semua"
44 | not_in: "tidak di"
45 | not_in_any: "tidak di beberapa"
46 | not_in_all: "tidak semua di"
47 | cont: "mengandung"
48 | cont_any: "mengandung beberapa"
49 | cont_all: "mengandung semua"
50 | not_cont: "tidak mengandung"
51 | not_cont_any: "tidak mengandung beberapa"
52 | not_cont_all: "tidak mengandung semua"
53 | start: "diawali dengan"
54 | start_any: "diawali beberapa dengan"
55 | start_all: "diawali semua dengan"
56 | not_start: "tidak diawali dengan"
57 | not_start_any: "tidak diawali beberapa dengan"
58 | not_start_all: "tidak diawali semua dengan"
59 | end: "diakhiri dengan"
60 | end_any: "diakhiri beberapa dengan"
61 | end_all: "diakhiri semua dengan"
62 | not_end: "tidak diakhiri dengan"
63 | not_end_any: "tidak diakhiri dengan beberapa"
64 | not_end_all: "tidak diakhiri dengan semua"
65 | 'true': "bernilai benar"
66 | 'false': "bernilai salah"
67 | present: "ada"
68 | blank: "kosong"
69 | 'null': "null"
70 | not_null: "tidak null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/it.yml:
--------------------------------------------------------------------------------
1 | it:
2 | ransack:
3 | search: "cerca"
4 | predicate: "predicato"
5 | and: "e"
6 | or: "o"
7 | any: "qualsiasi"
8 | all: "tutti"
9 | combinator: "combinatore"
10 | attribute: "attributo"
11 | value: "valore"
12 | condition: "condizione"
13 | sort: "ordinamento"
14 | asc: "crescente"
15 | desc: "decrescente"
16 | predicates:
17 | eq: "uguale a"
18 | eq_any: "uguale ad almeno un"
19 | eq_all: "uguale ad ognuno"
20 | not_eq: "diverso da"
21 | not_eq_any: "diverso da uno qualsiasi"
22 | not_eq_all: "diverso da tutti"
23 | matches: "combacia con"
24 | matches_any: "combacia con almeno un"
25 | matches_all: "combacia con tutti"
26 | does_not_match: "non corrisponde"
27 | does_not_match_any: "non corrisponde ad uno qualsiasi"
28 | does_not_match_all: "non corrisponde con nessuno"
29 | lt: "minore di"
30 | lt_any: "minore di almeno un"
31 | lt_all: "minore di tutti"
32 | lteq: "minore o uguale a"
33 | lteq_any: "minore o uguale ad almeno un"
34 | lteq_all: "minore o uguale a tutti"
35 | gt: "maggiore di"
36 | gt_any: "maggiore di almeno un"
37 | gt_all: "maggiore di tutti"
38 | gteq: "maggiore o uguale a"
39 | gteq_any: "maggiore o uguale ad almeno un"
40 | gteq_all: "maggiore o uguale a tutti"
41 | in: "in"
42 | in_any: "in almeno un"
43 | in_all: "in tutti"
44 | not_in: "non in"
45 | not_in_any: "non in almeno un"
46 | not_in_all: "non in tutti"
47 | cont: "contiene"
48 | cont_any: "contiene almeno un"
49 | cont_all: "contiene tutti"
50 | not_cont: "non contiene"
51 | not_cont_any: "non contiene un qualsiasi"
52 | not_cont_all: "non contiene nessuno"
53 | start: "inizia con"
54 | start_any: "inizia con almeno un"
55 | start_all: "inizia con tutti"
56 | not_start: "non inizia con"
57 | not_start_any: "non inizia con uno qualsiasi"
58 | not_start_all: "non inizia con nessuno"
59 | end: "finisce con"
60 | end_any: "finisce con almeno un"
61 | end_all: "finisce con tutti"
62 | not_end: "non finisce con"
63 | not_end_any: "non finisce con uno qualsiasi"
64 | not_end_all: "non finisce con nessuno"
65 | 'true': "è vero"
66 | 'false': "è falso"
67 | present: "è presente"
68 | blank: "è vuoto"
69 | 'null': "è nullo"
70 | not_null: "non è nullo"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/ja.yml:
--------------------------------------------------------------------------------
1 | ja:
2 | ransack:
3 | search: "検索"
4 | predicate: "は以下である"
5 | and: "と"
6 | or: "あるいは"
7 | any: "いずれか"
8 | all: "全て"
9 | combinator: "組み合わせ"
10 | attribute: "属性"
11 | value: "値"
12 | condition: "状態"
13 | sort: "分類"
14 | asc: "昇順"
15 | desc: "降順"
16 | predicates:
17 | eq: "は以下と等しい"
18 | eq_any: "は以下のいずれかに等しい"
19 | eq_all: "は以下の全てに等しい"
20 | not_eq: "は以下と等しくない"
21 | not_eq_any: "は以下のいずれかに等しくない"
22 | not_eq_all: "は以下の全てと等しくない"
23 | matches: "は以下と合致している"
24 | matches_any: "は以下のいずれかと合致している"
25 | matches_all: "は以下の全てと合致している"
26 | does_not_match: "は以下と合致していない"
27 | does_not_match_any: "は以下のいずれかに合致していない"
28 | does_not_match_all: "は以下の全てに合致していない"
29 | lt: "は以下よりも小さい"
30 | lt_any: "は以下のいずれかより小さい"
31 | lt_all: "は以下の全てよりも小さい"
32 | lteq: "は以下より小さいか等しい"
33 | lteq_any: "は以下のいずれかより小さいか等しい"
34 | lteq_all: "は以下の全てより小さいか等しい"
35 | gt: "は以下より大きい"
36 | gt_any: "は以下のいずれかより大きい"
37 | gt_all: "は以下の全てより大きい"
38 | gteq: "は以下より大きいか等しい"
39 | gteq_any: "は以下のいずれかより大きいか等しい"
40 | gteq_all: "は以下の全てより大きいか等しい"
41 | in: "は以下の範囲内である"
42 | in_any: "は以下のいずれかの範囲内である"
43 | in_all: "は以下の全ての範囲内である"
44 | not_in: "は以下の範囲内でない"
45 | not_in_any: "は以下のいずれかの範囲内でない"
46 | not_in_all: "は以下の全ての範囲内"
47 | cont: "は以下を含む"
48 | cont_any: "はいずれかを含む"
49 | cont_all: "は以下の全てを含む"
50 | not_cont: "は含まない"
51 | not_cont_any: "は以下のいずれかを含まない"
52 | not_cont_all: "は以下の全てを含まない"
53 | start: "は以下で始まる"
54 | start_any: "は以下のどれかで始まる"
55 | start_all: "は以下の全てで始まる"
56 | not_start: "は以下で始まらない"
57 | not_start_any: "は以下のいずれかで始まらない"
58 | not_start_all: "は以下の全てで始まらない"
59 | end: "は以下で終わる"
60 | end_any: "は以下のいずれかで終わる"
61 | end_all: "は以下の全てで終わる"
62 | not_end: "は以下のどれでも終わらない"
63 | not_end_any: "は以下のいずれかで終わらない"
64 | not_end_all: "は以下の全てで終わらない"
65 | 'true': "真"
66 | 'false': "偽"
67 | present: "は存在する"
68 | blank: "は空である"
69 | 'null': "無効"
70 | not_null: "は無効ではない"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/ko.yml:
--------------------------------------------------------------------------------
1 | ko:
2 | ransack:
3 | search: "검색"
4 | predicate: "조건"
5 | and: "그리고"
6 | or: "또는"
7 | any: "어떤 것이든"
8 | all: "모두"
9 | combinator: "조합기"
10 | attribute: "속성"
11 | value: "값"
12 | condition: "조건"
13 | sort: "정렬"
14 | asc: "오름차순"
15 | desc: "내림차순"
16 | predicates:
17 | eq: "같음"
18 | eq_any: "어떤 것이든 같음"
19 | eq_all: "모두 같음"
20 | not_eq: "같지 않음"
21 | not_eq_any: "어떤 것이든 같지 않음"
22 | not_eq_all: "모두 같지 않음"
23 | matches: "일치함"
24 | matches_any: "어떤 것이든 일치함"
25 | matches_all: "모두 일치함"
26 | does_not_match: "일치하지 않음"
27 | does_not_match_any: "어떤 것이든 일치하지 않음"
28 | does_not_match_all: "모두 일치하지 않음"
29 | lt: "보다 작음"
30 | lt_any: "어떤 것이든 보다 작음"
31 | lt_all: "모두 보다 작음"
32 | lteq: "보다 작거나 같음"
33 | lteq_any: "어떤 것이든 보다 작거나 같음"
34 | lteq_all: "모두 보다 작거나 같음"
35 | gt: "보다 큼"
36 | gt_any: "어떤 것이든 보다 큼"
37 | gt_all: "모두 보다 큼"
38 | gteq: "보다 크거나 같음"
39 | gteq_any: "어떤 것이든 보다 크거나 같음"
40 | gteq_all: "모두 보다 크거나 같음"
41 | in: "포함됨"
42 | in_any: "어떤 것이든 포함됨"
43 | in_all: "모두 포함됨"
44 | not_in: "포함되지 않음"
45 | not_in_any: "어떤 것이든 포함되지 않음"
46 | not_in_all: "모두 포함되지 않음"
47 | cont: "포함함"
48 | cont_any: "어떤 것이든 포함함"
49 | cont_all: "모두 포함함"
50 | not_cont: "포함하지 않음"
51 | not_cont_any: "어떤 것이든 포함하지 않음"
52 | not_cont_all: "모두 포함하지 않음"
53 | start: "시작함"
54 | start_any: "어떤 것이든 시작함"
55 | start_all: "모두 시작함"
56 | not_start: "시작하지 않음"
57 | not_start_any: "어떤 것이든 시작하지 않음"
58 | not_start_all: "모두 시작하지 않음"
59 | end: "끝남"
60 | end_any: "어떤 것이든 끝남"
61 | end_all: "모두 끝남"
62 | not_end: "끝나지 않음"
63 | not_end_any: "어떤 것이든 끝나지 않음"
64 | not_end_all: "모두 끝나지 않음"
65 | 'true': "is true"
66 | 'false': "is false"
67 | present: "is present"
68 | blank: "is blank"
69 | 'null': "is null"
70 | not_null: "is not null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/nl.yml:
--------------------------------------------------------------------------------
1 | nl:
2 | ransack:
3 | search: "zoeken"
4 | predicate: "eigenschap"
5 | and: "en"
6 | or: "of"
7 | any: "enig"
8 | all: "alle"
9 | combinator: "combinator"
10 | attribute: "attribuut"
11 | value: "waarde"
12 | condition: "conditie"
13 | sort: "sorteren"
14 | asc: "oplopend"
15 | desc: "aflopend"
16 | predicates:
17 | eq: "gelijk"
18 | eq_any: "gelijk enig"
19 | eq_all: "gelijk alle"
20 | not_eq: "niet gelijk aan"
21 | not_eq_any: "niet gelijk aan enig"
22 | not_eq_all: "niet gelijk aan alle"
23 | matches: "evenaart"
24 | matches_any: "evenaart enig"
25 | matches_all: "evenaart alle"
26 | does_not_match: "evenaart niet"
27 | does_not_match_any: "evenaart niet voor enig"
28 | does_not_match_all: "evenaart niet voor alle"
29 | lt: "kleiner dan"
30 | lt_any: "kleiner dan enig"
31 | lt_all: "kleiner dan alle"
32 | lteq: "kleiner dan of gelijk aan"
33 | lteq_any: "kleiner dan of gelijk aan enig"
34 | lteq_all: "kleiner dan of gelijk aan alle"
35 | gt: "groter dan"
36 | gt_any: "groter dan enig"
37 | gt_all: "groter dan alle"
38 | gteq: "groter dan of gelijk aan"
39 | gteq_any: "groter dan of gelijk aan enig"
40 | gteq_all: "groter dan of gelijk aan alle"
41 | in: "in"
42 | in_any: "in enig"
43 | in_all: "in alle"
44 | not_in: "niet in"
45 | not_in_any: "niet in enig"
46 | not_in_all: "niet in alle"
47 | cont: "bevat"
48 | cont_any: "bevat enig"
49 | cont_all: "bevat alle"
50 | not_cont: "bevat niet"
51 | not_cont_any: "bevat niet enig"
52 | not_cont_all: "bevat niet alle"
53 | start: "start met"
54 | start_any: "start met enig"
55 | start_all: "start met alle"
56 | not_start: "start niet met"
57 | not_start_any: "start niet met enig"
58 | not_start_all: "start niet met alle"
59 | end: "eindigt met"
60 | end_any: "eindigt met enig"
61 | end_all: "eindigt met alle"
62 | not_end: "eindigt niet met"
63 | not_end_any: "eindigt niet met enig"
64 | not_end_all: "eindigt niet met alle"
65 | 'true': "is waar"
66 | 'false': "is niet waar"
67 | present: "is aanwezig"
68 | blank: "is afwezig"
69 | 'null': "is null"
70 | not_null: "is niet null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/pt-BR.yml:
--------------------------------------------------------------------------------
1 | pt-BR:
2 | ransack:
3 | search: "pesquisar"
4 | predicate: "predicado"
5 | and: "e"
6 | or: "ou"
7 | any: "algum"
8 | all: "todos"
9 | combinator: "combinador"
10 | attribute: "atributo"
11 | value: "valor"
12 | condition: "condição"
13 | sort: "classificar"
14 | asc: "ascendente"
15 | desc: "descendente"
16 | predicates:
17 | eq: "igual"
18 | eq_any: "igual a algum"
19 | eq_all: "igual a todos"
20 | not_eq: "não é igual a"
21 | not_eq_any: "não é igual a algum"
22 | not_eq_all: "não é igual a todos"
23 | matches: "corresponde"
24 | matches_any: "corresponde a algum"
25 | matches_all: "corresponde a todos"
26 | does_not_match: "não corresponde"
27 | does_not_match_any: "não corresponde a algum"
28 | does_not_match_all: "não corresponde a todos"
29 | lt: "menor que"
30 | lt_any: "menor que algum"
31 | lt_all: "menor que todos"
32 | lteq: "menor ou igual a"
33 | lteq_any: "menor ou igual a algum"
34 | lteq_all: "menor ou igual a todos"
35 | gt: "maior que"
36 | gt_any: "maior que algum"
37 | gt_all: "maior que todos"
38 | gteq: "maior que ou igual a"
39 | gteq_any: "maior que ou igual a algum"
40 | gteq_all: "maior que ou igual a todos"
41 | in: "em"
42 | in_any: "em algum"
43 | in_all: "em todos"
44 | not_in: "não em"
45 | not_in_any: "não em algum"
46 | not_in_all: "não em todos"
47 | cont: "contém"
48 | cont_any: "contém algum"
49 | cont_all: "contém todos"
50 | not_cont: "não contém"
51 | not_cont_any: "não contém algum"
52 | not_cont_all: "não contém todos"
53 | start: "começa com"
54 | start_any: "começa com algum"
55 | start_all: "começa com todos"
56 | not_start: "não começa com"
57 | not_start_any: "não começa com algum"
58 | not_start_all: "não começa com algum"
59 | end: "termina com"
60 | end_any: "termina com algum"
61 | end_all: "termina com todos"
62 | not_end: "não termina com"
63 | not_end_any: "não termina com algum"
64 | not_end_all: "não termina com todos"
65 | 'true': "é verdadeiro"
66 | 'false': "é falso"
67 | present: "está presente"
68 | blank: "está em branco"
69 | 'null': "é nulo"
70 | not_null: "não é nulo"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/ro.yml:
--------------------------------------------------------------------------------
1 | ro:
2 | ransack:
3 | search: "caută"
4 | predicate: "predicat"
5 | and: "și"
6 | or: "sau"
7 | any: "oricare"
8 | all: "toate"
9 | combinator: "combinator"
10 | attribute: "atribut"
11 | value: "valoare"
12 | condition: "condiție"
13 | sort: "sortează"
14 | asc: "crescător"
15 | desc: "descrescător"
16 | predicates:
17 | eq: "egal cu"
18 | eq_any: "egal cu unul din"
19 | eq_all: "egal cu toate"
20 | not_eq: "diferit de"
21 | not_eq_any: "diferit de toate"
22 | not_eq_all: "nu este egal cu toate"
23 | matches: "corespunde"
24 | matches_any: "corespunde cu unul din"
25 | matches_all: "corespunde cu toate"
26 | does_not_match: "nu corespunde"
27 | does_not_match_any: "nu corespunde cu nici un"
28 | does_not_match_all: "nu corespunde cu toate"
29 | lt: "mai mic de"
30 | lt_any: "mai mic decât cel puțin unul din"
31 | lt_all: "mai mic decât toate"
32 | lteq: "mai mic sau egal decât"
33 | lteq_any: "mai mic sau egal decât cel puțin unul din"
34 | lteq_all: "mai mic sau egal decât toate"
35 | gt: "mai mare de"
36 | gt_any: "mai mare decât cel puțin unul din"
37 | gt_all: "mai mare decât toate"
38 | gteq: "mai mare sau egal decât"
39 | gteq_any: "mai mare sau egal decât cel puțin unul din"
40 | gteq_all: "mai mare sau egal decât toate"
41 | in: "inclus în"
42 | in_any: "inclus într-unul din"
43 | in_all: "inclus în toate"
44 | not_in: "nu este inclus în"
45 | not_in_any: "nu este inclus într-unul din"
46 | not_in_all: "nu este inclus în toate"
47 | cont: "conține"
48 | cont_any: "conține unul din"
49 | cont_all: "conține toate"
50 | not_cont: "nu conține"
51 | not_cont_any: "nu conține unul din"
52 | not_cont_all: "nu conține toate"
53 | start: "începe cu"
54 | start_any: "începe cu unul din"
55 | start_all: "începe cu toate"
56 | not_start: "nu începe"
57 | not_start_any: "nu începe cu unul din"
58 | not_start_all: "nu începe cu toate"
59 | end: "se termină cu"
60 | end_any: "se termină cu unul din"
61 | end_all: "se termină cu toate"
62 | not_end: "nu se termină cu"
63 | not_end_any: "nu se termină cu unul din"
64 | not_end_all: "nu se termină cu toate"
65 | 'true': "este adevărat"
66 | 'false': "este fals"
67 | present: "este prezent"
68 | blank: "este gol"
69 | 'null': "este nul"
70 | not_null: "nu este nul"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/ru.yml:
--------------------------------------------------------------------------------
1 | ru:
2 | ransack:
3 | search: "Поиск"
4 | predicate: "predicate"
5 | and: "и"
6 | or: "или"
7 | any: "любое"
8 | all: "все"
9 | combinator: "combinator"
10 | attribute: "аттрибут"
11 | value: "значение"
12 | condition: "условие"
13 | sort: "сортировка"
14 | asc: "по возрастанию"
15 | desc: "по убыванию"
16 | predicates:
17 | eq: "равный"
18 | eq_any: "равный любому"
19 | eq_all: "равный всем"
20 | not_eq: "не равный любому"
21 | not_eq_any: "не равный любому"
22 | not_eq_all: "не равный всем"
23 | matches: "совпадение"
24 | matches_any: "совпадение любому"
25 | matches_all: "совпадение всем"
26 | does_not_match: "не совпадение"
27 | does_not_match_any: "не совпадение любому"
28 | does_not_match_all: "не совпадение всем"
29 | lt: "меньше чем"
30 | lt_any: "меньше чем любое"
31 | lt_all: "меньше чем все"
32 | lteq: "меньше чем или равен"
33 | lteq_any: "меньше чем или равен любому"
34 | lteq_all: "меньше чем или равен всем"
35 | gt: "больше чем"
36 | gt_any: "больше чем любое"
37 | gt_all: "больше чем все"
38 | gteq: "больше чем или равен"
39 | gteq_any: "больше чем или равен любому"
40 | gteq_all: "больше чем или равен всем"
41 | in: "в"
42 | in_any: "в любом из"
43 | in_all: "во всех"
44 | not_in: "нет в"
45 | not_in_any: "не в каком из"
46 | not_in_all: "нет во всех"
47 | cont: "содержит"
48 | cont_any: "содержит любое"
49 | cont_all: "содержит все"
50 | not_cont: "не содержит"
51 | not_cont_any: "не содержит любое"
52 | not_cont_all: "не содержит все"
53 | start: "начинается с"
54 | start_any: "начинается с любого"
55 | start_all: "начинается с всего"
56 | not_start: "не начинается с"
57 | not_start_any: "не начинается с любого"
58 | not_start_all: "не начинается со всего"
59 | end: "кончается с"
60 | end_any: "кончается с любого"
61 | end_all: "кончается со всего"
62 | not_end: "не кончается с"
63 | not_end_any: "не кончается с любого"
64 | not_end_all: "не кончается со всего"
65 | 'true': "верно"
66 | 'false': "не верно"
67 | present: "настоящее"
68 | blank: "пусто"
69 | 'null': "нулевой"
70 | not_null: "не нулевой"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/sk.yml:
--------------------------------------------------------------------------------
1 | sk:
2 | ransack:
3 | search: "vyhľadávanie"
4 | predicate: "predikát"
5 | and: "a"
6 | or: "alebo"
7 | any: "akýkoľvek"
8 | all: "každý"
9 | combinator: "kombinátor"
10 | attribute: "atribút"
11 | value: "hodnota"
12 | condition: "podmienka"
13 | sort: "poradie"
14 | asc: "vzostupne"
15 | desc: "zostupne"
16 | predicates:
17 | eq: "sa rovná"
18 | eq_any: "sa rovná akémukoľvek"
19 | eq_all: "sa rovná všetkým"
20 | not_eq: "sa nerovná"
21 | not_eq_any: "sa nerovná akémukoľvek"
22 | not_eq_all: "sa nerovná všetkým"
23 | matches: "zodpovedá"
24 | matches_any: "zodpovedá akémukoľvek"
25 | matches_all: "zodpovedá všetkým"
26 | does_not_match: "nezodpovedá"
27 | does_not_match_any: "nezodpovedá akémukoľvek"
28 | does_not_match_all: "nezodpovedá všetkým"
29 | lt: "menší ako"
30 | lt_any: "menší ako akýkoľvek"
31 | lt_all: "menší ako všetky"
32 | lteq: "menší alebo rovný"
33 | lteq_any: "menší alebo rovný akémukoľvek"
34 | lteq_all: "menší alebo rovný všetkým"
35 | gt: "väčší ako"
36 | gt_any: "väčší ako akýkoľvek"
37 | gt_all: "väčší ako všetky"
38 | gteq: "väčší alebo rovný"
39 | gteq_any: "väčší alebo rovný akémukoľvek"
40 | gteq_all: "väčší alebo rovný všetkým"
41 | in: "v"
42 | in_any: "v akejkoľvek"
43 | in_all: "vo všetkých"
44 | not_in: "nie je v"
45 | not_in_any: "nie je v akejkoľvek"
46 | not_in_all: "nie je vo všetkých"
47 | cont: "obsahuje"
48 | cont_any: "obsahuje akúkoľvek"
49 | cont_all: "obsahuje všetky"
50 | not_cont: "neobsahuje"
51 | not_cont_any: "neobsahuje akúkoľvek"
52 | not_cont_all: "neobsahuje všetky"
53 | start: "začína na"
54 | start_any: "začína s akoukoľvek"
55 | start_all: "začína so všetkými"
56 | not_start: "nezačíná s"
57 | not_start_any: "nezačíná s akoukoľvek"
58 | not_start_all: "nezačíná so všetkými"
59 | end: "končí s"
60 | end_any: "končí s akoukoľvek"
61 | end_all: "končí so všetkými"
62 | not_end: "nekončí s"
63 | not_end_any: "nekončí s akoukoľvek"
64 | not_end_all: "nekončí so všetkými"
65 | 'true': "je pravdivé"
66 | 'false': "nie je pravdivé"
67 | present: "je vyplnené"
68 | blank: "je prázdne"
69 | 'null': "je null"
70 | not_null: "nie je null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/sv.yml:
--------------------------------------------------------------------------------
1 | sv:
2 | ransack:
3 | search: "sök"
4 | predicate: "predikat"
5 | and: "och"
6 | or: "eller"
7 | any: "vilken som"
8 | all: "alla"
9 | combinator: "kombinator"
10 | attribute: "attribut"
11 | value: "värde"
12 | condition: "villkor"
13 | sort: "sortera"
14 | asc: "stigande"
15 | desc: "fallande"
16 | predicates:
17 | eq: "lika med"
18 | eq_any: "lika med vilket som"
19 | eq_all: "lika med alla"
20 | not_eq: "inte lika med"
21 | not_eq_any: "inte lika med någon"
22 | not_eq_all: "inte lika med alla"
23 | matches: "matchar"
24 | matches_any: "matchar någon"
25 | matches_all: "matchar alla"
26 | does_not_match: "matchar inte"
27 | does_not_match_any: "matchar inte någon"
28 | does_not_match_all: "matchar inte alla"
29 | lt: "mindre än"
30 | lt_any: "mindre än någon"
31 | lt_all: "mindre än alla"
32 | lteq: "mindre än eller lika med"
33 | lteq_any: "mindre än eller lika med någon"
34 | lteq_all: "mindre än eller lika med alla"
35 | gt: "större än"
36 | gt_any: "större än någon"
37 | gt_all: "större än alla"
38 | gteq: "större än eller lika med"
39 | gteq_any: "större än eller lika med någon"
40 | gteq_all: "större än eller lika med alla"
41 | in: "i"
42 | in_any: "i någon"
43 | in_all: "i alla"
44 | not_in: "inte i"
45 | not_in_any: "inte i någon"
46 | not_in_all: "inte i alla"
47 | cont: "innehåller"
48 | cont_any: "innehåller någon"
49 | cont_all: "innehåller alla"
50 | not_cont: "innehåller inte"
51 | not_cont_any: "innehåller inte någon"
52 | not_cont_all: "innehåller inte alla"
53 | start: "börjar med"
54 | start_any: "börjar med någon"
55 | start_all: "börjar med alla"
56 | not_start: "börjar inte med"
57 | not_start_any: "börjar inte med någon"
58 | not_start_all: "börjar inte med alla"
59 | end: "slutar med"
60 | end_any: "slutar med någon"
61 | end_all: "slutar med alla"
62 | not_end: "slutar inte med"
63 | not_end_any: "slutar inte med någon"
64 | not_end_all: "slutar inte med alla"
65 | 'true': "är sant"
66 | 'false': "är falskt"
67 | present: "existerar"
68 | blank: "är tom"
69 | 'null': "är null"
70 | not_null: "är inte null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/tr.yml:
--------------------------------------------------------------------------------
1 | tr:
2 | ransack:
3 | search: "ara"
4 | predicate: "doğrula"
5 | and: "ve"
6 | or: "veya"
7 | any: "herhangi"
8 | all: "hepsi"
9 | combinator: "birleştirici"
10 | attribute: "nitelik"
11 | value: "değer"
12 | condition: "şart"
13 | sort: "sırala"
14 | asc: "artan"
15 | desc: "azalan"
16 | predicates:
17 | eq: "eşit"
18 | eq_any: "herhangi birine eşit"
19 | eq_all: "hepsine eşit"
20 | not_eq: "eşit değil"
21 | not_eq_any: "herhangi birine eşit değil"
22 | not_eq_all: "hiçbirine eşit değil"
23 | matches: "eşleşen"
24 | matches_any: "herhangi biri ile eşleşen"
25 | matches_all: "hepsi ile eşleşen"
26 | does_not_match: "eşleşmeyen"
27 | does_not_match_any: "herhangi biri ile eşleşmeyen"
28 | does_not_match_all: "hiçbiri ile eşleşmeyen"
29 | lt: "daha küçük"
30 | lt_any: "herhangi birinden küçük"
31 | lt_all: "hepsinden küçük"
32 | lteq: "daha küçük veya eşit"
33 | lteq_any: "daha küçük veya herhangi birine eşit"
34 | lteq_all: "daha küçük veya hepsine eşit"
35 | gt: "daha büyük "
36 | gt_any: "herhangi birinden daha büyük"
37 | gt_all: "hepsinden daha büyük"
38 | gteq: "daha büyük veya eşit"
39 | gteq_any: "daha büyük veya herhangi birine eşit"
40 | gteq_all: "daha büyük veya hepsine eşit"
41 | in: "içinde"
42 | in_any: "herhangi birinde"
43 | in_all: "hepsinde"
44 | not_in: "içinde değil"
45 | not_in_any: "herhangi birinde değil"
46 | not_in_all: "hiçbirinde değil"
47 | cont: "içeren"
48 | cont_any: "herhangi birini içeren"
49 | cont_all: "hepsini içeren"
50 | not_cont: "içermeyen"
51 | not_cont_any: "herhangi birini içermeyen"
52 | not_cont_all: "hiçbirini birini içermeyen"
53 | start: "ile başlayan"
54 | start_any: "herhangi biriyle başlayan"
55 | start_all: "hepsiyle başlayan"
56 | not_start: "ile başlamayan"
57 | not_start_any: "herhangi biriyle başlamayan"
58 | not_start_all: "hiçbiriyle başlamayan"
59 | end: "ile biten"
60 | end_any: "herhangi biriyle biten"
61 | end_all: "hepsi ile biten"
62 | not_end: "ile bitmeyen"
63 | not_end_any: "herhangi biriyle bitmeyen"
64 | not_end_all: "hiçbiriyle bitmeyen"
65 | 'true': "doğru"
66 | 'false': "yanlış"
67 | present: "mevcut"
68 | blank: "boş"
69 | 'null': "geçersiz"
70 | not_null: "geçerli"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/zh-CN.yml:
--------------------------------------------------------------------------------
1 | zh-CN:
2 | ransack:
3 | search: "搜索"
4 | predicate: "基于(predicate)"
5 | and: "并且"
6 | or: "或者"
7 | any: "任意"
8 | all: "所有"
9 | combinator: "条件组合(combinator)"
10 | attribute: "属性"
11 | value: "数值"
12 | condition: "条件"
13 | sort: "排序"
14 | asc: "升序"
15 | desc: "降序"
16 | predicates:
17 | eq: "等于"
18 | eq_any: "等于任意值"
19 | eq_all: "等于所有值"
20 | not_eq: "不等于"
21 | not_eq_any: "不等于任意值"
22 | not_eq_all: "不等于所有值"
23 | matches: "符合"
24 | matches_any: "符合任意条件"
25 | matches_all: "符合所有条件"
26 | does_not_match: "不符合"
27 | does_not_match_any: "符合任意条件"
28 | does_not_match_all: "不符合所有条件"
29 | lt: "小于"
30 | lt_any: "小于任意一个值"
31 | lt_all: "小于所有值"
32 | lteq: "小于等于"
33 | lteq_any: "小于等于任意一个值"
34 | lteq_all: "小于等于所有值"
35 | gt: "大于"
36 | gt_any: "大于任意一个值"
37 | gt_all: "大于所有值"
38 | gteq: "大于等于"
39 | gteq_any: "大于等于任意一个值"
40 | gteq_all: "大于等于所有值"
41 | in: "被包含"
42 | in_any: "被任意值包含"
43 | in_all: "被所有值包含"
44 | not_in: "不被包含"
45 | not_in_any: "不被任意值包含"
46 | not_in_all: "不被所有值包含"
47 | cont: "包含"
48 | cont_any: "包含任意一个值"
49 | cont_all: "包含所有值"
50 | not_cont: "不包含"
51 | not_cont_any: "不包含任意一个值"
52 | not_cont_all: "不包含所有值"
53 | start: "始于"
54 | start_any: "始于任一值"
55 | start_all: "始于任意值"
56 | not_start: "非始于"
57 | not_start_any: "非始于任一值"
58 | not_start_all: "非始于任意值"
59 | end: "止于"
60 | end_any: "止于任一值"
61 | end_all: "止于任意值"
62 | not_end: "非止于"
63 | not_end_any: "非止于任一值"
64 | not_end_all: "非止于任意值"
65 | 'true': "等于true"
66 | 'false': "等于false"
67 | present: "有值"
68 | blank: "为空"
69 | 'null': "是null"
70 | not_null: "不是null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/locale/zh-TW.yml:
--------------------------------------------------------------------------------
1 | zh-TW:
2 | ransack:
3 | search: "搜尋"
4 | predicate: "基於"
5 | and: "而且"
6 | or: "或者"
7 | any: "任何"
8 | all: "所有"
9 | combinator: "條件組合"
10 | attribute: "屬性"
11 | value: "數值"
12 | condition: "條件"
13 | sort: "排序"
14 | asc: "升冪排序"
15 | desc: "降冪排序"
16 | predicates:
17 | eq: "等於"
18 | eq_any: "等於任何一個值"
19 | eq_all: "等於所有值"
20 | not_eq: "不等於"
21 | not_eq_any: "不等於任何一個值"
22 | not_eq_all: "不等於所有值"
23 | matches: "符合"
24 | matches_any: "符合任何一個條件"
25 | matches_all: "符合所有條件"
26 | does_not_match: "不符合"
27 | does_not_match_any: "不符合任何一個條件"
28 | does_not_match_all: "不符合所有條件"
29 | lt: "小於"
30 | lt_any: "小於任何一個值"
31 | lt_all: "小於所有值"
32 | lteq: "小於或等於"
33 | lteq_any: "小於或等於任何一個值"
34 | lteq_all: "小於或等於所有值"
35 | gt: "大於"
36 | gt_any: "大於任何一個值"
37 | gt_all: "大於所有值"
38 | gteq: "大於或等於"
39 | gteq_any: "大於或等於任何一個值"
40 | gteq_all: "大於或等於所有值"
41 | in: "被包含於"
42 | in_any: "被包含於任何一個值"
43 | in_all: "被包含於所有值"
44 | not_in: "不被包含於"
45 | not_in_any: "不被包含於任何一個值"
46 | not_in_all: "不被包含於所有值"
47 | cont: "包含"
48 | cont_any: "包含任何一個值"
49 | cont_all: "包含所有值"
50 | not_cont: "不包含"
51 | not_cont_any: "不包含任何一個值"
52 | not_cont_all: "不包含所有值"
53 | start: "以某個值開始"
54 | start_any: "以任何一個值開始"
55 | start_all: "以所有值開始"
56 | not_start: "不以某個值開始"
57 | not_start_any: "不以任何一值開始"
58 | not_start_all: "不以所有值開始"
59 | end: "以某個值結尾"
60 | end_any: "以任何一個值結尾"
61 | end_all: "以所有值結尾"
62 | not_end: "不以某個值結尾"
63 | not_end_any: "不以任何一個值結尾"
64 | not_end_all: "不以所有值結尾"
65 | 'true': "為真"
66 | 'false': "為假"
67 | present: "有值"
68 | blank: "為空"
69 | 'null': "為 null"
70 | not_null: "不為 null"
71 |
--------------------------------------------------------------------------------
/lib/ransack/naming.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | module Naming
3 |
4 | def self.included(base)
5 | base.extend ClassMethods
6 | end
7 |
8 | def persisted?
9 | false
10 | end
11 |
12 | def to_key
13 | nil
14 | end
15 |
16 | def to_param
17 | nil
18 | end
19 |
20 | def to_model
21 | self
22 | end
23 |
24 | def model_name
25 | self.class.model_name
26 | end
27 | end
28 |
29 | class Name < String
30 | attr_reader :singular, :plural, :element, :collection, :partial_path,
31 | :human, :param_key, :route_key, :i18n_key
32 | alias_method :cache_key, :collection
33 |
34 | def initialize
35 | super(Constants::CAP_SEARCH)
36 | @singular = Constants::SEARCH
37 | @plural = Constants::SEARCHES
38 | @element = Constants::SEARCH
39 | @human = Constants::CAP_SEARCH
40 | @collection = Constants::RANSACK_SLASH_SEARCHES
41 | @partial_path = Constants::RANSACK_SLASH_SEARCHES_SLASH_SEARCH
42 | @param_key = Constants::Q
43 | @route_key = Constants::SEARCHES
44 | @i18n_key = :ransack
45 | end
46 | end
47 |
48 | module ClassMethods
49 | def model_name
50 | @_model_name ||= Name.new
51 | end
52 |
53 | def i18n_scope
54 | :ransack
55 | end
56 | end
57 |
58 | end
59 |
--------------------------------------------------------------------------------
/lib/ransack/nodes/attribute.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | module Nodes
3 | class Attribute < Node
4 | include Bindable
5 |
6 | attr_reader :name, :ransacker_args
7 |
8 | delegate :blank?, :present?, to: :name
9 | delegate :engine, to: :context
10 |
11 | def initialize(context, name = nil, ransacker_args = [])
12 | super(context)
13 | self.name = name unless name.blank?
14 | @ransacker_args = ransacker_args
15 | end
16 |
17 | def name=(name)
18 | @name = name
19 | end
20 |
21 | def valid?
22 | bound? && attr &&
23 | context.klassify(parent).ransackable_attributes(context.auth_object)
24 | .include?(attr_name.split('.').last)
25 | end
26 |
27 | def associated_collection?
28 | parent.respond_to?(:reflection) && parent.reflection.collection?
29 | end
30 |
31 | def type
32 | if ransacker
33 | ransacker.type
34 | else
35 | context.type_for(self)
36 | end
37 | end
38 |
39 | def eql?(other)
40 | self.class == other.class &&
41 | self.name == other.name
42 | end
43 | alias :== :eql?
44 |
45 | def hash
46 | self.name.hash
47 | end
48 |
49 | def persisted?
50 | false
51 | end
52 |
53 | def inspect
54 | "Attribute <#{name}>"
55 | end
56 |
57 | end
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/lib/ransack/nodes/bindable.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | module Nodes
3 | module Bindable
4 |
5 | attr_accessor :parent, :attr_name
6 |
7 | def attr
8 | @attr ||= get_arel_attribute
9 | end
10 | alias :arel_attribute :attr
11 |
12 | def ransacker
13 | klass._ransackers[attr_name]
14 | end
15 |
16 | def klass
17 | @klass ||= context.klassify(parent)
18 | end
19 |
20 | def bound?
21 | attr_name.present? && parent.present?
22 | end
23 |
24 | def reset_binding!
25 | @parent = @attr_name = @attr = @klass = nil
26 | end
27 |
28 | private
29 |
30 | def get_arel_attribute
31 | if ransacker
32 | ransacker.attr_from(self)
33 | else
34 | get_attribute
35 | end
36 | end
37 |
38 | def get_attribute
39 | if is_alias_attribute?
40 | context.table_for(parent)[parent.base_klass.attribute_aliases[attr_name]]
41 | else
42 | context.table_for(parent)[attr_name]
43 | end
44 | end
45 |
46 | def is_alias_attribute?
47 | Ransack::SUPPORTS_ATTRIBUTE_ALIAS &&
48 | parent.base_klass.attribute_aliases.key?(attr_name)
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/lib/ransack/nodes/node.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | module Nodes
3 | class Node
4 | attr_reader :context
5 | delegate :contextualize, to: :context
6 | class_attribute :i18n_words
7 | class_attribute :i18n_aliases
8 | self.i18n_words = []
9 | self.i18n_aliases = {}
10 |
11 | class << self
12 | def i18n_word(*args)
13 | self.i18n_words += args.map(&:to_s)
14 | end
15 |
16 | def i18n_alias(opts = {})
17 | self.i18n_aliases.merge! Hash[opts.map { |k, v| [k.to_s, v.to_s] }]
18 | end
19 | end
20 |
21 | def initialize(context)
22 | @context = context
23 | end
24 |
25 | def translate(key, options = {})
26 | key = i18n_aliases[key.to_s] if i18n_aliases.has_key?(key.to_s)
27 | if i18n_words.include?(key.to_s)
28 | Translate.word(key)
29 | end
30 | end
31 |
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/lib/ransack/nodes/sort.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | module Nodes
3 | class Sort < Node
4 | include Bindable
5 |
6 | attr_reader :name, :dir, :ransacker_args
7 | i18n_word :asc, :desc
8 |
9 | class << self
10 | def extract(context, str)
11 | return if str.blank?
12 | attr, direction = str.split(/\s+/, 2)
13 | self.new(context).build(name: attr, dir: direction)
14 | end
15 | end
16 |
17 | def build(params)
18 | params.with_indifferent_access.each do |key, value|
19 | if key.match(/^(name|dir|ransacker_args)$/)
20 | self.send("#{key}=", value)
21 | end
22 | end
23 |
24 | self
25 | end
26 |
27 | def valid?
28 | bound? && attr &&
29 | context.klassify(parent).ransortable_attributes(context.auth_object)
30 | .include?(attr_name)
31 | end
32 |
33 | def name=(name)
34 | @name = context.ransackable_alias(name) || name
35 | context.bind(self, @name)
36 | end
37 |
38 | def dir=(dir)
39 | dir = dir.downcase if dir
40 | @dir =
41 | if dir == 'asc'.freeze || dir == 'desc'.freeze
42 | dir
43 | else
44 | 'asc'.freeze
45 | end
46 | end
47 |
48 | def ransacker_args=(ransack_args)
49 | @ransacker_args = ransack_args
50 | end
51 |
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/lib/ransack/nodes/value.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | module Nodes
3 | class Value < Node
4 | attr_accessor :value
5 | delegate :present?, :blank?, to: :value
6 |
7 | def initialize(context, value = nil)
8 | super(context)
9 | @value = value
10 | end
11 |
12 | def persisted?
13 | false
14 | end
15 |
16 | def eql?(other)
17 | self.class == other.class && self.value == other.value
18 | end
19 | alias :== :eql?
20 |
21 | def hash
22 | value.hash
23 | end
24 |
25 | def cast(type)
26 | case type
27 | when :date
28 | cast_to_date(value)
29 | when :datetime, :timestamp, :time, :timestamptz
30 | cast_to_time(value)
31 | when :boolean
32 | cast_to_boolean(value)
33 | when :integer
34 | cast_to_integer(value)
35 | when :float
36 | cast_to_float(value)
37 | when :decimal
38 | cast_to_decimal(value)
39 | when :money
40 | cast_to_money(value)
41 | else
42 | cast_to_string(value)
43 | end
44 | end
45 |
46 | def cast_array
47 | if value.is_a?(Array)
48 | cast_to_date(value)
49 | else
50 | value
51 | end
52 | end
53 |
54 | def cast_to_date(val)
55 | if val.respond_to?(:to_date)
56 | val.to_date rescue nil
57 | else
58 | y, m, d = *[val].flatten
59 | m ||= 1
60 | d ||= 1
61 | Date.new(y, m, d) rescue nil
62 | end
63 | end
64 |
65 | def cast_to_time(val)
66 | if val.is_a?(Array)
67 | Time.zone.local(*val) rescue nil
68 | else
69 | unless val.acts_like?(:time)
70 | val = val.is_a?(String) ? Time.zone.parse(val) : val.to_time rescue val
71 | end
72 | val.in_time_zone rescue nil
73 | end
74 | end
75 |
76 | def cast_to_boolean(val)
77 | if Constants::TRUE_VALUES.include?(val)
78 | true
79 | elsif Constants::FALSE_VALUES.include?(val)
80 | false
81 | else
82 | nil
83 | end
84 | end
85 |
86 | def cast_to_string(val)
87 | val.respond_to?(:to_s) ? val.to_s : String.new(val)
88 | end
89 |
90 | def cast_to_integer(val)
91 | val.blank? ? nil : val.to_i
92 | end
93 |
94 | def cast_to_float(val)
95 | val.blank? ? nil : val.to_f
96 | end
97 |
98 | def cast_to_decimal(val)
99 | if val.blank?
100 | nil
101 | elsif val.class == BigDecimal
102 | val
103 | elsif val.respond_to?(:to_d)
104 | val.to_d
105 | else
106 | val.to_s.to_d
107 | end
108 | end
109 |
110 | def cast_to_money(val)
111 | val.blank? ? nil : val.to_f.to_s
112 | end
113 |
114 | def inspect
115 | "Value <#{value}>"
116 | end
117 |
118 | def array_of_arrays?(val)
119 | Array === val && Array === val.first
120 | end
121 | end
122 | end
123 | end
124 |
--------------------------------------------------------------------------------
/lib/ransack/predicate.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | class Predicate
3 | attr_reader :name, :arel_predicate, :type, :formatter, :validator,
4 | :compound, :wants_array, :case_insensitive
5 |
6 | class << self
7 |
8 | def names
9 | Ransack.predicates.keys
10 | end
11 |
12 | def named(name)
13 | Ransack.predicates[(name || Ransack.options[:default_predicate]).to_s]
14 | end
15 |
16 | def detect_and_strip_from_string!(str)
17 | detect_from_string str, chomp: true
18 | end
19 |
20 | def detect_from_string(str, chomp: false)
21 | return unless str
22 |
23 | Ransack.predicates.sorted_names_with_underscores.each do |predicate, underscored|
24 | if str.end_with? underscored
25 | str.chomp! underscored if chomp
26 | return predicate
27 | end
28 | end
29 |
30 | nil
31 | end
32 |
33 | end
34 |
35 | def initialize(opts = {})
36 | @name = opts[:name]
37 | @arel_predicate = opts[:arel_predicate]
38 | @type = opts[:type]
39 | @formatter = opts[:formatter]
40 | @validator = opts[:validator] ||
41 | lambda { |v| v.respond_to?(:empty?) ? !v.empty? : !v.nil? }
42 | @compound = opts[:compound]
43 | @wants_array = opts.fetch(:wants_array,
44 | @compound || Constants::IN_NOT_IN.include?(@arel_predicate))
45 | @case_insensitive = opts[:case_insensitive]
46 | end
47 |
48 | def eql?(other)
49 | self.class == other.class &&
50 | self.name == other.name
51 | end
52 | alias :== :eql?
53 |
54 | def hash
55 | name.hash
56 | end
57 |
58 | def format(val)
59 | if formatter
60 | formatter.call(val)
61 | else
62 | val
63 | end
64 | end
65 |
66 | def validate(vals, type = @type)
67 | vals.any? { |v| validator.call(type ? v.cast(type) : v.value) }
68 | end
69 |
70 | def negative?
71 | @name.include?("not_".freeze)
72 | end
73 |
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/lib/ransack/ransacker.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | class Ransacker
3 |
4 | attr_reader :name, :type, :formatter, :args
5 |
6 | delegate :call, to: :@callable
7 |
8 | def initialize(klass, name, opts = {}, &block)
9 | @klass, @name = klass, name
10 |
11 | @type = opts[:type] || :string
12 | @args = opts[:args] || [:parent]
13 | @formatter = opts[:formatter]
14 | @callable = opts[:callable] || block ||
15 | (@klass.method(name) if @klass.respond_to?(name)) ||
16 | proc { |parent| parent.table[name] }
17 | end
18 |
19 | def attr_from(bindable)
20 | call(*args.map { |arg| bindable.send(arg) })
21 | end
22 |
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/ransack/translate.rb:
--------------------------------------------------------------------------------
1 | require 'i18n'
2 |
3 | I18n.load_path += Dir[
4 | File.join(File.dirname(__FILE__), 'locale'.freeze, '*.yml'.freeze)
5 | ]
6 |
7 | module Ransack
8 | module Translate
9 | class << self
10 | def word(key, options = {})
11 | I18n.translate(:"ransack.#{key}", default: key.to_s)
12 | end
13 |
14 | def predicate(key, options = {})
15 | I18n.translate(:"ransack.predicates.#{key}", default: key.to_s)
16 | end
17 |
18 | def attribute(key, options = {})
19 | unless context = options.delete(:context)
20 | raise ArgumentError, "A context is required to translate attributes"
21 | end
22 |
23 | original_name = key.to_s
24 | base_class = context.klass
25 | base_ancestors = base_class.ancestors.select {
26 | |x| x.respond_to?(:model_name)
27 | }
28 | attributes_str = original_name.dup # will be modified by ⬇
29 | predicate = Predicate.detect_and_strip_from_string!(attributes_str)
30 | attribute_names = attributes_str.split(/_and_|_or_/)
31 | combinator = attributes_str =~ /_and_/ ? :and : :or
32 | defaults = base_ancestors.map do |klass|
33 | "ransack.attributes.#{i18n_key(klass)}.#{original_name}".to_sym
34 | end
35 | defaults << options.delete(:default) if options[:default]
36 |
37 | translated_names = attribute_names.map do |name|
38 | attribute_name(context, name, options[:include_associations])
39 | end
40 |
41 | interpolations = {
42 | attributes: translated_names.join(" #{Translate.word(combinator)} ")
43 | }
44 |
45 | if predicate
46 | defaults << "%{attributes} %{predicate}".freeze
47 | interpolations[:predicate] = Translate.predicate(predicate)
48 | else
49 | defaults << "%{attributes}".freeze
50 | end
51 |
52 | options.reverse_merge! count: 1, default: defaults
53 | I18n.translate(defaults.shift, **options.merge(interpolations))
54 | end
55 |
56 | def association(key, options = {})
57 | unless context = options.delete(:context)
58 | raise ArgumentError, "A context is required to translate associations"
59 | end
60 |
61 | defaults =
62 | if key.blank?
63 | [:"ransack.models.#{i18n_key(context.klass)}",
64 | :"#{context.klass.i18n_scope}.models.#{i18n_key(context.klass)}"]
65 | else
66 | [:"ransack.associations.#{i18n_key(context.klass)}.#{key}"]
67 | end
68 | defaults << context.traverse(key).model_name.human
69 | options = { count: 1, default: defaults }
70 | I18n.translate(defaults.shift, **options)
71 | end
72 |
73 | private
74 |
75 | def attribute_name(context, name, include_associations = nil)
76 | @context, @name = context, name
77 | @assoc_path = context.association_path(name)
78 | @attr_name = @name.sub(/^#{@assoc_path}_/, ''.freeze)
79 | associated_class = @context.traverse(@assoc_path) if @assoc_path.present?
80 | @include_associated = include_associations && associated_class
81 |
82 | defaults = default_attribute_name << fallback_args
83 | options = { count: 1, default: defaults }
84 | interpolations = build_interpolations(associated_class)
85 |
86 | I18n.translate(defaults.shift, **options.merge(interpolations))
87 | end
88 |
89 | def default_attribute_name
90 | ["ransack.attributes.#{i18n_key(@context.klass)}.#{@name}".to_sym]
91 | end
92 |
93 | def fallback_args
94 | if @include_associated
95 | '%{association_name} %{attr_fallback_name}'.freeze
96 | else
97 | '%{attr_fallback_name}'.freeze
98 | end
99 | end
100 |
101 | def build_interpolations(associated_class)
102 | {
103 | attr_fallback_name: attr_fallback_name(associated_class),
104 | association_name: association_name
105 | }.reject { |_, value| value.nil? }
106 | end
107 |
108 | def attr_fallback_name(associated_class)
109 | I18n.t(
110 | :"ransack.attributes.#{fallback_class(associated_class)}.#{@attr_name}",
111 | default: default_interpolation(associated_class)
112 | )
113 | end
114 |
115 | def fallback_class(associated_class)
116 | i18n_key(associated_class || @context.klass)
117 | end
118 |
119 | def association_name
120 | association(@assoc_path, context: @context) if @include_associated
121 | end
122 |
123 | def default_interpolation(associated_class)
124 | [
125 | associated_attribute(associated_class),
126 | ".attributes.#{@attr_name}".to_sym,
127 | @attr_name.humanize
128 | ].flatten
129 | end
130 |
131 | def associated_attribute(associated_class)
132 | if associated_class
133 | translated_attribute(associated_class)
134 | else
135 | translated_ancestor_attributes
136 | end
137 | end
138 |
139 | def translated_attribute(associated_class)
140 | key = "#{associated_class.i18n_scope}.attributes.#{
141 | i18n_key(associated_class)}.#{@attr_name}"
142 | ["#{key}.one".to_sym, key.to_sym]
143 | end
144 |
145 | def translated_ancestor_attributes
146 | @context.klass.ancestors
147 | .select { |ancestor| ancestor.respond_to?(:model_name) }
148 | .map { |ancestor| translated_attribute(ancestor) }
149 | end
150 |
151 | def i18n_key(klass)
152 | klass.model_name.i18n_key
153 | end
154 | end
155 | end
156 | end
157 |
--------------------------------------------------------------------------------
/lib/ransack/version.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | VERSION = '4.3.0'
3 | end
4 |
--------------------------------------------------------------------------------
/lib/ransack/visitor.rb:
--------------------------------------------------------------------------------
1 | module Ransack
2 | class Visitor
3 |
4 | def accept(object)
5 | visit(object)
6 | end
7 |
8 | def can_accept?(object)
9 | respond_to? DISPATCH[object.class]
10 | end
11 |
12 | def visit_Array(object)
13 | object.map { |o| accept(o) }.compact
14 | end
15 |
16 | def visit_Ransack_Nodes_Condition(object)
17 | object.arel_predicate if object.valid?
18 | end
19 |
20 | def visit_Ransack_Nodes_Grouping(object)
21 | if object.combinator == Constants::OR
22 | visit_or(object)
23 | else
24 | visit_and(object)
25 | end
26 | end
27 |
28 | def visit_and(object)
29 | nodes = object.values.map { |o| accept(o) }.compact
30 | return nil unless nodes.size > 0
31 |
32 | if nodes.size > 1
33 | Arel::Nodes::Grouping.new(Arel::Nodes::And.new(nodes))
34 | else
35 | nodes.first
36 | end
37 | end
38 |
39 | def visit_or(object)
40 | nodes = object.values.map { |o| accept(o) }.compact
41 | nodes.inject(&:or)
42 | end
43 |
44 | def quoted?(object)
45 | case object
46 | when Arel::Nodes::SqlLiteral, Bignum, Fixnum
47 | false
48 | else
49 | true
50 | end
51 | end
52 |
53 | def visit(object)
54 | send(DISPATCH[object.class], object)
55 | end
56 |
57 | def visit_Ransack_Nodes_Sort(object)
58 | if object.valid?
59 | if object.attr.is_a?(Arel::Attributes::Attribute)
60 | object.attr.send(object.dir)
61 | else
62 | ordered(object)
63 | end
64 | else
65 | scope_name = :"sort_by_#{object.name}_#{object.dir}"
66 | scope_name if object.context.object.respond_to?(scope_name)
67 | end
68 | end
69 |
70 | DISPATCH = Hash.new do |hash, klass|
71 | hash[klass] = "visit_#{
72 | klass.name.gsub(Constants::TWO_COLONS, Constants::UNDERSCORE)
73 | }"
74 | end
75 |
76 | private
77 |
78 | def ordered(object)
79 | case object.dir
80 | when 'asc'.freeze
81 | Arel::Nodes::Ascending.new(object.attr)
82 | when 'desc'.freeze
83 | Arel::Nodes::Descending.new(object.attr)
84 | end
85 | end
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/ransack.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 |
3 | $:.push File.expand_path("../lib", __FILE__)
4 | require "ransack/version"
5 |
6 | Gem::Specification.new do |s|
7 | s.name = "ransack"
8 | s.version = Ransack::VERSION
9 | s.platform = Gem::Platform::RUBY
10 | s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Sean Carroll", "David Rodríguez"]
11 | s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com", "sfcarroll@gmail.com"]
12 | s.homepage = "https://github.com/activerecord-hackery/ransack"
13 | s.summary = %q{Object-based searching for Active Record.}
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.}
15 | s.required_ruby_version = '>= 3.0'
16 | s.license = 'MIT'
17 |
18 | s.add_dependency 'activerecord', '>= 7.1'
19 | s.add_dependency 'activesupport', '>= 7.1'
20 | s.add_dependency 'i18n'
21 |
22 | s.files = `git ls-files`.split("\n")
23 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25 | s.require_paths = ["lib"]
26 | end
27 |
--------------------------------------------------------------------------------
/spec/blueprints/articles.rb:
--------------------------------------------------------------------------------
1 | Article.blueprint do
2 | person
3 | title
4 | body
5 | end
6 |
--------------------------------------------------------------------------------
/spec/blueprints/comments.rb:
--------------------------------------------------------------------------------
1 | Comment.blueprint do
2 | article
3 | person
4 | body
5 | end
6 |
--------------------------------------------------------------------------------
/spec/blueprints/notes.rb:
--------------------------------------------------------------------------------
1 | Note.blueprint do
2 | note
3 | notable_type { "Article" }
4 | notable_id
5 | end
6 |
--------------------------------------------------------------------------------
/spec/blueprints/people.rb:
--------------------------------------------------------------------------------
1 | Person.blueprint do
2 | name
3 | email { "test@example.com" }
4 | salary
5 | only_sort
6 | only_search
7 | only_admin
8 | end
9 |
--------------------------------------------------------------------------------
/spec/blueprints/tags.rb:
--------------------------------------------------------------------------------
1 | Tag.blueprint do
2 | name { Sham.tag_name }
3 | end
4 |
--------------------------------------------------------------------------------
/spec/console.rb:
--------------------------------------------------------------------------------
1 | Bundler.setup
2 | require 'machinist/active_record'
3 | require 'sham'
4 | require 'faker'
5 | require 'ransack'
6 |
7 | Dir[File.expand_path('../../spec/{helpers,support,blueprints}/*.rb', __FILE__)]
8 | .each do |f|
9 | require f
10 | end
11 |
12 | Sham.define do
13 | name { Faker::Name.name }
14 | title { Faker::Lorem.sentence }
15 | body { Faker::Lorem.paragraph }
16 | salary { |index| 30000 + (index * 1000) }
17 | tag_name { Faker::Lorem.words(number: 3).join(' ') }
18 | note { Faker::Lorem.words(number: 7).join(' ') }
19 | only_admin { Faker::Lorem.words(number: 3).join(' ') }
20 | only_search { Faker::Lorem.words(number: 3).join(' ') }
21 | only_sort { Faker::Lorem.words(number: 3).join(' ') }
22 | notable_id { |id| id }
23 | end
24 |
25 | Schema.create
26 |
--------------------------------------------------------------------------------
/spec/helpers/polyamorous_helper.rb:
--------------------------------------------------------------------------------
1 | module PolyamorousHelper
2 | def new_join_association(reflection, children, klass)
3 | Polyamorous::JoinAssociation.new reflection, children, klass
4 | end
5 |
6 | def new_join_dependency(klass, associations = {})
7 | Polyamorous::JoinDependency.new klass, klass.arel_table, associations, Polyamorous::InnerJoin
8 | end
9 |
10 | def new_join(name, type = Polyamorous::InnerJoin, klass = nil)
11 | Polyamorous::Join.new name, type, klass
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/helpers/ransack_helper.rb:
--------------------------------------------------------------------------------
1 | module RansackHelper
2 | def quote_table_name(table)
3 | ActiveRecord::Base.connection.quote_table_name(table)
4 | end
5 |
6 | def quote_column_name(column)
7 | ActiveRecord::Base.connection.quote_column_name(column)
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/polyamorous/activerecord_compatibility_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module Polyamorous
4 | describe "ActiveRecord Compatibility" do
5 | it 'works with self joins and includes' do
6 | trade_account = Account.create!
7 | Account.create!(trade_account: trade_account)
8 |
9 | accounts = Account.joins(:trade_account).includes(:trade_account, :agent_account)
10 | account = accounts.first
11 |
12 | expect(account.agent_account).to be_nil
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/polyamorous/join_association_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module Polyamorous
4 | describe JoinAssociation do
5 | let(:join_dependency) { new_join_dependency Note, {} }
6 | let(:reflection) { Note.reflect_on_association(:notable) }
7 | let(:parent) { join_dependency.send(:join_root) }
8 | let(:join_association) {
9 | new_join_association(reflection, parent.children, Article)
10 | }
11 |
12 | subject { new_join_association(reflection, parent.children, Person) }
13 |
14 | it 'leaves the original reflection intact for thread safety' do
15 | reflection.instance_variable_set(:@klass, Article)
16 | join_association
17 | .swapping_reflection_klass(reflection, Person) do |new_reflection|
18 | expect(new_reflection.options).not_to equal reflection.options
19 | expect(new_reflection.options).not_to have_key(:polymorphic)
20 | expect(new_reflection.klass).to eq(Person)
21 | expect(reflection.klass).to eq(Article)
22 | end
23 | end
24 |
25 | it 'sets the polymorphic option to true after initializing' do
26 | expect(join_association.reflection.options[:polymorphic]).to be true
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/polyamorous/join_dependency_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module Polyamorous
4 | describe JoinDependency do
5 | context 'with symbol joins' do
6 | subject { new_join_dependency Person, articles: :comments }
7 |
8 | specify { expect(subject.send(:join_root).drop(1).size)
9 | .to eq(2) }
10 | specify { expect(subject.send(:join_root).drop(1).map(&:join_type).uniq)
11 | .to eq [Polyamorous::InnerJoin] }
12 | end
13 |
14 | context 'with has_many :through association' do
15 | subject { new_join_dependency Person, :authored_article_comments }
16 |
17 | specify { expect(subject.send(:join_root).drop(1).size)
18 | .to eq 1 }
19 | specify { expect(subject.send(:join_root).drop(1).first.table_name)
20 | .to eq 'comments' }
21 | end
22 |
23 | context 'with outer join' do
24 | subject { new_join_dependency Person, new_join(:articles, :outer) }
25 |
26 | specify { expect(subject.send(:join_root).drop(1).size)
27 | .to eq 1 }
28 | specify { expect(subject.send(:join_root).drop(1).first.join_type)
29 | .to eq Polyamorous::OuterJoin }
30 | end
31 |
32 | context 'with nested outer joins' do
33 | subject { new_join_dependency Person,
34 | new_join(:articles, :outer) => new_join(:comments, :outer) }
35 |
36 | specify { expect(subject.send(:join_root).drop(1).size)
37 | .to eq 2 }
38 | specify { expect(subject.send(:join_root).drop(1).map(&:join_type))
39 | .to eq [Polyamorous::OuterJoin, Polyamorous::OuterJoin] }
40 | specify { expect(subject.send(:join_root).drop(1).map(&:join_type).uniq)
41 | .to eq [Polyamorous::OuterJoin] }
42 | end
43 |
44 | context 'with polymorphic belongs_to join' do
45 | subject { new_join_dependency Note, new_join(:notable, :inner, Person) }
46 |
47 | specify { expect(subject.send(:join_root).drop(1).size)
48 | .to eq 1 }
49 | specify { expect(subject.send(:join_root).drop(1).first.join_type)
50 | .to eq Polyamorous::InnerJoin }
51 | specify { expect(subject.send(:join_root).drop(1).first.table_name)
52 | .to eq 'people' }
53 | end
54 |
55 | context 'with polymorphic belongs_to join and nested symbol join' do
56 | subject { new_join_dependency Note,
57 | new_join(:notable, :inner, Person) => :comments }
58 |
59 | specify { expect(subject.send(:join_root).drop(1).size)
60 | .to eq 2 }
61 | specify { expect(subject.send(:join_root).drop(1).map(&:join_type).uniq)
62 | .to eq [Polyamorous::InnerJoin] }
63 | specify { expect(subject.send(:join_root).drop(1).first.table_name)
64 | .to eq 'people' }
65 | specify { expect(subject.send(:join_root).drop(1)[1].table_name)
66 | .to eq 'comments' }
67 | end
68 |
69 | context 'with polymorphic belongs_to join and nested join' do
70 | subject { new_join_dependency Note,
71 | new_join(:notable, :outer, Person) => :comments }
72 | specify { expect(subject.send(:join_root).drop(1).size).to eq 2 }
73 | specify { expect(subject.send(:join_root).drop(1).map(&:join_type)).to eq [Polyamorous::OuterJoin, Polyamorous::InnerJoin] }
74 | specify { expect(subject.send(:join_root).drop(1).first.table_name)
75 | .to eq 'people' }
76 | specify { expect(subject.send(:join_root).drop(1)[1].table_name)
77 | .to eq 'comments' }
78 | end
79 | end
80 | end
81 |
--------------------------------------------------------------------------------
/spec/polyamorous/join_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module Polyamorous
4 | describe Join do
5 | it 'is a tree node' do
6 | join = new_join(:articles, :outer)
7 | expect(join).to be_kind_of(TreeNode)
8 | end
9 |
10 | it 'can be added to a tree' do
11 | join = new_join(:articles, :outer)
12 |
13 | tree_hash = {}
14 | join.add_to_tree(tree_hash)
15 |
16 | expect(tree_hash[join]).to be {}
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/ransack/nodes/condition_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module Ransack
4 | module Nodes
5 | describe Condition do
6 | context 'bug report #1245' do
7 | it 'preserves tuple behavior' do
8 | ransack_hash = {
9 | m: 'and',
10 | g: [
11 | { title_type_in: ['["title 1", ""]'] }
12 | ]
13 | }
14 |
15 | sql = Article.ransack(ransack_hash).result.to_sql
16 | expect(sql).to include("IN (('title 1', ''))")
17 | end
18 | end
19 |
20 | context 'with an alias' do
21 | subject {
22 | Condition.extract(
23 | Context.for(Person), 'term_start', Person.first(2).map(&:name)
24 | )
25 | }
26 |
27 | specify { expect(subject.combinator).to eq 'or' }
28 | specify { expect(subject.predicate.name).to eq 'start' }
29 |
30 | it 'converts the alias to the correct attributes' do
31 | expect(subject.attributes.map(&:name)).to eq(['name', 'email'])
32 | end
33 | end
34 |
35 | context 'with multiple values and an _any predicate' do
36 | subject {
37 | Condition.extract(
38 | Context.for(Person), 'name_eq_any', Person.first(2).map(&:name)
39 | )
40 | }
41 |
42 | specify { expect(subject.values.size).to eq(2) }
43 | end
44 |
45 | describe '#negative?' do
46 | let(:context) { Context.for(Person) }
47 | let(:eq) { Condition.extract(context, 'name_eq', 'A') }
48 | let(:not_eq) { Condition.extract(context, 'name_not_eq', 'A') }
49 |
50 | specify { expect(not_eq.negative?).to be true }
51 | specify { expect(eq.negative?).to be false }
52 | end
53 |
54 | context 'with an invalid predicate' do
55 | subject {
56 | Condition.extract(
57 | Context.for(Person), 'name_invalid', Person.first.name
58 | )
59 | }
60 |
61 | context "when ignore_unknown_conditions is false" do
62 | before do
63 | Ransack.configure { |c| c.ignore_unknown_conditions = false }
64 | end
65 |
66 | specify { expect { subject }.to raise_error ArgumentError }
67 | specify { expect { subject }.to raise_error InvalidSearchError }
68 | end
69 |
70 | context "when ignore_unknown_conditions is true" do
71 | before do
72 | Ransack.configure { |c| c.ignore_unknown_conditions = true }
73 | end
74 |
75 | specify { expect(subject).to be_nil }
76 | end
77 | end
78 |
79 | context 'with an empty predicate' do
80 | subject {
81 | Condition.extract(
82 | Context.for(Person), 'full_name', Person.first.name
83 | )
84 | }
85 |
86 | context "when default_predicate = nil" do
87 | before do
88 | Ransack.configure { |c| c.default_predicate = nil }
89 | end
90 |
91 | specify { expect(subject).to be_nil }
92 | end
93 |
94 | context "when default_predicate = 'eq'" do
95 | before do
96 | Ransack.configure { |c| c.default_predicate = 'eq' }
97 | end
98 |
99 | specify { expect(subject).to eq Condition.extract(Context.for(Person), 'full_name_eq', Person.first.name) }
100 | end
101 | end
102 | end
103 | end
104 | end
105 |
--------------------------------------------------------------------------------
/spec/ransack/nodes/grouping_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module Ransack
4 | module Nodes
5 | describe Grouping do
6 | before do
7 | @g = 1
8 | end
9 |
10 | let(:context) { Context.for(Person) }
11 |
12 | subject { described_class.new(context) }
13 |
14 | describe '#attribute_method?' do
15 | context 'for attributes of the context' do
16 | it 'is true' do
17 | expect(subject.attribute_method?('name')).to be true
18 | end
19 |
20 | context "when the attribute contains '_and_'" do
21 | it 'is true' do
22 | expect(subject.attribute_method?('terms_and_conditions')).to be true
23 | end
24 | end
25 |
26 | context "when the attribute contains '_or_'" do
27 | it 'is true' do
28 | expect(subject.attribute_method?('true_or_false')).to be true
29 | end
30 | end
31 |
32 | context "when the attribute ends with '_start'" do
33 | it 'is true' do
34 | expect(subject.attribute_method?('life_start')).to be true
35 | end
36 | end
37 |
38 | context "when the attribute ends with '_end'" do
39 | it 'is true' do
40 | expect(subject.attribute_method?('stop_end')).to be true
41 | end
42 | end
43 | end
44 |
45 | context 'for unknown attributes' do
46 | it 'is false' do
47 | expect(subject.attribute_method?('not_an_attribute')).to be false
48 | end
49 | end
50 | end
51 |
52 | describe '#conditions=' do
53 | context 'when conditions are identical' do
54 | let(:conditions) do
55 | {
56 | '0' => {
57 | 'a' => { '0'=> { 'name' => 'name', 'ransacker_args' => '' } },
58 | 'p' => 'cont',
59 | 'v' => { '0' => { 'value' => 'John' } }
60 | },
61 | '1' => {
62 | 'a' => { '0' => { 'name' => 'name', 'ransacker_args' => '' } },
63 | 'p' => 'cont',
64 | 'v' => { '0' => { 'value' => 'John' } }
65 | }
66 | }
67 | end
68 |
69 | before { subject.conditions = conditions }
70 |
71 | it 'expect duplicates to be removed' do
72 | expect(subject.conditions.count).to eq 1
73 | end
74 | end
75 |
76 | context 'when conditions differ only by ransacker_args' do
77 | let(:conditions) do
78 | {
79 | '0' => {
80 | 'a' => {
81 | '0' => {
82 | 'name' => 'with_arguments',
83 | 'ransacker_args' => [1, 2]
84 | }
85 | },
86 | 'p' => 'eq',
87 | 'v' => { '0' => { 'value' => '10' } }
88 | },
89 | '1' => {
90 | 'a' => {
91 | '0' => {
92 | 'name' => 'with_arguments',
93 | 'ransacker_args' => [3, 4]
94 | }
95 | },
96 | 'p' => 'eq',
97 | 'v' => { '0' => { 'value' => '10' } }
98 | }
99 | }
100 | end
101 |
102 | before { subject.conditions = conditions }
103 |
104 | it 'expect them to be parsed as different and not as duplicates' do
105 | expect(subject.conditions.count).to eq 2
106 | end
107 | end
108 | end
109 | end
110 | end
111 | end
112 |
--------------------------------------------------------------------------------
/spec/ransack/nodes/value_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module Ransack
4 | module Nodes
5 | describe Value do
6 | let(:context) { Context.for(Person) }
7 |
8 | subject do
9 | Value.new(context, raw_value)
10 | end
11 |
12 | context "with a date value" do
13 | let(:raw_value) { "2022-05-23" }
14 |
15 | [:date].each do |type|
16 | it "should cast #{type} correctly" do
17 | result = subject.cast(type)
18 |
19 | expect(result).to be_a_kind_of(Date)
20 | expect(result).to eq(Date.parse(raw_value))
21 | end
22 | end
23 | end
24 |
25 | context "with a timestamp value" do
26 | let(:raw_value) { "2022-05-23 10:40:02 -0400" }
27 |
28 | [:datetime, :timestamp, :time, :timestamptz].each do |type|
29 | it "should cast #{type} correctly" do
30 | result = subject.cast(type)
31 |
32 | expect(result).to be_a_kind_of(Time)
33 | expect(result).to eq(Time.zone.parse(raw_value))
34 | end
35 | end
36 | end
37 |
38 | Constants::TRUE_VALUES.each do |value|
39 | context "with a true boolean value (#{value})" do
40 | let(:raw_value) { value.to_s }
41 |
42 | it "should cast boolean correctly" do
43 | result = subject.cast(:boolean)
44 | expect(result).to eq(true)
45 | end
46 | end
47 | end
48 |
49 | Constants::FALSE_VALUES.each do |value|
50 | context "with a false boolean value (#{value})" do
51 | let(:raw_value) { value.to_s }
52 |
53 | it "should cast boolean correctly" do
54 | result = subject.cast(:boolean)
55 |
56 | expect(result).to eq(false)
57 | end
58 | end
59 | end
60 |
61 | ["12", "101.5"].each do |value|
62 | context "with an integer value (#{value})" do
63 | let(:raw_value) { value }
64 |
65 | it "should cast #{value} to integer correctly" do
66 | result = subject.cast(:integer)
67 |
68 | expect(result).to be_an(Integer)
69 | expect(result).to eq(value.to_i)
70 | end
71 | end
72 | end
73 |
74 | ["12", "101.5"].each do |value|
75 | context "with a float value (#{value})" do
76 | let(:raw_value) { value }
77 |
78 | it "should cast #{value} to float correctly" do
79 | result = subject.cast(:float)
80 |
81 | expect(result).to be_an(Float)
82 | expect(result).to eq(value.to_f)
83 | end
84 | end
85 | end
86 |
87 | ["12", "101.5"].each do |value|
88 | context "with a decimal value (#{value})" do
89 | let(:raw_value) { value }
90 |
91 | it "should cast #{value} to decimal correctly" do
92 | result = subject.cast(:decimal)
93 |
94 | expect(result).to be_a(BigDecimal)
95 | expect(result).to eq(value.to_d)
96 | end
97 | end
98 | end
99 |
100 | ["12", "101.513"].each do |value|
101 | context "with a money value (#{value})" do
102 | let(:raw_value) { value }
103 |
104 | it "should cast #{value} to money correctly" do
105 | result = subject.cast(:money)
106 |
107 | expect(result).to be_a(String)
108 | expect(result).to eq(value.to_f.to_s)
109 | end
110 | end
111 | end
112 | end
113 | end
114 | end
115 |
--------------------------------------------------------------------------------
/spec/ransack/translate_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module Ransack
4 | describe Translate do
5 | describe '.attribute' do
6 | it 'translate namespaced attribute like AR does' do
7 | ar_translation = ::Namespace::Article.human_attribute_name(:title)
8 | ransack_translation = Ransack::Translate.attribute(
9 | :title,
10 | context: ::Namespace::Article.ransack.context
11 | )
12 | expect(ransack_translation).to eq ar_translation
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'ransack'
2 | require 'sham'
3 | require 'faker'
4 | require 'action_controller'
5 | require 'ransack/helpers'
6 | require 'pry'
7 | require 'simplecov'
8 | require 'byebug'
9 | require 'machinist/active_record'
10 |
11 | SimpleCov.start
12 | I18n.enforce_available_locales = false
13 | Time.zone = 'Eastern Time (US & Canada)'
14 | I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'support', '*.yml')]
15 |
16 | Dir[File.expand_path('../{helpers,support,blueprints}/*.rb', __FILE__)]
17 | .each { |f| require f }
18 |
19 | Faker::Config.random = Random.new(0)
20 | Sham.define do
21 | name { Faker::Name.name }
22 | title { Faker::Lorem.sentence }
23 | body { Faker::Lorem.paragraph }
24 | salary { |index| 30000 + (index * 1000) }
25 | tag_name { Faker::Lorem.words(number: 3).join(' ') }
26 | note { Faker::Lorem.words(number: 7).join(' ') }
27 | only_admin { Faker::Lorem.words(number: 3).join(' ') }
28 | only_search { Faker::Lorem.words(number: 3).join(' ') }
29 | only_sort { Faker::Lorem.words(number: 3).join(' ') }
30 | notable_id { |id| id }
31 | end
32 |
33 | RSpec.configure do |config|
34 | config.alias_it_should_behave_like_to :it_has_behavior, 'has behavior'
35 |
36 | config.before(:suite) do
37 | message = "Running Ransack specs with #{
38 | ActiveRecord::Base.connection.adapter_name
39 | }, Active Record #{::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION
40 | } and Ruby #{RUBY_VERSION}"
41 | line = '=' * message.length
42 | puts line, message, line
43 | Schema.create
44 | SubDB::Schema.create
45 | end
46 |
47 | config.before(:all) { Sham.reset(:before_all) }
48 | config.before(:each) { Sham.reset(:before_each) }
49 |
50 | config.include RansackHelper
51 | config.include PolyamorousHelper
52 | end
53 |
54 | RSpec::Matchers.define :be_like do |expected|
55 | match do |actual|
56 | actual.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip ==
57 | expected.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip
58 | end
59 | end
60 |
61 | RSpec::Matchers.define :have_attribute_method do |expected|
62 | match do |actual|
63 | actual.attribute_method?(expected)
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/spec/support/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | attributes:
3 | id: 'id'
4 | ransack:
5 | attributes:
6 | person:
7 | name: Full Name
8 | only_admin: admin uSer Only
9 | salary: wages
10 | awesome: ransack is really awesome
11 | article:
12 | body: maiN BoDy
13 | activerecord:
14 | attributes:
15 | namespace/article:
16 | title: AR Namespaced Title
17 | namespace_article:
18 | title: Old Ransack Namespaced Title
19 |
--------------------------------------------------------------------------------