README.md in active_hash_relation-1.2.0 vs README.md in active_hash_relation-1.4.0
- old
+ new
@@ -1,38 +1,40 @@
# 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)
+ActiveHashRelation is a complete substitute of ActiveRecord::Relation that allows you to run ActiveRecord queries using only regular Hash using a very powerful yet simple API. It was initially built to allow front-end teams to specify from the API exactly what they need withoug bugging the backend developers, eventually emerged into its own little gem.
+
## 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"})
+apply_filters(User.all, {name: 'filippos', created_at: {leq: "2016-10-19"}, role: 'regular', email: {like: 'vasilakis'}})
```
-filter a resource based on it's associations:
+filter a resource based on it's associations using a NOT filter:
```ruby
-apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9})
+apply_filters(Microposts.all, {updated_at: { geq: "2014-11-2 14:25:04"}, user: {not: {email: vasilakisfil@gmail.com}})
```
-or even filter a resource based on it's associations' associations:
+or even filter a resource based on it's associations' associations using an OR filter:
```ruby
-apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9, areas: {or: [{id: 22}, {id: 21}]} }})
+apply_filters(Comments.all, {updated_at: { geq: "2014-11-2 14:25:04"}, user: {id: 9, units: {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.
+and the list could go on.. Basically you can run anything ActiveRecord supports, except from groupping. 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.
-_\*Actually nothing is exposed, but a user could retrieve resources based
+You can also do [__aggregation queries__](#aggregation-queries), like `sum`, `avg`, `min` and `max` on any column.
+
+_\*A user could retrieve resources based
on unknown attributes (attributes not returned from the API) by brute forcing
-which might or might not be a security issue. If you don't like that check
-[whitelisting](#whitelisting)._
+which might or might not be a security issue. If you don't like that check you can specify from the `params` exactly what is allowed and what is not allowed. For more information check: [whitelisting](#whitelisting)._
-*New*! You can now do [__aggregation queries__](#aggregation-queries).
## Installation
Add this line to your application's Gemfile:
- gem 'active_hash_relation', '~> 1.2.0
+ gem 'active_hash_relation', '~> 1.3.0
And then execute:
$ bundle
@@ -59,11 +61,12 @@
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:
+If you **need to enable filtering on scopes**, you need to specify that explicitly from the initializer. Please run:
+`bundle exec rails g active_hash_relation:initialize` which will create an initializer with the following content:
```ruby
ActiveHashRelation.configure do |config|
#override default scope when accessing associations
config.use_unscoped = true
@@ -77,10 +80,11 @@
end
#requires monkeyparched scopes, optional if you don't enable them
ActiveHashRelation.initialize!
```
+If you are not using Rails, just add the code above in your equivelant initialize block.
## 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
@@ -138,13 +142,25 @@
However I would strongly advice you to use a pagination gem like Kaminari, and use `page` and `per_page` params.
### Sorting
+You can apply sorting using the property as the key of the hash and order as the value. For instance:
+* `{sort: {created_at: desc}}`
+
+You can also order by multiple attributes:
+* `{sort: {created_at: desc, microposts_count: :asc}}`
+
+If there is no column named after the property value, sorting is skipped.
+
+#### Deprecated API (will be removed in version 2.0)
You can apply sorting using the `property` and `order` attributes. For instance:
-* `{sort: {property: 'created_at', order: 'desc'}}`
+* `{sort: {property: :created_at, order: :desc}}`
+You can also order by multiple attributes:
+* `{sort: [{property: :created_at, order: :desc}, {property: :created_at, order: :desc}]}`
+
If there is no column named after the property value, sorting is skipped.
### Associations
If the association is a `belongs_to` or `has_one`, then the hash key name must be in singular. If the association is `has_many` the attribute must be in plural reflecting the association type. When you have, in your hash, filters for an association, the sub-hash is passed in the association's model. For instance, let's say a user has many microposts and the following filter is applied (could be through an HTTP GET request on controller's index method):
@@ -172,11 +188,24 @@
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 ;)
+### NOT Filter
+You can apply an SQL `NOT` (for ActiveRecord 4+) using the following syntax:
+`{not: {name: 'Filippos', email: {ends_with: '@gmail.com'}}}`
+
+It will generate: `WHERE (NOT (users.name = 'Filippos')) AND (NOT (users.email LIKE '%@gmail.com'))`
+
+You can apply an `NOT` on associations as well or even nested ones, there isn't much limitation on that.
+I suggest you to also take a look on the [tests](spec/tests/not_filter_spec.rb).
+
+Also I should note that you need to add specific (partial) queries if you don't want
+to have performance issues on tables with millions of rows.
+
+
### Scopes
-**Filtering on scopes is not enabled by default**.
+**Filtering on scopes is not enabled by default. You need to add the initializer mentioned in the beginning of the [How to use](#how-to-use) section.**.
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