README.md in active_hash_relation-1.1.0 vs README.md in active_hash_relation-1.2.0

- old
+ new

@@ -1,6 +1,7 @@ # ActiveHashRelation +[ ![Codeship Status for kollegorna/active_hash_relation](https://app.codeship.com/projects/02f08850-cc66-0134-028f-5ad72e690a75/status?branch=master)](https://app.codeship.com/projects/200194) ## Introduction Simple gem that allows you to manipulate ActiveRecord::Relation using JSON. For instance: ```ruby apply_filters(resource, {name: 'RPK', id: [1,2,3,4,5,6,7,8,9], start_date: {leq: "2014-10-19"}, act_status: "ongoing"}) @@ -9,11 +10,11 @@ ```ruby apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9}) ``` or even filter a resource based on it's associations' associations: ```ruby -apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9, areas: {id: 22} }}) +apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9, areas: {or: [{id: 22}, {id: 21}]} }}) ``` and the list could go on.. Basically your whole db is exposed\* there. It's perfect for filtering a collection of resources on APIs. It should be noted that `apply_filters` calls `ActiveHashRelation::FilterApplier` class underneath with the same params. @@ -27,21 +28,23 @@ ## Installation Add this line to your application's Gemfile: - gem 'active_hash_relation' + gem 'active_hash_relation', '~> 1.2.0 And then execute: $ bundle Or install it yourself as: $ gem install active_hash_relation + ## How to use -The gem exposes only one method: `apply_filters(resource, hash_params, include_associations: true, model: nil)`. `resource` is expected to be an ActiveRecord::Relation. +The gem exposes only one method: `apply_filters(resource, hash_params, include_associations: true, model: nil)`. +`resource` is expected to be an ActiveRecord::Relation. That way, you can add your custom filters before passing the `Relation` to `ActiveHashRelation`. In order to use it you have to include ActiveHashRelation module in your class. For instance in a Rails API controller you would do: ```ruby @@ -56,25 +59,44 @@ render json: resources, each_serializer: Api::V1::ResourceSerializer end end ``` +If you **need to enable filtering on scopes**, you need to specify that explicitly from the initializer. For instance: + +```ruby +ActiveHashRelation.configure do |config| + #override default scope when accessing associations + config.use_unscoped = true + #set true to be able to filter scopes (with params) + #please note that unfortunately (:/) rails does not provide any way + #to iterate through scopes so it uses a monkey patch. + #The monkey patch is as gentle as it can be by aliasing the method, adds some + #sugar and calls it, + #You need to run `initialize!` to actually include the required files + config.filter_active_record_scopes = true +end + + #requires monkeyparched scopes, optional if you don't enable them + ActiveHashRelation.initialize! +``` + ## The API ### Columns -For each param, `apply_filters` method will search in the model's (derived from the first param, or explicitly defined as the last param) all the record's column names and associations. (filtering based on scopes are not working at the moment but will be supported soon). For each column, if there is such a param, it will apply the filter based on the column type. The following column types are supported: +For each param, `apply_filters` method will search in the model's (derived from the +first param, or explicitly defined as the last param) all the record's column names +and associations. (filtering based on scopes are not working at the moment but +will be supported soon). For each column, if there is such a param, it will +apply the filter based on the column type. The following column types are supported: -#### Primary -You can apply a filter a column which is a primary key by value or using an array like: -* `{primary_key_column: 5}` -* `{primary_key_column: [1,3,4,5,6,7]}` #### Integer, Float, Decimal, Date, Time or Datetime/Timestamp You can apply an equality filter: * `{example_column: 500}` -or using an array +or using an array (`ActiveRecord` translates that internally to an `IN` query) * `{example_column: [500, 40]}` or using a hash as a value you get more options: * `{example_column: {le: 500}}` * `{example_column: {leq: 500}}` @@ -98,12 +120,18 @@ or using a hash as a value you get more options: * `{example_column: {eq: 'exact value'}}` `#runs: EXAMPLE_COLUMN = 'test'` * `{example_column: {starts_with: 'exac'}}` `#runs: EXAMPLE_COLUMN LIKE 'test%'` * `{example_column: {ends_with: 'alue'}}` `#runs: EXAMPLE_COLUMN LIKE '%test'` -* `{example_column: {like: 'ct_va}}` `#runs: EXAMPLE_COLUMN LIKE '%test%'` +* `{example_column: {like: 'ct_va'}}` `#runs: EXAMPLE_COLUMN LIKE '%test%'` +If you want to filter using `ILIKE` you can pass an `with_ilike` param: + +* `{example_column: {like: 'ct_va', with_ilike: true}}` `#runs: EXAMPLE_COLUMN ILIKE '%test%'` +* `{example_column: {like: 'ct_va', with_ilike: true}}` `#runs: EXAMPLE_COLUMN ILIKE '%test%'` + + **Please note that ILIKE and especially LIKE are quite slow if you have millions of records in the db even with an index.** ### Limit A limit param defines the number of returned resources. For instance: * `{limit: 10}` @@ -127,14 +155,43 @@ ```ruby micropost_filter = Micropost.all.where("CREATED_AT =< ?", '12-9-2014'.to_datetime) User.where(email: 'test@user.com').joins(:microposts).merge(micropost_filter) ``` +### NULL Filter +You can apply null filter for generate query like this `"users.name IS NULL"` or `"users.name IS NOT NULL"` with this following code: +`{ name: { null: true } }` for is null filter and `{ name: { null: false } }` for not null filter. + +this can be used also for relations tables, so you can write like this `{ books: {title: {null: false }} }` + + +### OR Filter +You can apply an SQL `OR` (for ActiveRecord 5+) using the following syntax: +`{or: [{name: 'Filippos'}, {name: 'Vasilis'}]}` + +It will generate: `WHERE ((users.name = 'Filippos') OR (users.name = 'Vasilis'))` + +You can apply an `OR` on associations as well or even nested ones, there isn't much limitation on that. +I suggest you though to take a look on the [tests](spec/tests/or_filter_spec.rb), cause the syntax gets a bit complex after a while ;) + + ### Scopes -Scopes are supported via a tiny monkeypatch in the ActiveRecord's scope class method which holds the name of each scope. Only scopes that don't accept arguments are supported. The rest could also be supported but it wouldn't make much sense.. If you want to filter based on a scope in a model, the scope names should go under `scopes` sub-hash. For instance the following: +**Filtering on scopes is not enabled by default**. + +Scopes are supported via a tiny monkeypatch in the ActiveRecord's scope class method which holds the name of each scope. +The monkey patch is as gentle as it can be: it aliases the method, adds some sugar and executes it. + +Scopes with arguments are also supported but not tested much. Probably they will work fine unless your arguments expect +complex objects. + +If you want to filter based on a scope in a model, the scope names should go under `scopes` sub-hash. For instance the following: * `{ scopes: { planned: true } }` will run the `.planned` scope on the resource. + +* `{scopes: {created_between: [1988, 2018]}}` + +will run the `.created_on(1988, 2018)` scope on the resource. ### Unscoped assotiations If you have a default scope in your models and you have a good reason to keep that, `active_hash_relation` provides an option to override it when filtering associations: ```ruby