README.md in ii_finder-1.1.2 vs README.md in ii_finder-1.2.0

- old
+ new

@@ -22,18 +22,18 @@ ## Usage Prepare model: ```ruby -class User < ActiveRecord::Base +class Item < ActiveRecord::Base end ``` Prepare finder: ```ruby -class UsersFinder < IIFinder::Base +class ItemsFinder < IIFinder::Base parameters :name def name(value) @relation.where(name: value) end @@ -41,58 +41,58 @@ ``` Use finder as follows: ```ruby -UsersFinder.call(name: 'NAME').to_sql -#=> SELECT "users".* FROM "users" WHERE "users"."name" = 'NAME' +ItemsFinder.call(name: 'NAME').to_sql +#=> SELECT "items".* FROM "items" WHERE "items"."name" = 'NAME' ``` You can also specify relation as first argument: ```ruby -UsersFinder.call(User.where(id: [1, 2, 3]), name: 'NAME').to_sql -#=> SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 2, 3) AND "users"."name" = 'NAME' +ItemsFinder.call(Item.where(id: [1, 2, 3]), name: 'NAME').to_sql +#=> SELECT "items".* FROM "items" WHERE "items"."id" IN (1, 2, 3) AND "items"."name" = 'NAME' ``` ### Finder Finder loops keys of `parameters` and call the corresponding method with value of parameter as argument. Finder method will not be called when the value of parameter is blank. If you want to receive such value, set `allow_blank` as follows: ```ruby -class UsersFinder < IIFinder::Base +class ItemsFinder < IIFinder::Base parameters :name, allow_blank: true def name(value) @relation.where(name: value) end end -UsersFinder.call(name: '').to_sql -#=> SELECT "users".* FROM "users" WHERE "users"."name" = '' +ItemsFinder.call(name: '').to_sql +#=> SELECT "items".* FROM "items" WHERE "items"."name" = '' ``` Finder has following attributes: ```ruby -class UsersFinder < IIFinder::Base +class ItemsFinder < IIFinder::Base parameters :name def name(value) puts "relation: #{@relation}" puts "criteria: #{@criteria}" puts "model: #{@model}" puts "table: #{@table}" end end -UsersFinder.call(name: 'NAME') -#=> relation: #<User::ActiveRecord_Relation: +ItemsFinder.call(name: 'NAME') +#=> relation: #<Item::ActiveRecord_Relation: # criteria: {:name=>'NAME'} -# model: User +# model: Item # table: #<Arel::Table ...> ``` Return value of each finder method is merged into `@relation` if it is a kind of `ActiveRecord::Relation`. In case you want to merge by yourself, set configuration as follows: @@ -100,20 +100,20 @@ ```ruby IIFinder.configure do |config| config.merge_relation = false end -class UsersFinder < IIFinder::Base +class ItemsFinder < IIFinder::Base parameters :name def name(value) @relation = @relation.where(name: value) end end -UsersFinder.call(name: 'NAME').to_sql -#=> SELECT "users".* FROM "users" WHERE "users"."name" = 'NAME' +ItemsFinder.call(name: 'NAME').to_sql +#=> SELECT "items".* FROM "items" WHERE "items"."name" = 'NAME' ``` #### Callbacks Following callbacks are available. @@ -123,70 +123,115 @@ * `after_call` For example: ```ruby -class UsersFinder < IIFinder::Base +class ItemsFinder < IIFinder::Base after_call :default_order def default_order @relation = @relation.order(id: :desc) end end -UsersFinder.call.to_sql -#=> SELECT "users".* FROM "users" ORDER BY "users"."id" DESC +ItemsFinder.call.to_sql +#=> SELECT "items".* FROM "items" ORDER BY "items"."id" DESC ``` Note that finder does not handle the return value of callback. When you want to update `@relation` in the callback, reassign `@relation` or use methods like `where!` or `order!`. +#### Chain + +You can chain multiple finders by using `chain`. For example: + +```ruby +class NameFinder < IIFinder::Base + parameters :name + + def name(value) + @relation.where(name: value) + end +end + +class AgeFinder < IIFinder::Base + parameters :age + + def age(value) + @relation.where(age: value) + end +end + +class ItemsFinder < IIFinder::Base + chain NameFinder, AgeFinder +end + +ItemsFinder.call(Item.all, name: 'name', age: 10).to_sql +#=> SELECT "items".* FROM "items" WHERE "items"."name" = 'name' AND "items"."age" = 10 +``` + +You can also use method or block to find finder class dynamically: + +```ruby +class ItemFinder < IIFinder::Base + chain -> { NameFinder } +end + +class ItemFinder < IIFinder::Base + chain :chain_finder + + def chain_finder + NameFinder + end +end +``` + ### Lookup for model Finder lookups related model by its class name when the first argument of `call` is not relation. So the name of finder class should be composed of the name of model class. For example: ```ruby -class User < ActiveRecord::Base +class Item < ActiveRecord::Base end -class UsersFinder < IIFinder::Base +class ItemsFinder < IIFinder::Base end -IIFinder::Base.lookup(UsersFinder) -#=> User +IIFinder::Base.lookup(ItemsFinder) +#=> Item ``` Note that superclass of finder is also looked up until related model is found. ```ruby -class User < ActiveRecord::Base +class Item < ActiveRecord::Base end -class UsersFinder < IIFinder::Base +class ItemsFinder < IIFinder::Base end -class InheritedUsersFinder < UsersFinder +class InheritedItemsFinder < ItemsFinder end -IIFinder::Base.lookup(InheritedUsersFinder) -#=> User +IIFinder::Base.lookup(InheritedItemsFinder) +#=> Item ``` ### Scope for model In case you want to call finder from model, include `IIFinder::Scope` into model as follows: ```ruby -class User < ActiveRecord::Base +class Item < ActiveRecord::Base include IIFinder::Scope end -User.finder_scope(name: 'NAME').to_sql -#=> SELECT "users".* FROM "users" WHERE "users"."name" = 'NAME' +Item.finder_scope(name: 'NAME').to_sql +#=> SELECT "items".* FROM "items" WHERE "items"."name" = 'NAME' ``` ### Logging Finder supports instrumentation hook supplied by `ActiveSupport::Notifications`. @@ -197,10 +242,10 @@ ``` This subscriber will write logs in debug mode as the following example: ``` -Called UsersFinder with {:id=>1} (Duration: 9.9ms, Allocations: 915) +Called ItemsFinder with {:id=>1} (Duration: 9.9ms, Allocations: 915) ``` ## Contributing Bug reports and pull requests are welcome at https://github.com/kanety/ii_finder.