Posts Tagged ‘rails’

Freezing gems with Gem.use_path

Wednesday, April 9th, 2008

Edge rails now has gem dependency support built in, with tasks to freeze in vendor/gems.

I have tried a bunch of different methods, and things like setting $GEM_HOME or using gemsonrails work well. But lately I have been using the Gem.use_paths method.

If you put this in the config/environments rb:

Gem.use_paths(nil, [ "#{RAILS_ROOT}/vendor/rubygems" ])
# Gem.path is now:
# ["/Users/ghandford/Projects/my_rails_proj/vendor/rubygems", "/Library/Ruby/Gems/1.8"]

The vendor/rubygems directory is the same structure as system gems folder:

  vendor/rubygems/cache
  vendor/rubygems/doc
  vendor/rubygems/gems
  vendor/rubygems/specifications

The convention I use is any non-native gems get frozen with the project, and then all the native gems go in the system gem path, which gets you pretty close to clone/checkout and rake setup.

As far as unpacking and install gems, you just use gem install with GEM_HOME of your local project:

GEM_HOME=`pwd`/vendor/rubygems gem install capitate

A benefit of the use_paths method is that you can create scripts in your Rails project that use gems frozen in your project (and not necessarily have to require rails or environment.rb to get at them).

./script/my_non_rails_script
  require 'rubygems'
  Gem.use_paths(nil, [ File.dirname(__FILE__) + "/../vendor/rubygems" ])

Has anyone else tried this before?

nginx conf with alternate cache dir

Monday, January 7th, 2008

Its a good idea to use an alternate cache directory in rails (in your environment.rb):

config.action_controller.page_cache_directory = RAILS_ROOT + "/public/cache/"

This makes it easier to sweep. To setup nginx to pick up from a rails alternate cache directory like public/cache, I used:

if ($http_user_agent ~* "(iPhone|iPod)") {
  rewrite ^/$ /iphone break;
  proxy_pass http://mongrel-pt;
  break;
}
 
if (-f $request_filename) {
  break;
}
 
if (-f $document_root/cache/$uri/index.html) {
  rewrite (.*) /cache/$1/index.html break;
}
 
if (-f $document_root/cache/$uri.html) {
  rewrite (.*) /cache/$1.html break;
}
 
if (-f $document_root/cache/$uri) {
  rewrite (.*) /cache/$1 break;
}
 
if (!-f $request_filename) {
  proxy_pass http://mongrel-pt;
  break;
}

It was a little tricky to figure out to use the $uri variable. The first rewrite is used for iphone requests so it doesn’t get the non-iphone cached page.

View paths in Rails 2.0

Monday, December 17th, 2007

New in rails 2.0:

ActionController::Base.append_view_path

This is useful for loading views from a plugin or gem:

ActionController::Base.append_view_path(File.dirname(__FILE__) + "/views")

There is also ActionController::Base.prepend_path.

GC and ObjectSpace

Friday, August 24th, 2007

I saw this at the Rails Edge conference and thought it was cool; aside from its really expensive to implement in JRuby.

