Permalink
Browse files

Initial commit

  • Loading branch information...
dipth committed Apr 11, 2011
0 parents commit 4cf78385bd43f4fd516fb8792d79aa75c58fa3a0
Showing with 11,093 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +4 −0 Gemfile
  3. +246 −0 README.textile
  4. +2 −0 Rakefile
  5. +19 −0 heritage.gemspec
  6. +4 −0 heritage_demo/.gitignore
  7. +32 −0 heritage_demo/Gemfile
  8. +79 −0 heritage_demo/Gemfile.lock
  9. +256 −0 heritage_demo/README
  10. +7 −0 heritage_demo/Rakefile
  11. +3 −0 heritage_demo/app/controllers/application_controller.rb
  12. +45 −0 heritage_demo/app/controllers/blog_posts_controller.rb
  13. +45 −0 heritage_demo/app/controllers/image_posts_controller.rb
  14. +19 −0 heritage_demo/app/controllers/posts_controller.rb
  15. +2 −0 heritage_demo/app/helpers/application_helper.rb
  16. +2 −0 heritage_demo/app/helpers/blog_posts_helper.rb
  17. +2 −0 heritage_demo/app/helpers/image_posts_helper.rb
  18. +2 −0 heritage_demo/app/helpers/posts_helper.rb
  19. +9 −0 heritage_demo/app/models/blog_post.rb
  20. +5 −0 heritage_demo/app/models/image_post.rb
  21. +9 −0 heritage_demo/app/models/post.rb
  22. +13 −0 heritage_demo/app/views/blog_posts/_form.html.erb
  23. +2 −0 heritage_demo/app/views/blog_posts/edit.html.erb
  24. +11 −0 heritage_demo/app/views/blog_posts/index.html.erb
  25. +2 −0 heritage_demo/app/views/blog_posts/new.html.erb
  26. +12 −0 heritage_demo/app/views/blog_posts/show.html.erb
  27. +13 −0 heritage_demo/app/views/image_posts/_form.html.erb
  28. +2 −0 heritage_demo/app/views/image_posts/edit.html.erb
  29. +11 −0 heritage_demo/app/views/image_posts/index.html.erb
  30. +2 −0 heritage_demo/app/views/image_posts/new.html.erb
  31. +12 −0 heritage_demo/app/views/image_posts/show.html.erb
  32. +14 −0 heritage_demo/app/views/layouts/application.html.erb
  33. +9 −0 heritage_demo/app/views/posts/index.html.erb
  34. +4 −0 heritage_demo/config.ru
  35. +42 −0 heritage_demo/config/application.rb
  36. +6 −0 heritage_demo/config/boot.rb
  37. +22 −0 heritage_demo/config/database.yml
  38. +5 −0 heritage_demo/config/environment.rb
  39. +26 −0 heritage_demo/config/environments/development.rb
  40. +49 −0 heritage_demo/config/environments/production.rb
  41. +35 −0 heritage_demo/config/environments/test.rb
  42. +7 −0 heritage_demo/config/initializers/backtrace_silencers.rb
  43. +10 −0 heritage_demo/config/initializers/inflections.rb
  44. +5 −0 heritage_demo/config/initializers/mime_types.rb
  45. +7 −0 heritage_demo/config/initializers/secret_token.rb
  46. +8 −0 heritage_demo/config/initializers/session_store.rb
  47. +5 −0 heritage_demo/config/locales/en.yml
  48. +62 −0 heritage_demo/config/routes.rb
  49. +15 −0 heritage_demo/db/migrate/20110411095519_create_posts.rb
  50. +11 −0 heritage_demo/db/migrate/20110411095612_create_blog_posts.rb
  51. +11 −0 heritage_demo/db/migrate/20110411095655_create_image_posts.rb
  52. +45 −0 heritage_demo/db/schema.rb
  53. +7 −0 heritage_demo/db/seeds.rb
  54. +2 −0 heritage_demo/doc/README_FOR_APP
  55. 0 heritage_demo/lib/tasks/.gitkeep
  56. +26 −0 heritage_demo/public/404.html
  57. +26 −0 heritage_demo/public/422.html
  58. +26 −0 heritage_demo/public/500.html
  59. 0 heritage_demo/public/favicon.ico
  60. BIN heritage_demo/public/images/rails.png
  61. +239 −0 heritage_demo/public/index.html
  62. +2 −0 heritage_demo/public/javascripts/application.js
  63. +965 −0 heritage_demo/public/javascripts/controls.js
  64. +974 −0 heritage_demo/public/javascripts/dragdrop.js
  65. +1,123 −0 heritage_demo/public/javascripts/effects.js
  66. +6,001 −0 heritage_demo/public/javascripts/prototype.js
  67. +191 −0 heritage_demo/public/javascripts/rails.js
  68. +5 −0 heritage_demo/public/robots.txt
  69. 0 heritage_demo/public/stylesheets/.gitkeep
  70. +6 −0 heritage_demo/script/rails
  71. +9 −0 heritage_demo/test/fixtures/blog_posts.yml
  72. +9 −0 heritage_demo/test/fixtures/image_posts.yml
  73. +11 −0 heritage_demo/test/fixtures/posts.yml
  74. +8 −0 heritage_demo/test/functional/blog_posts_controller_test.rb
  75. +8 −0 heritage_demo/test/functional/image_posts_controller_test.rb
  76. +8 −0 heritage_demo/test/functional/posts_controller_test.rb
  77. +9 −0 heritage_demo/test/performance/browsing_test.rb
  78. +13 −0 heritage_demo/test/test_helper.rb
  79. +8 −0 heritage_demo/test/unit/blog_post_test.rb
  80. +4 −0 heritage_demo/test/unit/helpers/blog_posts_helper_test.rb
  81. +4 −0 heritage_demo/test/unit/helpers/image_posts_helper_test.rb
  82. +4 −0 heritage_demo/test/unit/helpers/posts_helper_test.rb
  83. +8 −0 heritage_demo/test/unit/image_post_test.rb
  84. +8 −0 heritage_demo/test/unit/post_test.rb
  85. 0 heritage_demo/vendor/plugins/.gitkeep
  86. +5 −0 lib/heritage.rb
  87. +57 −0 lib/heritage/active_record/acts_as_heir.rb
  88. +31 −0 lib/heritage/active_record/acts_as_predecessor.rb
  89. +20 −0 lib/heritage/railtie.rb
  90. +3 −0 lib/heritage/version.rb
