Partializer
Structure your partials in Rails 3+
Installation
# Gemfile
gem 'partializer'
Console, run:
$ bundle
Requirements
Only tested on Ruby 1.9.3 and Rails 3.2.
Why?
In a Rails project I notices this reoccuring pattern and thought it would be nice to encapsulate it better ;)
#communication.column
- [:upper :lower].each do |name|
= render partial: "properties/show/main/#{name}"
#lower.column
- [:communication :description].each do |name|
= render partial: "properties/show/main/lower/#{name}"
#communication.column
- [:profile, :contact_requests, :social, :favorite, :comments].each do |name|
= render partial: "properties/show/main/lower/communication/#{name}"
Note also that all the partial paths are hardcoded here. If I move a folder, all the partials withing this folder have to be reconfigured to the new location! Argh!
With partialized partials :)
Imagine you have a properties/show/_main
partial. Then you can render all its subpartials like this:
#main.column
= render_partials partialize('properties#show', 'main')
For the properties/show/main/_lower
partial, simply:
#lower.column
= render_partials partialize(partializer, 'lower')
And for the properties/show/main/lower/_communication
partial, simply:
#communication.column
= render_partials partialize('communication')
Since the partializer (with previous context) will be passed down as a local and used by partialize
to resolve the context (partial path). Sleek :)
Partialize will take advantage of this fact and use this local variable unless a specific Partializer is passed in as the first argument.
Context path building
Since the partializer has knowledge of the current context, you can use it to generate the path of a partial. The #partial_path
method will raise an erorr if the name of the partial is not registered with that partializer.
#communication.column
= render partial: partializer.partial_path(:sidebar)
You can also use the partializer to build a relative path to its context (path)
#communication.column
= render partial: partializer.build_path('sidebar/upper')
The REAL power!
Since the Partializer is class based, you can use simple inheritance and include to mixin partial configurations for other contexts, override, call super
etc.
Another great advantage is, that if you pass the "context" down the partial hierarchy, changing the top level context will take effect on all the partials in the call hierarchy. One change fix!
Configuration
Note: This should be improved with even better DSL and perhaps loading from YAML file or similar? Maybe even supplying a hash directly and using Hashie::Mash?
Structure your partial groupings like this:
module Partializers
class Properties < Partializer
class Show < Partializer
partials_for :main, [{upper: :gallery}, :lower]
partials_for :side, [:basic_info, :cost, :more_info, :period]
partializer :lower do
partializer :communication do
partialize :profile, :contact_requests, :social, :favorite, :priority_subscription, :free_subscription, :comments
end
partialize :_communication, :description
end
partials_for :my_main, [{upper: :gallery}, :_lower]
end
end
end
Alternatively using Classes and methods (sth like):
module Partializers
class Properties < Partializer
class Show < Partializer
def main
partials_for :main, [{upper: :gallery}, :lower]
end
def side
partials_for :side, [:basic_info, :cost, :more_info, :period]
end
class Lower < Partializer
class Communication < Partializer
def partials
partials_for [
:profile, :contact_requests, :social,
:favorite, :priority_subscription,
:free_subscription, :comments
]
end
end
def partials
partials_for [:_communication, :description]
end
end
def my_main
partials_for [{upper: :gallery}, :_lower]
end
end
end
end
This will likely be optimized in the near future. No need for the partials_for
method call in the instance methods. Can be auto-resolved when the Partializer resolves itself into a nested Partializer::Collections
structure.
Special Conventions
A Symbol prefixed with underscore will nest down the hierarchy, see fx :_lower
vs :lower
. In this case, the class must have been defined, since it uses a constant lookup on the class and instantiates it.
It might make sense to drop this _
convention and simply always attempt nested resolution?
Usage in Views and Partials
Now you can use the Partializers in your views!
#communication.column
= render_partials partialize('properties#show', 'main')
This will render fx properties/show/main/upper
and properties/show/main/lower
.
The partial called will have the partializer passed in as a local.
This allows you to continue calling like this, which will effectively be a shorthand for calling:
= render_partials partialize('properties#show', 'main.lower')
#main.column
= render_partials partialize(partializer, 'lower')
Hidden Rails feature
I'm taking advantage of this little hidden 'gem' in Rails :)
Now, you can solve this problem by defining to_partial_path
(part of the ActiveModel API) and can be implemented in any object.
class Activity
def to_partial_path
"activities/#{kind}"
end
end
And then invoking:
render :partial => @activities, :as => :activity
Contributing to partializer
- Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
- Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
- Fork the project.
- Start a feature/bugfix branch.
- Commit and push until you are happy with your contribution.
- Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
- Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
Copyright
Copyright (c) 2012 Kristian Mandrup. See LICENSE.txt for further details.