1
2
3
4
5
6
7
8
9
10
>> GC.disable
=> false
>> instances = 0
=> 0
>>  ObjectSpace.each_object(Artist) { |a| instances += 1 }
=> 0
>> Artist.find(:all, :limit => 5)
=> [#<Artist:0x327d348 @attributes={....
>>  ObjectSpace.each_object(Artist) { |a| instances += 1 }
=> 5

Markup, CSS and Helper cataloging (Part deux)

Monday, June 25th, 2007

I should be using ERB instead of eval. But because you can’t use <% inside a string block in your view (that I can figure out; <<EOF didn’t seem to be working in a template). And if you don’t want to specify the code string in your controller you can use [% ... %]. Here is the source I am using for our code helper now:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  def erb_code(code) 
    ERB.new(code).result(binding)    
  end
  
  def render_code(code, options = {}) 
    %{ <pre class="helper">\n<code class="ruby">\n#{h(code)}\n</code>\n</pre> }
  end
  
  def render_markup(html)
    doc = REXML::Document.new(html)
    markup = ""
    doc.write(markup, 2)
    
    %{ <pre class="markup">\n<code class="html">\n#{h(markup)}\n</code>\n</pre>\n }    
  end
  
  def render_eval(html)
    %{ <div class="eval">#{html}</div> }
  end
    
  def code_helper(code)
    code.gsub!(/\[%/, "<%").gsub!(/%\]/, "%>")    
    html = erb_code(code)
    render_code(code) + render_markup(html) + render_eval(html)
  end

Then you can do things like:


  <%= code_helper %{ [%= rating_field(:rating, :effectiveness) %] } %>

and you could also dump it in a helper:

1
2
3
4
5
6
7
8
9
def code_rating_form_field 
  <<-EOF
   <% form_for :rating, @rating2 do |f| %>
     <%= f.rating_field(:effectiveness) %>
   <% end %>
  EOF
end

  // And in your view: <%= code_helper(code_rating_form_field) %>

Network Facade API

Friday, June 8th, 2007

I was looking at the NetworkFacade api, (check it out at network-facade.rubyforge.org) and noticed:

1
2
3
  # Or declate the Foo class and set an uri
  class Foo < NetworkFacade::Client 'nf://localhost:5042'
  end

where the uri is defined with the Foo declaration. Foo descends from the class returned by the Client class method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module NetworkFacade

    def self.Client(uri = nil)
        TCP::Client.uri = uri
        TCP::Client
    end

    class Client < TCP::Client
    end


    class Server < TCP::Server
    end

end

That is cool.

Render content in layout

Wednesday, May 30th, 2007

I came up with a hack to render content inside a layout, from the controller:

1
2
3
4
5
6
7
8
9
10
  def content_with_layout(content_for_layout, controller, layout = "the_layout")
    locals = { :params => controller.params }
    template = controller.class.view_class.new(controller.class.view_root, locals, controller)
    template.instance_variable_set("@content_for_layout", content_for_layout)    
    
    # This might work to get assigns? untested...
    #template.assigns = controller.template.instance_variable_get("@assigns") 

    template.render_file("layouts/#{layout}", true)    
  end  

You have to pass in any locals you want, and if you want the use assigns in your layout you would have to set the template.assigns. There was some plugin to give you extra layouts in the view but I forget what it was called. Anybody know where that is?

Update: The inside layout is at: http://fora.pragprog.com/rails-recipes/write-your-own/post/144

Rake test task for a custom FileList

Friday, April 6th, 2007

I work on a very large rails project, and rake test has become unruly mostly due to the size of our codebase. Anyway, I wrote this task to run only the tests in the products/namespace I work on.

rake test:products PRODUCTS=product1,product2

Runs all unit tests in test/**/product1/*test*.rb, test/**/product2/*test*.rb

The task:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace :test do
  task :products do      
    raise "Usage: rake test:products PRODUCTS=product1,product2" if ENV['PRODUCTS'].blank?
    
    products = ENV['PRODUCTS'].split(/,/)
    files = []
    
    products.each do |product|
      dir = "#{RAILS_ROOT}/test/**/#{product}"
      files += FileList["#{dir}/*test*.rb"]
    end    
    
    test_task = Rake::TestTask.new("test_products") do |t|
      t.libs << "test"
      t.test_files = files
      t.verbose = true
    end
    
    task("test_products").execute           
  end
end

There is probably an easier way, but this works for me. Also I wouldn’t mind someone patching rake to accept opts style arguments, like –opt=blah, or -o blah. The ENV stuff seems really not so friendly.

Fragment Cache Store with TTL

Wednesday, February 21st, 2007

I wanted to use a fragment cache, but I didn’t want to deal with expiring it. So I just made a self-expiring cache based on a TTL.

The fragment cache store just wants 4 methods: read, write, delete, delete_matched

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Cache::MemStore

  attr_reader :ttl, :auto_expire
    
  def initialize(options = {})
    @ttl = options[:ttl] || 10.minutes
    @cache = {}
  end
    
  def expire(pattern)
    @cache.each { |key, val| delete key if key =~ pattern }
  end
  
  def read(key, options)
    delete(key, options) and return if is_expired?(key)
    return @cache[key][:content] if @cache.has_key? key
  end
  
  def write(key, content, options)
    @cache[key] = { :content => content, :created_on => Time.now }
  end
  
  def delete_matched(pattern, options)
    expire pattern
  end
  
  def delete(key, options)
    @cache.delete key
  end
  
  def is_expired?(key)
    if @cache.has_key? key
      created_on = @cache[key][:created_on]
      return Time.now > (created_on + @ttl)
    end
  end
  
end

Then in your environment.rb put:


ActionController::Base.fragment_cache_store = Cache::MemStore.new(:ttl => 10.minutes)

railsbench gave me the following results:

page request                                      total  stddev%     r/s    ms/r
with cache: /                                   7.23471   2.8012   13.82   72.35
nocache:    /                                  25.81057   0.6966    3.87  258.11