Here a getting started tutorial for Sashimi
.
Sashimi: Getting Started Tutorial 0
Released Sashimi 0.1.6 0
I just released a new version of Sashimi, with tiny fixes and a new home! In fact the project is also hosted on RubyForge, now you can install the gem with:
$ (sudo) gem install sashimi
or with:
$ (sudo) gem install jodosha-sashimi --source=http://gems.github.com
If you wish, you can visit the project pages on GitHub and on RubyForge.
UPDATE The release 0.1.6 is broken, you are strongly encouraged to update your gem with the newest 0.1.7.
Rails: Single File App 0
I took inspiration from the Pratik Naik post, and realized a more simplistic version of its Rails single file app. My implementation has only Rails as unique dependency.
require 'rubygems'
require 'action_controller'
require 'webrick'
require 'webrick_server'
class HelloWorldController < ActionController::Base
session :off
def index; render :text => 'Hello World!' end
end
ActionController::Routing::Routes.draw do |map|
map.root :controller => "hello_world"
end
DispatchServlet.dispatch :port => 3000,
:server_root => File.dirname(__FILE__)
Update 2008-06-04: I just wrote another version which also uses ActiveRecord and a template.
require 'rubygems'
require 'activerecord'
require 'action_controller'
require 'webrick'
require 'webrick_server'
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'tiny_rails.sqlite3',
:timeout => 5000)
ActiveRecord::Schema.define do
create_table :people, :force => true do |t|
t.string :first_name
end
end
class Person < ActiveRecord::Base; end
Person.create :first_name => 'Luca'
File.open('index.html.erb', 'w') do |f|
f << "Hello, my name is <%=h @person.first_name %>!\n"
end
class HelloWorldController < ActionController::Base
session :off
def index
@person = Person.find :first
render :file => 'index.html.erb'
end
end
ActionController::Routing::Routes.draw do |map|
map.root :controller => "hello_world"
end
DispatchServlet.dispatch :port => 3000,
:server_root => File.dirname(__FILE__)
Just start the script and point your browser at http://localhost:3000!
Ruby: Class Methods Proxy 2
Did you ever used class methods inside your instance methods? If yes, you probably noticed how frustrating can be to use the self.class.my_class_method syntax. A solution could be to create a private method which encapsulates the class one.
class Repository
def self.path
@@path
end
def print_path
puts path
end
private
def path
self.class.path
end
end
In the above example, #print_path can print the @@code value, without worrying about it's a class value, because we have wrapped it.
When I developed Sashimi I've widely used this technique, with a bad impact on the code duplication, and in order to DRY-up my code I extended the Ruby's Class class in this way:
class Class
def class_method_proxy(*method_names)
method_names.each do |m|
self.class_eval %{
# Proxy method for <tt>#{self.class.name}##{m}</tt>
def #{m}(*args)
self.class.#{m}(*args)
end
private :#{m}
}, __FILE__, __LINE__
end
end
end
This approach allow us to annotate our classes, choosing which class methods should be available as private methods.
Now, our example class should look like the following:
class Repository
def self.path
@@path
end
class_method_proxy :path
def print_path
puts path
end
end
Of course you can pass multiple symbols to proxy many methods at the same time.
class_method_proxy :path, :another_path, :a_third_one
Sashimi: Just Received A Tasteful Update 0
Yesterday I announced Sashimi and now it's already time of tasteful updates.
Changes
First, thanks to all the people that appreciated my work. Second, new commands and options are avaliable for Sashimi.
Update all plugins in your repository:
$ sashimi update --all
Install a plugin to your Rails app:
$ sashimi install --rails click-to-globalize
NOTE this is an alias for the add command.
Update a plugin of a Rails app:
$ sashimi update --rails click-to-globalize
NOTE If your application is versioned with Svn or Git, Sashimi will automatically schedules for add/remove the added/removed files.
How To Update
$ (sudo) gem update jodosha-sashimi --source=http://gems.github.com
I hope you enjoy those addictional options.
Sashimi: A Rails Plugins Manager Gem 3
I have a really, really bad memory: each time I need to install a Rails plugin, I Google to find the repository, then try to download the code. Often, I remember the url, but the server is down. Damn!
All this annoying issues kill the Rails rapidity on application prototyping. But, what if all the plugins which I need are available offline on my notebook? I can forget about all that urls, and I should stop to worry about the server status.
To solve this problems, I wrote Sashimi, it's a gem that manages you favourite Rails plugins in a local repository.
How It Works?
First you need to install it with:
$ (sudo) gem install jodosha-sashimi --source=http://gems.github.comNow you can install a plugin on your local repository:
$ sashimi install git://github.com/jodosha/click-to-globalize.gitTa-daaaa!! Now Click to Globalize is available offline:
$ sashimi list
click-to-globalizeIf you need to add it to your Rails app, just move to your app root, then type:
$ sashimi add click-to-globalize
Conclusion
I found Sashimi really useful, I hope you too.
For the complete reference, please check out at the official gem page.
Sashimi 0
Sashimi
Sashimi is a Rails plugins manager.
It allows to install your favourite plugins in a local repository and add them to your Rails apps.
The main goal of this gem is to allow the plugins offline installation.
Installation
$ (sudo) gem install sashimi
or:
$ (sudo) gem install jodosha-sashimi --source=http://gems.github.com
Usage
Install a plugin from a subversion URL:
$ sashimi install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder
Install a plugin from a git URL:
$ sashimi install git://github.com/jodosha/click-to-globalize.git
Uninstall a plugin:
$ sashimi uninstall continuous_builder
Update a plugin:
$ sashimi update click-to-globalize
Update all installed plugins:
$ sashimi update --all
Update a plugin of a Rails app:
$ sashimi update --rails click-to-globalize
NOTE: If your application is versioned with Svn or Git, Sashimi will automatically schedules for add/remove the
added/removed files.
List all installed plugins:
$ sashimi list
Add installed plugin(s) to a Rails app:
$ sashimi add click-to-globalize
or$ sashimi install --rails click-to-globalize
Known And Common Issues
- When add a plugin to an app, make sure your current directory is the rails root.
- Only Subversion and Git repositories are currently supported.
Contribute
Check out the code and test it:
$ git clone git://github.com/jodosha/sashimi.git
$ rake
Create a ticket to: http://sushistar.lighthouseapp.comCreate a patch and add as attachement to the ticket.
Credits
Partially inspired to RaPT.
Copyright
Copyright © 2008 Luca Guidi - http://lucaguidi.com, released under the MIT license
Acts As Resource: Rails 2.1 ready and moved to GitHub 0
Acts As Resource is ready for the imminent Rails 2.1!
I also moved it to GitHub, the new repo is http://github.com/jodosha/acts-as-resource/tree/master, the SVN one is deprecated.
If you enjoyed this post, feel free to recommend me on Working With Rails.Click to Globalize: rewritten and moved to GitHub 4
Click to Globalize has been rewritten, in order to use all instance methods of the JavaScript class, instead of class methods.
I also fixed some tiny issues:
- The
form_authentication_tokenmethod is called only if the application is protected against the CSRF attacks (protect_against_forgery?returnstrue) - It uses as parameter key what
request_forgery_protection_tokenreturns, instead of the hard-codedauthenticity_token - The JavaScript class uses Prototype's dom:loaded custom event.
I also migrated the plugin GitHub. The new repo is http://github.com/jodosha/click-to-globalize/tree/master, so the oldest one is deprecated.
Have a nice globalization!
If you enjoyed this post, feel free to recommend me on Working With Rails.
Ruby on Rails: Test Model Domain Changes 0
Create and Destroy
ActiveSupport provides few useful tools to improve our test, I really appreciateassert_difference and assert_no_difference.
Basically, this two methods accepts as arguments a code chunk (as string) and a block. When the test run, it binds the block first, then it assert if the changements caused by the block call are the same expected by first argument.
def test_should_be_created
assert_difference 'Person.count' do
create_person
end
end
We are testing a Person creation, we pass as first argument 'Person.count', and the code that should correctly save the person. If the model will be saved, a new record should exists into the database table. At this moment assert_difference evaluates the first argument, and assert if there are differences in the Person count.
def test_should_be_destroyed
assert_difference 'Person.count', -1 do
destroy_person
end
end
This example is just a bit different, we are also passing a Fixnum as argument. This because we want assert another difference from the default one, which is +1. So, if the model will be correctly saved, we will have a negative difference, of one, into the Person count.
def test_should_require_first_name_on_create
assert_no_difference 'Person.count' do
create_person
assert person.errors.on(:first_name)
end
end
The third example uses assert_no_difference, to test aganist model validations. ActiveRecord, by default, prevents the creation of a model if a validation doesn't pass. In this case our model requires first_name as mandatory attribute, but unfortunately it's nil, so the creation fails and the brand new record will be not created.
Update
As you can see, those two methods are very useful for test creation and destruction of models, but totally missing the goal of the update. In fact, the update process of a record, doesn't produces numerical differences. I created two methods to supply this lack.def assert_updated(model, message = nil, &block)
yield
assert_not_equal model.attributes, model.reload.attributes, message
end
def assert_not_updated(model, message = nil, &block)
yield
assert_equal model.attributes, model.reload.attributes, message
end
Just add them to your test/test_helper.rb, and they will be available in all your test cases.
def test_should_update
assert_updated person do
update_person
end
end
First, you should notice that the first argument it isn't a string but an ActiveRecord. The behaviour of this method is similar to the previous I illustrated, it first bind the block, then assert if the attributes of the model are different.
It internally uses ActiveRecord::Base#attributes which returns an hash of model attributes, then assert the differences with Ruby's assert_not_equal.
def test_should_require_first_name_on_update
assert_not_updated person do
update_person
assert person.errors.on(:first_name)
end
end
Similarly to all other examples, it first performs the block, call then assert there are no changes in the model attributes.
Conclusion
Those methods should provide a rapid way to write and mantain your test cases. If you enjoyed this post, feel free to recommend me on Working With Rails.Ruby: XML Parsing With SAX 3
SAX is an event-driven parser for XML.
It sequentially reads the xml and generates special events. So, if you want to use SAX, you should implement the code to handle them. It's quite different from the DOM model, where the whole xml is parsed and loaded in an tree.As you can see, the first approach is more difficult than the DOM one. Why we should use it? Depends.
If you want to extract certain informations from a big file, probably you should choose a SAX implementation, in this way you can avoid the initial DOM loading overhead.
The Ruby XML Library
The Ruby core library has a built-in XML parser (both DOM and SAX) called REXML, but it's terribly slow, it's highly advisable to use libxml. It's a binding to the popular library from Gnome and it was released as gem.
The Ruby Implementation
In first instance we need an handler, to deal with the SAX events.
class Handler
def method_missing(method_name, *attributes, &block)
end
end
Libxml generates several events and it expects to find certain methods into the class assigned ad handler. With method_missing we simply avoid any exception.
A More Useful Example
We try to extract the most recent headlines of a blog.
Download the feed:
curl http://feeds.feedburner.com/LucaGuidi >> luca.xml
Now we need our custom SAX parser:
require 'rubygems'
require 'xml/libxml'
require 'handler'
class SaxParser
def initialize(xml)
@parser = XML::SaxParser.new
@parser.string = xml
@parser.callbacks = Handler.new
end
def parse
@parser.parse
@parser.callbacks.elements
end
end
We have just wrapped the SAX parser from libxml and we have registered our first class as callback handler.
Now we are going to improve the handler to recognize and save the post titles:
class Handler
attr_accessor :elements
def initialize
@elements = []
end
def on_start_element(element, attributes)
@print = true if element == 'title'
end
def on_characters(characters = '')
@elements << characters if @print
end
def on_end_element(element)
@print = false
end
# Handle all missing methods of the SAX events chain.
# You can implement or omit one or many of those methods, without any raising Exception.
#
# The complete chain is:
# on_start_document
# on_processing_instruction(instruction, arguments)
# on_start_element(element, attributes)
# on_characters(characters = '')
# on_end_element(element)
# on_end_document
def method_missing(method_name, *attributes, &block)
end
end
When the handler is instantiated we create an internal array to store our results, then when we find and title element we set on true the print flag. When it's true we can store the data into elements, then we set on false on the ending handler of the element.
Usage
We create a trivial script:
#!/usr/bin/env ruby
require 'sax_parser'
xml = open(ARGV[0], 'r').collect { |l| l }.join
puts SaxParser.new(xml).parse
From the shell:
./parse luca.xml
Conclusion
SAX is less elegant and easy than DOM, but could be very useful in certain cases.
Click To Globalize 0
Click To Globalize
Click To Globalize is an extension for Globalize plugin, it allows to edit in place globalized labels. With this plugin you don't have to create a globalization back-end, but just edit your interface in place!!
If you have a previous globalization experience, you probably noticed that the main problem of dedicated back-ends is the lack of the context where the string will be placed in. It isn't a trivial issue: you'll never know if the meaning of your string is harmonious with other labels and how your string will be rendered, until the page load.
Now you can forget this issues, you can directly globalize in place!!
Getting Started
- Install Globalize, if you don't already done.
- Install Click To Globalize:
$ ./scripts/plugin install git://github.com/jodosha/click-to-globalize.git - Run the setup task:
$ rake click:setup - Add at the end of your layout the following code:
<%= click_to_globalize -%> - Put in your view:
<%= 'hello_world'.t %> - Start the server and click to globalize.
Prerequisites
- Globalize plugin: www.globalize-rails.org.
- Prototype >= 1.5.1.1, this version is provided by Click To Globalize installation.
- RedCloth gem (optional) for textile formatting.
- BlueCloth gem (optional) for markdown formatting.
Install
- Install Globalize, if you don't already done.
- Install Click To Globalize:
Rails 2.1.x
Rails 2.0.x$ ./script/plugin install git://github.com/jodosha/click-to-globalize.git
Rails 1.2.x$ ./script/plugin install http://dev.23labs.net/svn/rails/plugins/click_to_globalize/trunk$ ./script/plugin install http://dev.23labs.net/svn/rails/plugins/click_to_globalize/branches/for-1.2.x - Run the setup task:
$ rake click:setup - Run the tests (optional):
$ rake click:test - Choose your languages, they will be used to create a picker menu.
class ApplicationController < ActionController::Base self.languages = { :english => 'en-US', :italian => 'it-IT' } end - If you like to use wiki-formatting styles, you could use textile (RedCloth) or markdown (BlueCloth).
class ApplicationController < ActionController::Base self.formatting :textile end - Personalize the access to the globalization features, overriding #globalize:
class ApplicationController < ActionController::Base def self.globalize? current_user.admin? end end
NOTICE: #globalize? is method to turn on/off all the Click To Globalize features. - Add at the end of your layout the following code:
<%= click_to_globalize -%>
Uninstall
$ ./script/plugin remove click_to_globalize
Features
- In-place editing for each globalized string.
- Easy and painless plug-in/plug-out process.
- Unobtrusive Javascript.
- Auto transformation from input text to textarea for long strings.
Common Issues
- The install process upgrades your Prototype version to 1.5.1.1 only if needed, but it's advisable to backup your scripts.
- Since the Globalize plugin creates folders like for-1.2, be sure to rename the folder to globalize.
- Due to unobtrusive nature of the plugin, each page is parsed by javascript to find the right element and bind to it an Ajax.InPlaceEditor.
Be sure your (X)HTML is wellformed.
NOTICE: If you use the Rails 1.2.3 scaffold system, Click To Globalize doesn't works. Cleanup the code before to use. - If you use a wiki-formatting style it could be advisable to use #h to avoid security problems (i.e. XSS).
<%= textilize(h('some_text'.t)) %>
Contribute
- Check out the code and test it:
$ git clone git://github.com/jodosha/click-to-globalize.git $ rake click - Create tickets at the project Trac.
- Create a patches and add as attachement to the tickets.
Repository
http://github.com/jodosha/click-to-globalize/tree/master
Credits
Javascript tests are based on Prototype test libs.
Vote
If you find it useful feel free to add to your favs on agilewebdevelopment.com.
Copyright
Copyright © 2007 - 2008 Luca Guidi - 23 Labs, released under the MIT license.
Tutorials
Click To Globalize: High Resolution Video Tutorial 3
The video tutorial for Click to Globalize is now available in high resolution: Click To Globalize: Getting Started.
Ruby: How To Avoid A respond_to? Call 0
I'm writing this post as contribution to the Campagna Anti-IF (Anti-IF Campaign).
Problem
I'm developing an internal Rails plugin for widgets, it provides a class called Widget (really unconventional :-P), and each widget should inherit from it.
The actual implementation provides a callback called before_render, that allows to add some logic to a widget, if implemented it's called before the widget rendering.
Ruby doesn't have abstract methods, so I have to check if the subclass has the implementation of mentioned method:
# Rendering code..
before_render if respond_to? :before_render
# ...
As you can see it's an inefficient and inelegant way to render the widget, cause we always check if the method was implemented, and because I have introduced an if statement.
Solution
I added to Widget an empty before_render method: if the method it wasn't implemented into the subclass the rendering code will be safely called.
Here the new code:
def before_render
end
# Rendering code..
before_render
# ...
Rails: How To Create Custom Validations 0
Often our model objects leaning toward to be confused or noisy, due to validations DSLs. Imagine a class Answer, with an attribute, that should be exactly a string representation of a boolean. Ok, I know it's an odd example, but: it's trivial enough to make this example clear, and.. It happened to me to deal with this situation. :-P
class Answer < ActiveRecord::Base
validates_inclusion_of :value, :in => %w( true false ),
:message => "Should be exactly true or false."
end
Now, we try to clean-up a bit this code.
First, create a file named validations.rb into lib, then copy and paste this code:
module ActiveRecord
module Validations
module ClassMethods
@@boolean_values = %w( true false )
@@validates_boolean_msg = "Should be exactly #{@@boolean_values.join(' or ')}."
# Check if the value is a boolean: <tt>true</tt> or <tt>false</tt>.
def validates_boolean(*attr_names)
configuration = { :message => @@validates_boolean_msg,
:in => @@boolean_values }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
validates_inclusion_of attr_names, configuration
end
end
end
endThen we are going to add the following line at the end of
environment.rb
require 'validations'
Let's clean the code:
class Answer < ActiveRecord::Base
validates_boolean :value
end
Is it better? Maybe.. ;-)
![Click To Globalize: Getting Started [High Resolution]](http://lucaguidi.com/assets/2008/1/26/click_to_globalize_getting_started.png)
