{"id":13878952,"url":"https://github.com/infinum/jsonapi-query_builder","last_synced_at":"2025-09-20T00:34:08.439Z","repository":{"id":46601821,"uuid":"276052467","full_name":"infinum/jsonapi-query_builder","owner":"infinum","description":null,"archived":false,"fork":false,"pushed_at":"2025-09-17T22:48:11.000Z","size":110,"stargazers_count":14,"open_issues_count":13,"forks_count":0,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-09-18T00:27:35.101Z","etag":null,"topics":["open-source","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/infinum.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-06-30T09:15:31.000Z","updated_at":"2025-09-11T06:35:00.000Z","dependencies_parsed_at":"2025-07-27T22:07:37.221Z","dependency_job_id":"5d047aeb-9ae1-4515-a21b-955387b1dfad","html_url":"https://github.com/infinum/jsonapi-query_builder","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/infinum/jsonapi-query_builder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinum%2Fjsonapi-query_builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinum%2Fjsonapi-query_builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinum%2Fjsonapi-query_builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinum%2Fjsonapi-query_builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/infinum","download_url":"https://codeload.github.com/infinum/jsonapi-query_builder/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinum%2Fjsonapi-query_builder/sbom","scorecard":{"id":488033,"data":{"date":"2025-08-11","repo":{"name":"github.com/infinum/jsonapi-query_builder","commit":"ab62a6d1bc2c97e170b88db64360792a9f0bbf8f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.7,"checks":[{"name":"Code-Review","score":2,"reason":"Found 5/22 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/lint.yml:1","Warn: no topLevel permission defined: .github/workflows/spec.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/infinum/jsonapi-query_builder/lint.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/lint.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/infinum/jsonapi-query_builder/lint.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/infinum/jsonapi-query_builder/lint.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/lint.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/infinum/jsonapi-query_builder/lint.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/spec.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/infinum/jsonapi-query_builder/spec.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/spec.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/infinum/jsonapi-query_builder/spec.yml/master?enable=pin","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: MIT License: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 14 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"40 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-ch3h-j2vf-95pv","Warn: Project is vulnerable to: GHSA-xp5h-f8jf-rc8q","Warn: Project is vulnerable to: GHSA-3hhc-qp5v-9p2j","Warn: Project is vulnerable to: GHSA-579w-22j4-4749","Warn: Project is vulnerable to: GHSA-76r7-hhxj-r776","Warn: Project is vulnerable to: GHSA-hq7p-j377-6v63","Warn: Project is vulnerable to: GHSA-cr5q-6q9f-rq6q","Warn: Project is vulnerable to: GHSA-j6gc-792m-qgm2","Warn: Project is vulnerable to: GHSA-pj73-v5mw-pm9j","Warn: Project is vulnerable to: GHSA-228g-948r-83gx","Warn: Project is vulnerable to: GHSA-3x8r-x6xp-q4vm","Warn: Project is vulnerable to: GHSA-486f-hjj9-9vhh","Warn: Project is vulnerable to: GHSA-2qc6-mcvw-92cw","Warn: Project is vulnerable to: GHSA-353f-x4gh-cqq8","Warn: Project is vulnerable to: GHSA-59gp-qqm7-cw4j","Warn: Project is vulnerable to: GHSA-5w6v-399v-w3cc","Warn: Project is vulnerable to: GHSA-cgx6-hpwq-fhv5","Warn: Project is vulnerable to: GHSA-crjr-9rc5-ghw8","Warn: Project is vulnerable to: GHSA-fq42-c5rg-92c2","Warn: Project is vulnerable to: GHSA-gx8x-g87m-h5q6","Warn: Project is vulnerable to: GHSA-jc36-42cf-vqwj","Warn: Project is vulnerable to: GHSA-mrxw-mxhj-p664","Warn: Project is vulnerable to: GHSA-pxvg-2qj5-37jq","Warn: Project is vulnerable to: GHSA-r95h-9x8f-r3f7","Warn: Project is vulnerable to: GHSA-v6gp-9mmm-c6p5","Warn: Project is vulnerable to: GHSA-vvfq-8hwr-qm4m","Warn: Project is vulnerable to: GHSA-xc9x-jj77-9p9j","Warn: Project is vulnerable to: GHSA-xh29-r2w5-wx8m","Warn: Project is vulnerable to: GHSA-xxx9-3xcr-gjj3","Warn: Project is vulnerable to: GHSA-5x79-w82f-gw8w","Warn: Project is vulnerable to: GHSA-9h9g-93gc-623h","Warn: Project is vulnerable to: GHSA-mcvf-2q2m-x72m","Warn: Project is vulnerable to: GHSA-pg8v-g4xq-hww9","Warn: Project is vulnerable to: GHSA-rrfc-7g8p-99q8","Warn: Project is vulnerable to: GHSA-2rxp-v6pw-ch6m","Warn: Project is vulnerable to: GHSA-4xqq-m2hx-25v8","Warn: Project is vulnerable to: GHSA-5866-49gr-22v4","Warn: Project is vulnerable to: GHSA-r55c-59qm-vjw6","Warn: Project is vulnerable to: GHSA-vg3r-rm7w-2xgh","Warn: Project is vulnerable to: GHSA-vmwr-mc7x-5vc3"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T18:18:46.192Z","repository_id":46601821,"created_at":"2025-08-19T18:18:46.192Z","updated_at":"2025-08-19T18:18:46.192Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276022596,"owners_count":25571825,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-19T02:00:09.700Z","response_time":108,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["open-source","ruby"],"created_at":"2024-08-06T08:02:05.212Z","updated_at":"2025-09-20T00:34:08.392Z","avatar_url":"https://github.com/infinum.png","language":"Ruby","readme":"# Jsonapi::QueryBuilder ![lint](https://github.com/infinum/jsonapi-query_builder/workflows/lint/badge.svg)![spec](https://github.com/infinum/jsonapi-query_builder/workflows/spec/badge.svg)\n\n`Jsonapi::QueryBuilder` serves the purpose of adding the json api query related SQL conditions to the already scoped\ncollection, usually used in controller index actions.\n\nWith the query builder we can easily define logic for query filters, attributes by which we can sort, and delegate\npagination parameters to the underlying paginator. Included relationships are automatically included via the\n`ActiveRecord::QueryMethods#includes`, to prevent N+1 query problems.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'jsonapi-query_builder'\n```\n\nAnd then execute:\n\n    $ bundle install\n\nOr install it yourself as:\n\n    $ gem install jsonapi-query_builder\n\n## Usage\n\n```ruby\nclass UserQuery \u003c Jsonapi::QueryBuilder::BaseQuery\n  ## pagination\n  paginator Jsonapi::QueryBuilder::Paginator::Pagy # default paginator\n\n  ## sorting\n  default_sort created_at: :desc\n  sorts_by :last_name\n  sorts_by :first_name, -\u003e(collection, direction) { collection.order(name: direction) }\n  sorts_by :email, EmailSort\n\n  ## filtering\n  filters_by :first_name\n  filters_by :last_name\n  filters_by :email, -\u003e(collection, query) { collection.where('email ilike ?', \"%#{query}%\") }\n  filters_by :type, TypeFilter\n  filters_by :mrn, MrnInMemoryFilter\nend\n\nclass UsersController \u003c ApplicationController\n  def index\n    user_query = UserQuery.new(User, params.to_unsafe_hash)\n\n    render json: user_query.results, status: :ok\n  end\nend\n```\n\nThe query class is initialized using a collection and query parameters. Since query parameters are referenced explicitly\nwe can pass them as an unsafe hash. `Jsonapi::QueryBuilder::BaseQuery` should not be responsible for scoping records\nbased on current user permissions, or for any other type of scoping. It's only responsibility is to support\nthe `json:api` querying. Use `pundit` or similar for policy scoping, custom query objects for other scoping, and then\npass the scoped collection to the `Jsonapi::QueryBuilder::BaseQuery` object.\n\n### Pagination\n\nPagination support is configurable using the `paginator` method to define the paginator. It defaults to the `Pagy`\npaginator, a lightweight and fast paginator. Other paginators currently supported are `Kaminari`, `PagyCountless` and\nan implementation of keyset pagination. Before using these paginators we need to explicitly require the gems in our\nGemfile and the paginator file in question. Additionally one can implement it's own paginator by inheriting\nfrom `Jsonapi::QueryBuilder::Paginator::BasePaginator`. The minimum required implementation is a `#paginate` method that\nreceives page params and returns a page of the collection. It can return the pagination details as the second item of\nthe returned array, that can be used in the serializer for pagination metadata.\n\n#### Using the Kaminari Paginator\n\n```ruby\nrequire \"jsonapi/query_builder/paginator/kaminari\"\n\npaginator Jsonapi::QueryBuilder::Paginator::Kaminari\n```\n\n#### Using the Keyset Paginator\n\n```ruby\nrequire \"jsonapi/query_builder/paginator/keyset\"\n\npaginator Jsonapi::QueryBuilder::Paginator::Keyset\n```\n\n#### Using the Pagy Countless Paginator\n\n```ruby\nrequire \"jsonapi/query_builder/paginator/pagy_countless\"\n\npaginator Jsonapi::QueryBuilder::Paginator::PagyCountless\n```\n\n### Sorting\n\n#### Ensuring deterministic results\n\nSorting has a fallback to an unique attribute which defaults to the `id` attribute. This ensures deterministic paginated\ncollection responses. You can override the `unique_sort_attribute` in the query object.\n\n```ruby\n# set the unique sort attribute\nunique_sort_attribute :email\n# use compound unique sort attributes\nunique_sort_attributes :created_at, :email\n````\n\n#### Default sort options\n\nThe `default_sort` can be set to sort by any field like `created_at` timestamp or similar. It is only used if no sort\nparameter is set, unlike the `unique_sort_attribute` which is always appended as the last sort attribute. The parameters\nare passed directly to the underlying active record relation, so the usual ordering options are possible. It is also\npossible to define the default sort with a lambda or by passing a sort object.\n\n```ruby\ndefault_sort :created_at\n# or\ndefault_sort created_at: :desc\n# or\ndefault_sort -\u003e(collection) { collection.order(created_at: :desc) }\n# or\ndefault_sort SortObject\n```\n\n#### Enabling simple sorting for attributes\n\n`sorts_by` denotes which attributes can be used for sorting. Sorting parameters are usually parsed from the\n`json:api` sort query parameter in the order they are given. So `sort=-first_name,email` would translate to\n`{ first_name: :desc, email: :asc }`\n\n```ruby\nsorts_by :first_name\nsorts_by :email\n```\n\n#### Sorting with lambdas\n\n`sorts_by` also supports passing a lambda to implement a custom order or reorder function. The parameters passed to the\nlamdba are collection and the direction of the order, which is either `:desc` or `:asc`.\n\n```ruby\nsorts_by :first_name, -\u003e(collection, direction) { collection.order(name: direction) }\n```\n\n#### Sorting with sort classes\n\nBut since we're devout followers of the SOLID principles, we can define a sort class that responds to `#results` method,\nwhich returns the sorted collection. Under the hood the sort class is initialized with the current scope and the\ndirection parameter.\n\n#### Dynamic sorting (prefix-based)\n\nSometimes you want to allow sorting by a dynamic subset of attributes that share a common prefix (e.g., JSON/JSONB keys, translated columns, join records). You can register a dynamic sort by attribute prefix using `dynamically_sorts_by`.\n\n- The configured prefix is matched against each parsed sort attribute.\n- The prefix is stripped and only the dynamic part is passed to your sort handler.\n- You can provide either a lambda/proc or a class. The callable receives `(collection, dynamic_attribute, direction)`.\n\nExample with a lambda (PostgreSQL JSONB text value):\n\n```ruby\n# Allows sorting by any key in the `data` column: e.g. sort=-data.name,data.created_at\ndynamically_sorts_by :'data.', -\u003e(collection, attribute, direction) {\n  # attribute is the part after the prefix, e.g. \"name\" or \"created_at\"\n  quoted_attribute = ActiveRecord::Base.connection.quote(attribute)\n  collection.order(Arel.sql(\"(data-\u003e\u003e#{quoted_attribute}) #{direction}\"))\n}\n```\n\nExample with a sort class (PostgreSQL JSONB text value):\n\n```ruby\nclass DataSort \u003c Jsonapi::QueryBuilder::DynamicSort\n  def results\n    quoted_attribute = ActiveRecord::Base.connection.quote(dynamic_attribute)\n    collection.order(Arel.sql(\"(data-\u003e\u003e#{quoted_attribute}) #{direction}\"))\n  end\nend\n\ndynamically_sorts_by :'data.', DataSort\n```\n\n### Filtering\n\n#### Simple exact match filters\n\n```ruby\nfilters_by :first_name\n# =\u003e collection.where(first_name: params.dig(:filter, :first_name)) if params.dig(:filter, :first_name).present?\n```\n\n#### Lambda as a filter\n\n```ruby\nfilters_by :email, -\u003e(collection, query) { collection.where('email ilike ?', \"%#{query}%\") }\n# =\u003e collection.where('email ilike ?', \"%#{params.dig(:filter, :email)}%\") if params.dig(:filter, :email).present?\n```\n\n#### Filter classes\n\nWe can define a filter class that responds to `#results` method, which returns the filtered collection results. Under\nthe hood the filter class is initialized with the current scope and the query parameter. However, if the object responds\nto a `call` method it sends the current scope and the query parameter to that instead. This is great if you're using\nquery objects for ActiveRecord scopes, you can easily use them to filter with as well.\n\n```ruby\nfilters_by :type, TypeFilter\n```\n\nThe filter class could look something like\n\n```ruby\n\nclass TypeFilter \u003c Jsonapi::QueryBuilder::BaseFilter\n  def results\n    collection.where(type: query.split(','))\n  end\nend\n```\n\nSometimes you need to perform in-memory filtering, for example when database attributes are encrypted. In that case,\nthose filters should be applied last, the order of definition in the query object matters.\n\n```ruby\n\nclass MrnFilter \u003c Jsonapi::QueryBuilder::BaseFilter\n  def results\n    collection.select { |record| /#{query}/.match?(record.mrn) }\n  end\nend\n```\n\n#### Additional Options\n\nYou can override the filter query parameter name by passing the `query_parameter` option.\n\n```ruby\nfilters_by :first_name, query_parameter: 'name'\n# =\u003e collection.where(first_name: params.dig(:filter, :name)) if params.dig(:filter, :name).present?\n```\n\n`allow_nil` option changes the filter conditional to allow explicit checks for an attribute null value.\n\n```ruby\nfilters_by :first_name, allow_nil: true\n# =\u003e collection.where(first_name: params.dig(:filter, :first_name)) if params[:filter]\u0026.key?(:first_name)\n```\n\nThe conditional when the filter is applied can also be defined explicitly. Note that these options override the\n`allow_nil` option, as the condition if defined explicitly and you should handle `nil` explicitly as well.\n\n```ruby\nfilters_by :first_name, if: -\u003e(query) { query.length \u003e= 2 }\n# =\u003e collection.where(first_name: params.dig(:filter, :first_name)) if params.dig(:filter, :first_name) \u003e= 2\nfilters_by :first_name, unless: -\u003e(query) { query.length \u003c 2 }\n# =\u003e collection.where(first_name: params.dig(:filter, :first_name)) unless params.dig(:filter, :first_name) \u003c 2\n```\n\nWhen you're using a filter class you can pass a symbol to the `:if` and `:unless` options which invokes the method on\nthe filter class.\n\n```ruby\nfilters_by :type, TypeFilter, if: :correct_type?\n# =\u003e type_filter = TypeFilter.new(collection, query); type_filter.results if type_filter.correct_type?\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can\nalso run `bin/console` for an interactive prompt that will allow you to experiment.\n\nWe're using `standardrb` and `lefthook`. You can install lefthook hooks via `lefthook install`. It will run linters and\nstandardrb checks before commits, and a bundle audit + whole spec suite before push.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the\nversion number in `version.rb`, and then run `LEFTHOOK=0 bundle exec rake release`, which will create a git tag for the\nversion, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/infinum/jsonapi-query_builder.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinum%2Fjsonapi-query_builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finfinum%2Fjsonapi-query_builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinum%2Fjsonapi-query_builder/lists"}