@@ -0,0 +1,3 @@
+pkg/*
+*.gem
+.bundle
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in heritage.gemspec
+gemspec
@@ -0,0 +1,246 @@
+h1. Heritage
+
+Heritage is a gem that implements Multiple Table Inheritance for ActiveRecord models.
+
+h2. Compatability
+
+Heritage has only been tested with Rails 3
+
+h2. Installation
+
+Simply add Heritage to your Gemfile and bundle it up:
+
+<pre>
+ gem 'heritage'
+</pre>
+
+h2. Usage
+
+Heritage works by assigning one model as your @predecessor@, and one or more other models as it's @heir@.
+The predecessor is the parent of it's heirs, and thereby implicitly gives it's heirs access to it's columns, and optionally exposing methods to them.
+
+To mark a model as predecessor, simply use the @acts_as_predecessor@ class-method:
+
+<pre>
+ class Post < ActiveRecord::Base
+ acts_as_predecessor
+ end
+</pre>
+
+To mark a model as heir, simply use the @acts_as_heir_of@ class-method, passing a symbol to the model that is to be the heirs predecessor.
+
+<pre>
+ class BlogPost < ActiveRecord::Base
+ acts_as_heir_of :post
+ end
+</pre>
+
+This takes care of the model configuration. We however need to add two extra columns to the Posts table.
+We need a @heir_id@ column of type @integer@ and a @heir_type@ column of type @string@.
+
+<pre>
+ class CreatePosts < ActiveRecord::Migration
+ def self.up
+ create_table :posts do |t|
+ t.integer :heir_id
+ t.string :heir_type
+ t.string :title
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :posts
+ end
+ end
+
+ class CreateBlogPosts < ActiveRecord::Migration
+ def self.up
+ create_table :blog_posts do |t|
+ t.text :body
+ end
+ end
+
+ def self.down
+ drop_table :blog_posts
+ end
+ end
+<end>
+
+When this is done and the database is migrated, we can begin using the models.
+
+h2. Creating new instances
+
+Now we can simply call the following to create a new @BlogPost@
+
+<pre>
+ blog_post = BlogPost.create(:title => "Wow", :body => "That's a nice blog post!")
+</pre>
+
+Notice that the @title@ attribute belongs to the @Post@ model, and the @body@ attribute belongs to the @BlogPost@ model.
+
+h2. Attributes
+
+We can directly access the @title@ attribute through @BlogPost@ and even change it's value
+
+<pre>
+ blog_post.title # "Wow"
+ blog_post.title = "Oh boy!"
+ blog_post.save!
+ blog_post.title # "Oh boy!"
+</pre>
+
+We can also update attributes like normal through @update_attributes@
+
+<pre>
+ blog_post.update_attributes(:title => "Hubba Hubba", :body => "Nice blog post!")
+ blog_post.title # "Hubba Hubba"
+ blog_post.body # "Nice blog post!"
+</pre>
+
+h2. Methods
+
+If we want to expose some methods from our predecessor model to it's heirs, we can do so when calling the @acts_as_predecessor@ class-method
+
+<pre>
+ class Post < ActiveRecord::Base
+
+ acts_as_predecessor :exposes => :hello
+
+ def hello
+ "Hi there!"
+ end
+
+ end
+</pre>
+
+Now all heirs of @Post@ will have a hello-method, which we can call directly on the heir-model:
+
+<pre>
+ blog_post = BlogPost.create(:title => "I am full", :body => "of methods...")
+ blog_post.hello # "Hi there!"
+</pre>
+
+If you for some reason need to override the method in one of your heir-models, you can simply implement the method, and it will override the method from the predecessor.
+
+<pre>
+ class BlogPost < ActiveRecord::Base
+
+ acts_as_heir_of :post
+
+ def hello
+ "Yo!"
+ end
+
+ end
+</pre>
+
+Calling the @hello@ method on BlogPost will now yield another result:
+
+<pre>
+ blog_post = BlogPost.create(:title => "I have", :body => "my own methods...")
+ blog_post.hello # "Yo!"
+</pre>
+
+If we need to combine the local method in the heir, with the method in the predecessor, we can do so through the @predecessor@ method of the heir model, kinda like you would use @super@.
+
+<pre>
+ class BlogPost < ActiveRecord::Base
+
+ acts_as_heir_of :post
+
+ def hello
+ "Yo! #{predecessor.hello}"
+ end
+
+ end
+</pre>
+
+The result would now be a combination of the local method in the heir, and the method in the predecessor:
+
+<pre>
+ blog_post = BlogPost.create(:title => "I have", :body => "my own methods...")
+ blog_post.hello # "Yo! Hi there!"
+</pre>
+
+h2. Listing and filtering
+
+To list all your wonderful heir models you do as you normally would in ActiveRecord, with one single exception.
+
+Normally you would call something like this, to show all @BlogPosts@
+
+<pre>
+ @posts = BlogPost.all
+</pre>
+
+This however will result in 1 + the number of returned records SQL calls, which is hardly good.
+Instead you need to tell ActiveRecord that it should include the predecessors of the heirs, like so:
+
+<pre>
+ @posts = BlogPost.all(:include => :predecessor)
+</pre>
+
+We now only call the database twice; Once for loading the heirs, and once for loading all referenced predecessors.
+
+Another gotcha is when you need to filter the heirs. You can't directly filter by attributes from the predecessor model.
+So in our example where we have the @title@ attribute in the @Post@ model, we can't do the following:
+
+<pre>
+ @posts = BLogPost.where("title = 'test'")
+</pre>
+
+Instead we need to reference predecessor attributes by the predecessors database-table, like so:
+
+<pre>
+ @posts = BlogPost.where("posts.title = 'test'")
+</pre>
+
+Behind the scenes, heritage works just like a simple ActiveRecord association, so it makes sense.
+
+h2. Timestamps
+
+If all of your heir-models needs timestamps, then you can simply add timestamps to the predecessor model, and omit them from the heir-models.
+Heritage will make sure, that whenever you update your heir-model, the @updated_at@ timestamp in the predecessor model will be updated.
+
+h2. A note on destruction
+
+Heritage depends on the destroy-method of the models, and as such you should always delete predecessor and heir models by calling the @destroy@ method on either, and NEVER by calling the @delete@ or @delete_all@ methods.
+If you absolutely need to do a direct delete in the database, then you need to manually remove the counterpart as well.
+
+For instance, if you manually delete a @BlogPost@ that is heir of @Post@, then you need to first find the right @Post@, then delete the heir and finally delete the predecessor.
+
+h2. Questions, Feedback
+
+Feel free to message me on Github (murui)
+
+h2. Contributing to Heritage
+
+Fork, fix, then send me a pull request.
+
+h2. Credits
+
+Credits goes out to Gerry from TechSpry.com for the idea for this implementation:
+http://techspry.com/ruby_and_rails/multiple-table-inheritance-in-rails-3/
+
+h2. Copyright
+
+Copyright (c) 2011 Benjamin Media A/S
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,2 @@
+require 'bundler'
+Bundler::GemHelper.install_tasks
@@ -0,0 +1,19 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "heritage/version"
+
+Gem::Specification.new do |s|
+ s.name = "heritage"
+ s.version = Heritage::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["Thomas Dippel"]
+ s.email = ["thomasdi@benjamin.dk"]
+ s.homepage = "http://rubygems.org/gems/heritage"
+ s.summary = %q{A gem for implementing multiple table inheritance in rails 3}
+ s.description = %q{A gem for implementing multiple table inheritance in rails 3}
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+end
@@ -0,0 +1,4 @@
+.bundle
+db/*.sqlite3
+log/*.log
+tmp/
@@ -0,0 +1,32 @@
+source 'http://rubygems.org'
+
+gem 'rails', '3.0.6'
+
+# Bundle edge Rails instead:
+# gem 'rails', :git => 'git://github.com/rails/rails.git'
+
+gem 'sqlite3'
+gem "heritage", :path => "~/Projects/benjamin/heritage"
+
+# Use unicorn as the web server
+# gem 'unicorn'
+
+# Deploy with Capistrano
+# gem 'capistrano'
+
+# To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+)
+# gem 'ruby-debug'
+# gem 'ruby-debug19', :require => 'ruby-debug'
+
+# Bundle the extra gems:
+# gem 'bj'
+# gem 'nokogiri'
+# gem 'sqlite3-ruby', :require => 'sqlite3'
+# gem 'aws-s3', :require => 'aws/s3'
+
+# Bundle gems for the local environment. Make sure to
+# put test-only gems in this group so their generators
+# and rake tasks are available in development mode:
+# group :development, :test do
+# gem 'webrat'
+# end
@@ -0,0 +1,79 @@
+PATH
+ remote: ~/Projects/benjamin/heritage
+ specs:
+ heritage (0.0.1)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ abstract (1.0.0)
+ actionmailer (3.0.6)
+ actionpack (= 3.0.6)
+ mail (~> 2.2.15)
+ actionpack (3.0.6)
+ activemodel (= 3.0.6)
+ activesupport (= 3.0.6)
+ builder (~> 2.1.2)
+ erubis (~> 2.6.6)
+ i18n (~> 0.5.0)
+ rack (~> 1.2.1)
+ rack-mount (~> 0.6.14)
+ rack-test (~> 0.5.7)
+ tzinfo (~> 0.3.23)
+ activemodel (3.0.6)
+ activesupport (= 3.0.6)
+ builder (~> 2.1.2)
+ i18n (~> 0.5.0)
+ activerecord (3.0.6)
+ activemodel (= 3.0.6)
+ activesupport (= 3.0.6)
+ arel (~> 2.0.2)
+ tzinfo (~> 0.3.23)
+ activeresource (3.0.6)
+ activemodel (= 3.0.6)
+ activesupport (= 3.0.6)
+ activesupport (3.0.6)
+ arel (2.0.9)
+ builder (2.1.2)
+ erubis (2.6.6)
+ abstract (>= 1.0.0)
+ i18n (0.5.0)
+ mail (2.2.15)
+ activesupport (>= 2.3.6)
+ i18n (>= 0.4.0)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.16)
+ polyglot (0.3.1)
+ rack (1.2.2)
+ rack-mount (0.6.14)
+ rack (>= 1.0.0)
+ rack-test (0.5.7)
+ rack (>= 1.0)
+ rails (3.0.6)
+ actionmailer (= 3.0.6)
+ actionpack (= 3.0.6)
+ activerecord (= 3.0.6)
+ activeresource (= 3.0.6)
+ activesupport (= 3.0.6)
+ bundler (~> 1.0)
+ railties (= 3.0.6)
+ railties (3.0.6)
+ actionpack (= 3.0.6)
+ activesupport (= 3.0.6)
+ rake (>= 0.8.7)
+ thor (~> 0.14.4)
+ rake (0.8.7)
+ sqlite3 (1.3.3)
+ thor (0.14.6)
+ treetop (1.4.9)
+ polyglot (>= 0.3.1)
+ tzinfo (0.3.26)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ heritage!
+ rails (= 3.0.6)
+ sqlite3
Oops, something went wrong.

0 comments on commit 4cf7838

Please sign in to comment.