Permalink
Browse files

Added a :from option. Updated docs. Upgraded version number

  • Loading branch information...
1 parent 403b57f commit 8ca85956dc87d30c03a98f97c50f69c5c4cbcbdb Justin Knowlden committed Mar 24, 2009
Showing with 112 additions and 30 deletions.
  1. +29 −17 README.markdown
  2. +57 −11 lib/load_model.rb
  3. +2 −2 load_model.gemspec
  4. +24 −0 test/functional/from_controller_test.rb
View
@@ -2,8 +2,6 @@
A glorified before_filter that loads an instance of an `ActiveRecord` object as the result of searching for said object against a model defined by a given model name. The value of the HTTP request parameter `:id` will be used as the default lookup value. `LoadModel` will give you the ability to require an instance be found and/or override several other default behaviors.
-Example
-
class SillyFellowController < Application
load_model :silly_fellow
def action
@@ -15,20 +13,14 @@ You can require that a model instance be found for all actions or given actions.
To turn require on for all actions, simply pass *true* to a provided `:require` attribute, like so:
-Example
-
load_model :silly_fellow, :require => true
To turn require on for specific actions, pass an array of action names to `:require`. The model will be loaded for all actions, regardless of whether or not required is provided, but the exception will only be raised when an record is not found for the provided actions.
-Example
-
load_model :silly_fellow, :require => [:show, :update]
To use a different parameter key and model than the default, you can provide the values in the `:paramater_key` and `:class` options (though not necessary to provide them together), like the following:
-Example
-
load_model :foo, :class => :user, :parameter_key => :bar_id
In the above example, `load_model` will assume the parameter_key and the model's primary/foreign key are both the same. For instance, the above example would result in a call like the following:
@@ -37,8 +29,6 @@ In the above example, `load_model` will assume the parameter_key and the model's
If you want to use a different lookup/foreign key than the default, you can also provide that key name using the `:foreign_key` parameter; like so:
-Example
-
load_model :foo, :class => :user, :parameter_key => :bar_id,
:foreign_key => :baz_id
@@ -48,14 +38,12 @@ Which would result in a call similar to the following:
If you want to only use `load_model` for some actions, you can still name them as you would with a `before_filter` using `:only` or `:except`. If you provide an `:only` and an `:except` value. `:only` will always win out over `:except` when there are collisions (i.e. you provide both in the same call)
-Example
-
load_model :foo, :only => [:show]
load_model :bar, :except => [:create]
-Finally, load_model supports a :through option. With :through, you can load a model via the association of an existing loaded model. This is especially useful for RESTful controllers.
+### Through
-Example
+Load Model supports a :through option. With :through, you can load a model via the association of an existing loaded model. This is especially useful for RESTful controllers.
load_model :user, :require => true, :parameter_key => :user_id
load_model :post, :through => :user
@@ -66,8 +54,6 @@ In this example, a @post record will be loaded through the @user record with ess
All of the previously mentioned options still apply (:parameter_key, :foreign_key, :require, :only, and :except) except for the :class option. Meaning you could really mess around!
-Example
-
load_model :user, :require => true, :parameter_key => :user_id
load_model :post, :through => :person, :parameter_key => :foo_id,
:foreign_key => :baz_id
@@ -78,7 +64,33 @@ Would result in a call similar to the following:
Require works as you would expect.
-The only current caveat is that load_model assumes a has_many association exists on the :through model and is named in the pluralized form. In essence, in the above example, there is no way to tell load_model not look for the "posts" association. Perhaps a future change.
+If you would like load_model to not assume a pluralized association name, you can provide the association name with the :association option. Like so:
+
+ class Person < ActiveRecord::Base
+ has_many :blog_postings
+ end
+
+ class PostController < ActionController::Base
+ load_model :post, :through => :person, :assocation => :blog_postings
+ end
+
+### From
+
+Perhaps you don't need to do a subquery on a model's association and you just need to load a model from another's `belongs_to` or `has_one` association. This would be impossible in the above example. Instead, will want to use the :from option. Like so:
+
+ class Post < ActiveRecord::Base
+ belongs_to :user
+ end
+
+ class PostController < ActionController::Base
+ load_model :post
+ load_model :user, :from => :post
+ end
+
+The example is contrived, but you get the point. Essentially, this would do the same as writing the following code:
+
+ @post = Post.find_by_id(params[:id])
+ @user = @post.user
## Installation
View
@@ -78,7 +78,9 @@ module ClassMethods #:nodoc
# load_model :foo, :only => [:show]
# load_model :bar, :except => [:create]
#
- # Finally, load_model supports a :through option. With :through, you can
+ # == Through
+ #
+ # Load Model supports a :through option. With :through, you can
# load a model via the association of an existing loaded model. This is
# especially useful for RESTful controllers.
#
@@ -104,20 +106,48 @@ module ClassMethods #:nodoc
#
# @person.posts.find_by_baz_id(params[:foo_id])
#
- # Require works as you would expect
+ # Require works as you would expect.
+ #
+ # If you would like load_model to not assume a pluralized association
+ # name, you can provide the association name with the :association
+ # option. Like so:
+ #
+ # Example
+ # class Person < ActiveRecord::Base
+ # has_many :blog_postings
+ # end
+ # class PostController < ActionController::Base
+ # load_model :post, :through => :person, :assocation => :blog_postings
+ # end
+ #
+ # == From
#
- # The only current caveat is that load_model assumes a has_many
- # association exists on the :through model and is named in the pluralized
- # form. In essence, in the above example, there is no way to tell
- # load_model not look for the "posts" association. Perhaps a future
- # change.
+ # Perhaps you don't need to do a subquery on a model's association and
+ # you just need to load a model from another's belongs_to or has_one
+ # association. This would be impossible in the above example. Instead,
+ # will want to use the :from option. Like so:
#
+ # Example
+ # class Post < ActiveRecord::Base
+ # belongs_to :user
+ # end
+ # class PostController < ActionController::Base
+ # load_model :post
+ # load_model :user, :from => :post
+ # end
+ #
+ # The example is contrived, but you get the point. Essentially, this
+ # would do the same as writing the following code:
+ #
+ # Example
+ # @post = Post.find_by_id(params[:id])
+ # @user = @post.user
def load_model(name, opts={})
unless loaders
self.class_eval { before_filter :load_specified_models }
write_inheritable_attribute(:loaders, [])
end
- loaders << (opts[:through] ? ThroughModelLoader : ModelLoader).new(name, opts)
+ loaders << loader_class(opts).new(name, opts)
end
def loaders; self.read_inheritable_attribute(:loaders); end
@@ -180,10 +210,27 @@ def source(controller)
end
end # ThroughModelLoader
+ class FromModelLoader < ModelLoader #:nodoc
+ attr_reader :load_from, :association
+ def initialize(name, opts={})
+ super(name, opts)
+ @load_from = "@#{opts[:from]}".to_sym
+ @association = name.to_s
+ end
+
+ def load_model(controller)
+ controller.instance_variable_get(load_from).send(association)
+ end
+ end # FromModelLoader
+ private
+ def loader_class(opts)
+ return ThroughModelLoader if opts[:through]
+ return FromModelLoader if opts[:from]
+ ModelLoader
+ end
end # ClassMethods
private
-
def load_specified_models
self.class.loaders.each do |loader|
if loader.action_allowed?(action_name)
@@ -194,8 +241,7 @@ def load_specified_models
instance_variable_set(loader.assigns_to, obj)
end
end
- end
-
+ end # load_specified_models
end # LoadModel
end # ThumbleMonks
View
@@ -1,7 +1,7 @@
Gem::Specification.new do |s|
s.name = "load_model"
- s.version = "0.1.0"
- s.date = "2009-01-03"
+ s.version = "0.2.0"
+ s.date = "2009-03-24"
s.summary = "Rails Controller plugin that provides easy and useful macros for tying models and requests together"
s.email = %w[gus@gusg.us gabriel.gironda@gmail.com]
s.homepage = "http://github.com/thumblemonks/load_model"
@@ -0,0 +1,24 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class FromController < ActionController::Base
+ load_model :post
+ load_model :user, :from => :post
+
+ def index; render :text => 'index'; end
+end
+
+class FromControllerTest < ActionController::TestCase
+ def setup
+ @user = User.create!(:name => 'Foo')
+ @post = @user.posts.create!(:name => 'Foo post')
+ end
+
+ context "loading user from post" do
+ setup do
+ get :index, :id => @post.id
+ end
+
+ should_assign_to(:post) { @post }
+ should_assign_to(:user) { @user }
+ end
+end

0 comments on commit 8ca8595

Please sign in to comment.