README.md in dase-4.1.1 vs README.md in dase-4.2.0

- old
+ new

@@ -1,109 +1,90 @@ [![Build Status](https://secure.travis-ci.org/vovayartsev/dase.png)](http://travis-ci.org/vovayartsev/dase) ## Overview -Dase gem provides 'includes_count_of' method on a relation, which works similar to ActiveRecord's 'includes' method. +Dase gem provides `includes_count_of` method on a relation, which works similar to ActiveRecord's `preload` method and solves [N+1 query problem](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations) when counting records in `has_many` ActiveRecord associations. -![Dase example](https://vovayartsev-home.s3.amazonaws.com/dase-mockup.png) - -Calling 'includes_count_of(:articles)' on a relation object adds 'articles_count' method to each of the authors: -``` - authors = Author.includes(:publisher).includes_count_of(:articles, :conditions => {:year => 2012}) - authors.first.name # => 'Billy' - authors.first.articles_count # => 2 -``` - - -## Installation - -Add this line to your Rails 3.2.x application's Gemfile: - - gem 'dase', "~> 3.2.4" - ## Usage -### Basic usage: +Given this data in the DB: +![Dase example](https://dl.dropboxusercontent.com/u/8560625/dase.png) +and this Rails model definition +```ruby +class Author + has_many :articles +end ``` - class Author - has_many :articles - end - - Author.includes_count_of(:articles).find_each do |author| - puts "#{author.name} has #{author.articles_count} articles published" - end +you can now write this: ``` + authors = Author.includes_count_of(:articles) + billy = authors.first # => #<Author name: 'Billy'> + billy.articles_count # => 2 +``` -### Using :conditions hash -Specify a hash of options which will be passed to the underlying finder -which retrieves the association. Valid keys are: :conditions, :group, :having, :joins, :include +with conditions on associated records (only articles in year 2012) ``` -Author.includes_count_of(:articles, :conditions => {:year => 2012}) # counts only articles in year 2012 + Author.includes_count_of(:articles, where: {year: 2012} ) ``` -### Using scope merging +using lambda syntax (in v4.1 and greater) ``` -class Article - belongs_to :author - scope this_year, lambda { where(:year => 2012) } -end + Author.includes_count_of(:articles, -> { where(year: 2012) } ) +``` -results = Author.includes_count_of(:articles, :only => Article.this_year) -results.first.articles_count # => # number of articles of given Author for year 2012 only +with renamed counter method ``` -This is achieved by merging the association scope with the scope provided as ":only => ..." option. -No additional checks are performed, and providing the association of proper type is solely your responsibility. + Author.includes_count_of(:articles, -> { where(year: 2012) }, as: :number_of_articles_in_2012) +``` - -### Renaming counter column +with multiple associations counted at once ``` -sites = WebSite.includes_count_of(:users, :conditions => {:role => 'admin'}, :as => :admins_count) -sites.each { |site| puts "Site #{site.url} has #{site.admins_count} admin users" } + Author.includes_count_of(:articles, :photos, :tweets) ``` -## Compatibility +## Installation -### Rails versions +| Rails version | Add this to Gemfile | +|---------------|------------------------| +| 3.2.x | gem 'dase', '~> 3.2.0' | +| 4.0.x | ----- N/A ----- | +| 4.1.x | gem 'dase', '~> 4.1.0' | +| 4.2.0.beta2 | gem 'dase', :github => 'vovayartsev/dase', :branch => 'rails-4-2' | -This gem is for Rails 3.2.x . Earlier versions are not supported. +## Under the hood -Note: the Dase gem version number correlates with the Active Record's versions number, -which it has been tested with. -E.g. the latest 3.2.* version of Dase will play nicely with the latest 3.2.* version of Active Record. -Since dase gem is a sort of a "hack", make sure you specified the version number for "dase" gem in your Gemfile. - -### Polymorphic associations and HasManyThrough associations - -Polymorphic associations and HasManyThrough associations support should work, but it is not tested quite well. -Bug reports and pull requests are very welcome. - -### jRuby support - -Not yet - -## How it works - -Here's a pseudo-code that gives an idea on how it works internally +When a relation is "materialized", we run a custom preloader which calculates the hash of counters in a single SQL query like this: ``` counters_hash = Article.where(:year => 2012).count(:group => :author_id) - Author.find_each do |author| - puts "#{author.name} has #{counters_hash[author.id] || 0} articles published" - end ``` +then we add counters to the parent records like this: +``` + define_method(:articles_count) { counters_hash[author.id] || 0 } +``` +## Alternative approaches +Dase calculates counters dynamically every time you make an SQL query. It makes an extra SQL query for each association processed. These alternatives may be more efficient: +* Cache column in the DB - see [counter_culture](https://github.com/magnusvk/counter_culture) gem, or [counter_cache](http://guides.rubyonrails.org/association_basics.html#counter_cache) in Rails Guides. +* Using subquery in SELECT clause, or JOIN+SELECT+GROUP approach, as explained in [that video](http://www.youtube.com/watch?v=rJg3I-leoo4) + ## Name origin The gem is named by the german mathematician [Johann Dase](http://en.wikipedia.org/wiki/Zacharias_Dase), -who was a mental calculator - he could count and multiply numbers very quickly. +who was a [mental calculator](http://en.wikipedia.org/wiki/Mental_calculator) and could add and multiply numbers very quickly. ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/vovayartsev/dase/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/vovayartsev/dase/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + + + +