CachedModels 0.0.2 0

Posted by luca
on Friday, October 10

CachedModels hit 0.0.2.

First of all, I transformed it to a Ruby gem, so you can use it outside Rails! Second, I dramatically enhanced performances, avoiding useless cache lookups and expirations. Take a look at the new benchmark stats: 1000 requests with a level of concurrency equal to 100, tooks 6 fewer seconds, if compared with standard ActiveRecord.

I strongly encourage you to upgrade to the newer version.

Released Sashimi 0.2.1 4

Posted by luca
on Monday, September 29

I've just released a new version of Sashimi (0.2.1), it fixes a bug for Ubuntu.

The Problem

When the script starts tries to load repository concerned classes: GitRepository and SvnRepository, which are subclasses of AbstractRepository. In repositories.rb I use Dir#[] to load all the .rb files in a certain directory, but the order of the resulting array is unpredictable, so if the first class was not AbstractRepository Sashimi was crashing.

The Solution

I've fixed it, so, if you have experienced this problem, please update Sashimi with:

$ (sudo) gem update sashimi

Cached Models 1

Posted by luca
on Wednesday, September 10

cached_models provides to your models a transparent approach to use Rails internal cache mechanism.

Usually, when you decide to use cache for your ActiveRecord results, you have to manually implement complex expiring policies.
cached_models simplifies your code:

class Author < ActiveRecord::Base
  has_many :posts, :cached => true
end

class Post < ActiveRecord::Base
  belongs_to :author, :cached => true
end

That's all!!.

A more complex example..

class Project < ActiveRecord::Base
  has_many :developers, :cached => true

  has_many :tickets, :cached => true
  has_many :recent_tickets, :limit => 5,
    :order => 'id DESC', :cached => true
end

class Developer < ActiveRecord::Base
  belongs_to :project, :cached => true
end

Example 1

project.developers # Database fetch and automatic cache storing

developer = project.developers.last
developer.update_attributes :first_name => 'Luca' # Database update and cache expiration for project cache

Example 2

# Fetch associated collection for both the projects
project.developers
project2.developers

developer = project.developers.last
project2.developers << developer # Database update and cache renewal for both project and project2 caches

Example 3

project.tickets # Database fetch and automatic cache storing
ticket = project.recent_tickets.first
ticket.update_attributes :state => 'solved' # Database update and cache expiration for both tickets and recent_tickets entries

The current version works only with the has_many macro.

How to install

$ ./script/plugin install git://github.com/jodosha/cached_models.git

Official page

http://lucaguidi.com/pages/cached_models

Cached Models

Posted by luca
on Wednesday, September 10

CachedModels provides to your models a transparent approach to use ActiveSupport cache mechanism.

Usually, when you decide to use cache for your ActiveRecord results, you have to manually implement complex expiring policies, like the following:

class Author < ActiveRecord::Base
  has_many :posts

  after_save :expire_cache

  def cached_posts
    Rails.cache.fetch("#{cache_key}/posts") { self.posts }
  end

  private
    def expire_cache
      Rails.cache.delete("#{cache_key}/posts")
    end
end

class Post < ActiveRecord::Base
  belongs_to :author

  after_save :expire_cache

  private
    def expire_cache
      Rails.cache.delete("#{author.cache_key}/posts")
    end
end

The problem with this kind of approach is that the code complexity grows on the cached-stuff growing. If I would to add has_many :recent_posts and cache the results, I should write other bureaucracy code, for observing my objects.
Note that the above code is incomplete, because doesn't handles a lot of cased provided by the has_many macro. For instance:
post = author.posts.last
another_author.posts << post

The second instruction should expire caches for both the models, cache values are now inconsistent. This means, of course, write other code and add code complexity.

Solution

It would be nice to DRYup your caching code. Here the cached_models approach:

class Author < ActiveRecord::Base
  has_many :posts, :cached => true
end

class Post < ActiveRecord::Base
  belongs_to :author, :cached => true
end

That's all!!.

A more complex example..

class Project < ActiveRecord::Base
  has_many :developers, :cached => true

  has_many :tickets, :cached => true
  has_many :recent_tickets, :limit => 5,
    :order => 'id DESC', :cached => true
end

class Developer < ActiveRecord::Base
  belongs_to :project, :cached => true
end

Example 1

project.developers # Database fetch and automatic cache storing

developer = project.developers.last
developer.update_attributes :first_name => 'Luca' # Database update and cache expiration for project cache

Example 2

# Fetch associated collection for both the projects
project.developers
project2.developers

developer = project.developers.last
project2.developers << developer # Database update and cache renewal for both project and project2 caches

Example 3

project.tickets # Database fetch and automatic cache storing
ticket = project.recent_tickets.first
ticket.update_attributes :state => 'solved' # Database update and cache expiration for both tickets and recent_tickets entries

Installation

There are three ways to install CachedModels:

Gemified Rails plugin

#config/environment.rb
Rails::Initializer.run do |config|
  config.gem 'cached-models'
end
$ (sudo) rake gems:install
$ rake gems:unpack

Rails plugin

$ ./script/plugin install git://github.com/jodosha/cached-models.git

Standalone

$ (sudo) gem install cached-models
require 'rubygems'
require 'activerecord'
require 'cached-models'

ActiveRecord::Base.rails_cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, 'localhost')

Issues

The current version works only with the has_many macro.

Make sure to configure your current environment with:

config.cache_classes = true
config.action_controller.perform_caching = true
config.cache_store = :mem_cache_store

Repository

http://github.com/jodosha/cached-models/tree/master

How to contribute

      Check out the code and test it:
      $ ./script/plugin install git://github.com/jodosha/cached-models.git
      Create a ticket to the Sushistar Lighthouse page
      Create a patch and add as attachment to the ticket.

Ruby Time marshaling bug in pre-1.9

Posted by luca
on Friday, August 29

Ruby's Time has a bug: when try to serialize an timezoned time, then unserialize it back, the result will use the server local time, instead of use the original time zone.

>> utc = Time.now.utc
=> Fri Aug 29 09:07:37 UTC 2008
>> marshaled = Marshal.dump utc
=> "\004\bu:\tTime\r\251\037\e\200\344\254T\036"
>> Marshal.load marshaled
=> Fri Aug 29 11:07:37 +0200 2008

This bug doesn't affects Ruby 1.9, but we still don't use that version for production purpose. If you use 1.8.x with Rails 2.1.0, your cached timestamps (including ActiveRecord objects), are probably wrong.

>> comment = Comment.first
=> #<Comment id: 865423346, post_id: 1, text: "Nice post.", created_at: "2008-08-29 09:27:48", updated_at: "2008-08-29 09:27:48">
>> Rails.cache.write('comment', comment)
=> true
>> Rails.cache.read('comment')

=> #<Comment id: 865423346, post_id: 1, text: "Nice post.", created_at: "2008-08-29 09:27:48", updated_at: "2008-08-29 09:27:48">
>> comment.update_attributes :text => "Nice post!"
=> true
>> Rails.cache.write('comment', comment)
=> true
>> Rails.cache.read('comment')
=> #<Comment id: 865423346, post_id: 1, text: "Nice post!", created_at: "2008-08-29 09:27:48", updated_at: "2008-08-29 11:28:42">


Look at the last updated_at attribute, it uses local time instead of UTC time zone.
The first time everything goes right, because #updated_at wasn't invoked and casted to a Time instance. It's a string, and the marshaling is ok.
But, when I update the object, ActiveRecord changes the value of that timestamp, but before, it cast it to a Time, and everything goes wrong.

I submitted a patch for this bug, it was committed, and will be available in the upcoming Rails 2.2 it's available in Rails 2.1.1. You are strongly encouraged to update your Rails version.

UPDATE 2008-09-08: This patch was already included in the Rails maintenance release 2.1.1.

Click To Globalize: Ready for Rails 2.1!

Posted by luca
on Monday, July 21

Click to Globalize is ready for Rails 2.1!

I spent a lot of time to make this release the best of ever! I know that a lot of time has passed since the latest Rails major release, and I apologize for this.
The official Globalize it seems dead, but the great work of Nate Clark (aka heythisisnate) has made it compatible with Rails 2.1. Thanks Nate!
The Rails Edge now includes a basic support for i18n!

What's Changed?

The first big change is about the configuration, past versions has cluttered a bit your ApplicationController, I know, but now Click uses a YAML file configuration file for locales.

As you can see, it's more readable than put an Hash inside your main controller, and it also take the advantage to separate configuration from logic.

The second change regards the default language, you can use the above file to declare it, or use the Globalize syntax (Globalize::Locale.set_base_language('en-US')), but you have to choose a locale before the app starts.
This because Click needs a secure fallback mechanism, if no active Locale was set, it's always guarantees your application to work.

The third feature is about formatting: you can always choose between textile and markdown without editing ApplicationController, but using the same configuration file.

I removed the deprecated class method ApplicationController#globalize? in favor of the instance one. This means you can use it to turn on/off Click to Globalize, just returning a boolean value:

This version also includes tiny fixes, refactoring and cleanup.




You may wish to learn a bit about Click to Globalize, so I encourage you to visit the official page.
I hope you enjoy your i18n on Rails!

Sashimi 0.2.0!

Posted by luca
on Monday, July 14

Sashimi has just hit 0.2.0!

This release has no sugar for you! No new features, just test improvements and code robustness.

Sashimi: Getting Started Tutorial

Posted by luca
on Monday, June 30

Here a getting started tutorial for Sashimi

. Sashimi getting started tutorial

Released Sashimi 0.1.6

Posted by luca
on Monday, June 23

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

Posted by luca
on Tuesday, June 03

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

Posted by luca
on Thursday, May 29

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, &block)
          self.class.#{m}(*args, &block)
        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

UPDATE 2008-07-06: I added the &block argument.

Sashimi: Just Received A Tasteful Update

Posted by luca
on Tuesday, May 20

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

Posted by luca
on Monday, May 19

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.com

Now you can install a plugin on your local repository:
$ sashimi install git://github.com/jodosha/click-to-globalize.git

Ta-daaaa!! Now Click to Globalize is available offline:
$ sashimi list

click-to-globalize

If 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

Posted by luca
on Monday, May 19

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.com
Create 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

Posted by luca
on Wednesday, May 14

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.