write a plugin for rails
This article will show you how to develop a plugin that adds functionality to a controller (other plugins, e.g. for models) will follow later. In fact, I’ll explain to you how I developed my Acts As Exportable plugin.
Let’s take a basic Rails application for starters. You have setup a model with some attributes and a scaffolded controller that allows you to CRUD your items. In this tutorial I’ll be working with books. The model is named ‘Book’ and the controller ‘BooksController’. Start your web server now and add some random data to play with.
Before you dive into writing a plugin for the controller to export data to XML you should have some basic functionality in your controller first. I’ve found it easier to develop my code in the controller first, and then port it to a plugin. So, add a new method to your BooksController that’ll export books to XML. This looks quite easy:
def export_to_xml
books = Book.find(:all, :order => 'title')
send_data books.to_xml,
:type => 'text/xml; charset=UTF-8;',
:disposition => "attachment; filename=books.xml"
end
Now, call /books/export_to_xml and you download a real XML file containing all your books! To make things a bit more complicated, we want to be able to feed this method some conditions to select books. A nice solution is to add a special method for this that defines these conditions. (You could also use them in listing books, for example.) I add a new method to the BooksController:
def conditions_for_collection
['title = ?', 'some title!']
end
The condition is of the same format you can feed to find. Here you could, for example, select only the books belonging to the currently logged in user.
Next, update the export_to_xml method to use these conditions
def export_to_xml
books = Book.find(:all, :order => 'title', :conditions => conditions_for_collection)
send_data books.to_xml,
:type => 'text/xml; charset=UTF-8;',
:disposition => "attachment; filename=books.xml"
end
Nice that’s it. Now, you like what you’ve made so far, and want to stuff it into a plugin and put it on your weblog. Here’s how to go about that.
Creating the plugin
First, generate the basic code for a plugin:
./script/generate plugin acts_as_exportable
This will create a new directory in vendor/plugins containing all the basic files you need. First, we’ll take a look at vendor/plugins/acts_as_exportable/lib/acts_as_exportable.rb. This is where all the magic happens.
What we want is to is add a method to ActionControllerBase that allows you to easily enable the plugin in a certain controller. So, how do you want to activate the plugin? Right, you just call ‘acts_as_exportable’ from the controller, or optionally, you add the name of the model you want to use.
acts_as_exportable
acts_as_exportable :book
The vendor/plugins/acts_as_exportable/lib/acts_as_exportable.rb contains a module that’s named after our plugin:
module ActsAsExportable
end
Next, we add a module named ‘ClassMethods’. These class methods will be added to ActionController::Base when the plugin is loaded (we’ll take care of that in a moment), and enable the functionality described above.
module ActsAsExportable
def self.included(base)
base.extend(ClassMethods)
end
class Config
attr_reader :model
attr_reader :model_id
def initialize(model_id)
@model_id = model_id
@model = model_id.to_s.camelize.constantize
end
def model_name
@model_id.to_s
end
end
module ClassMethods
def acts_as_exportable(model_id = nil)
# converts Foo::BarController to 'bar' and FooBarsController
# to 'foo_bar' and AddressController to 'address'
model_id = self.to_s.split('::').last.sub(/Controller$/, '').\
pluralize.singularize.underscore unless model_id
@acts_as_exportable_config = ActsAsExportable::Config.\
new(model_id)
include ActsAsExportable::InstanceMethods
end
# Make the @acts_as_exportable_config class variable easily
# accessable from the instance methods.
def acts_as_exportable_config
@acts_as_exportable_config || self.superclass.\
instance_variable_get('@acts_as_exportable_config')
end
end
end
So, what happened? The first method you see extends the current class (that’s every one of your controllers with the methods from the ClassMethods module).
Every class now has the ‘acts_as_exportable’ method available. What does it do? The plugin automatically grabs the name of the model associated (by convention) with the controller you use, unless you specify something else.
Next, we create a new configuration object that contains information about the model we’re working with. Later on this can contain more detailed information like what attributes to include or exclude from the export.
Finally we include the module InstanceMethods, which we still have to define. The instance methods are only included when we enable the plugin. In our case, the instance methods include the ‘export_to_xml’ and ‘conditions_for_collection’ methods. We can simply copy/paste them into your plugin.
module InstanceMethods
def export_to_xml
data = Book.find(:all, :order => 'title', :conditions => conditions_for_collection)
send_data data.to_xml,
:type => 'text/xml; charset=UTF-8;',
:disposition => "attachment; filename=books.xml"
end
# Empty conditions. You can override this in your controller
def conditions_for_collection
end
end
Take note that we don’t want to define any default conditions, because we don’t know what model we’re using here. By adding an empty method, the method is available and no conditions are used. Another developer can define ‘conditions_for_collection’ in his controller to override the one we write here.
In the ‘export_to_xml’ there are a few changes as well. First of all, I generalized ‘books’ to ‘data’.
The most important step is yet to come. We have still application specific code in your plugin, namely the Book model. This is where the Config class and @acts_as_exportable_config come in.
We have added a class variable to the controller named @acts_as_exportable_config. By default, this variable is not accessable by instance methods, so we need a little work around:
self.class.acts_as_exportable_config
This will call the class method ‘acts_as_exportable_config’ we defined in ClassMethods and return the value of @acts_as_exportable_config.
Note that we store the configuration in each seperate controller. This allows acts_as_exportable to be used with more than one controller at the same time.
With the model name made application independent, the whole plugin code looks like:
module ActsAsExportable
def self.included(base)
base.extend(ClassMethods)
end
class Config
attr_reader :model
attr_reader :model_id
def initialize(model_id)
@model_id = model_id
@model = model_id.to_s.camelize.constantize
end
def model_name
@model_id.to_s
end
end
module ClassMethods
def acts_as_exportable(model_id = nil)
# converts Foo::BarController to 'bar' and FooBarsController to 'foo_bar'
# and AddressController to 'address'
model_id = self.to_s.split('::').last.sub(/Controller$/, '').\
pluralize.singularize.underscore unless model_id
@acts_as_exportable_config = ActsAsExportable::Config.new(model_id)
include ActsAsExportable::InstanceMethods
end
# Make the @acts_as_exportable_config class variable easily
# accessable from the instance methods.
def acts_as_exportable_config
@acts_as_exportable_config || self.superclass.\
instance_variable_get('@acts_as_exportable_config')
end
end
module InstanceMethods
def export_to_xml
data = self.class.acts_as_exportable_config.model.find(:all,
:order => 'title',
:conditions => conditions_for_collection)
send_data data.to_xml,
:type => 'text/xml; charset=UTF-8;',
:disposition => "attachment; filename=\
#{self.class.acts_as_exportable_config.model_name.pluralize}.xml"
end
# Empty conditions. You can override this in your controller
def conditions_for_collection
end
end
end
Add the following line to your BooksController and restart your web server. (Oh, and make sure to remove the export_to_xml method from the controller as well)
acts_as_exportable
Done! – Or not?
Enabling the plugin by default
We have a very nice plugin now, but it is not loaded by default! If you take a look at your plugin directory, you’ll find a file named ‘init.rb’. This file is executed when you (re)start your web server. This is the perfect place to add our class methods to the ActionController::Base. Just add the following three lines of code to ‘init.rb’:
ActionController::Base.class_eval do
include ActsAsExportable
end
When we include our module, the ’self.included’ method is called, and the ClassMethods module is added, thus enabling the acts_as_exportable method.
That’s all! Happy plugin writing!
Feel free to comment on this post and write about any of your own plugin (for controllers) experiences
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
XML이란 무엇입니까?이것은 저장, 검색 및 공유할 수 있는 형식으로 데이터를 저장하는 강력한 방법입니다. 가장 중요한 것은 XML의 기본 형식이 표준화되어 있기 때문에 시스템이나 플랫폼 간에 로컬 또는 인터넷을 통해 XML을 공유하거나...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.