RailsCasts Pro episodes are now free!

Learn more or hide this

Recent Comments

Avatar

Hi Guerra!

Thanks for yuor interesting in my problem, it was a damn mistake with mass assignement as i supposed... I used something like this...
<% fields_for "project[task_attributes][]", task do |task_form| %>

and when changed this line:
<%= task_form.text_field :name %> with a line providing more infos about the text_field e.g. "project[task_attributes][#{@aNumber}]" it worked fine for me.... but still don't know where I've made mistake..

Avatar

Nice screencast as always, but maybe you should specify one key difference with :file_store, it stores only Strings not Objects.
So caching your Category.all query with cache store set as :file_store will results in an unusable String. Worth mentioning it I think.

Avatar

How do I put the associations of a db query into the cache for later usage?

Refering to the screencast example: If I try to access a subcategory of the previously cached category query "all_cached.subcategory.name" wouldn't work as the subcategory istn't stored in the cache.

Avatar

@Carl, that is one form of caching, yes. However, sometimes you do not have the luxury to cache the entire page. Sometimes you can only cache particular queries or results of calculations (or rendering of views). Depending on the speed of things involved it may make perfect sense to store your cache in the database (though this is probably an uncommon case).

Avatar

@Carl,

Caching has a couple of main purposes. First, it can be used to take the load off the database, since retrieving items from the cache is much faster than loading them from the database. Storing the cached objects in the database would defeat much of the benefits of this purpose. (Although, if you are caching complicated joins the cached version would likely be faster)

Second, caching is used to prevent Rails from performing expensive operations. Take for example, the the display of a list of your friends and their recent activities in a social application. This list would require Rails to do complicated joins and load all of the relevant objects. Caching this in memory or in the database will be faster than requiring Rails to regenerate the results. Caching is a critical tool for any high volume site whether you use memcache or the database store. Some people might prefer the database store because it is very easy to share the cache among multiple sites and it is persistent. Personally, memcache is the way to go.

Avatar

Ryan,

I always add a step right after the git pull followed by rebasing my branch, and that is to rerun the tests to make sure that they still work should any changes pulled down affect them.

Avatar

Isn't the whole point of caching to get the overhead of Ruby and Rails out of the way for unchanging content so that Apache (or Nginx or whatever) can server the content directly? Wouldn't interacting with the DB ruin that?

Avatar

Nevermind, there was an explanation:

gem 'mislav-will_paginate', '~>2.1'
# this will load any 2.x version
#(greater or equal to 2.1),
# but not 3.x

Avatar

I can't seem to get the memcached version of this running in script/console when using a namespace.

If I create a new rails app, then edit config/environment.rb, adding the line:
  config.cache_store = :mem_cache_store, 'localhost', :namespace => 'test'

it will fail to load script/console with the following error:

syntax error, unexpected tASSOC, expecting kEND (SyntaxError)
...re, 'localhost', :namespace => 'test'
                              ^
from /opt/local/lib/ruby/1.8/irb/init.rb:252:in `load_modules'
from /opt/local/lib/ruby/1.8/irb/init.rb:250:in `each'
from /opt/local/lib/ruby/1.8/irb/init.rb:250:in `load_modules'
from /opt/local/lib/ruby/1.8/irb/init.rb:21:in `setup'
from /opt/local/lib/ruby/1.8/irb.rb:54:in `start'
from /opt/local/bin/irb:13

If I remove all of the options ('localhost' and :namespace) it will load correctly.

On OSX using macports ruby 1.8.7 p22.

Avatar

Thanks for another great screencast. I just came across a gem's instructions to use:
 :version => '~> 2.3.2'

What does ~> mean?

Avatar

@David, good question! Actually it's very easy to write your own custom cache store to use the database or whatever else you want. See this post by Ryan Daigle on how to do this:

http://tinyurl.com/366763

Avatar

mind = blown

Avatar

great one again,
since rails 2.1 caching gets a bit more funny to code then before ;)

@David:

you can define views or user-defined-functions in your database which behave like caching a query or create indices on columns (b-tree, ...) to improve your db-performance,

but,

to store your cached data, retrieved from a database into a database to store the data for caching makes little sense to me.

maybe you lookup the definition of caching and its advantages and disadvantages.

Avatar

Thanks for the screencast. Funny think that rubygems was just updated, I installed it on a dev-server this morning and didn't get 1.2. Thanks for the heads up :)

Avatar

Great cast, thanks!
Is there a way to store the cache data in the database?

Avatar

I'm trying to implement this but with:

collection_select

instead of:

text_field

Using :index => nil works fine with text_field but not with collection_select. How can I get arround this?

Avatar

Hey Ryan, Great railscast episode (watched all three). I was trying to make use of your moving from link_to_remote to link_to_function. It worked for me when I tried a simple page.insert_html. However, when I tried using a replace_html, I get a memory overload condition on my mongrel server like it is looping again and again for some reason. Here is my helper function: def add_policy_link(name)
    link_to_function name do |page|
      page.replace_html 'policiestabs', :partial => 'policies/policies_tabs'
      page.call 'createPoliciesTabs'
    end
  end
.... Is there some obvious reason why this is the case?Thanks, John

Avatar

Hi, Thanks for the very informative episode

Now we are facing problem with the new will_paginate in rails 2. Can you make a screencast for us on how to use it because we keep getting errors

Thanks

Avatar

Just... a big up to Ryan for all his very quality AND FREE screencasts ! Not an error in my file or other... just thank you ! ;-)

An Happy RailsCasts Watcher.

Avatar

All good. The error was mine.

Avatar

I'm using Rails 2.1 and the updated code above and getting "undefined method `new_record?' for "1":String" when I update a record.

The error comes from this line in the #existing_task_attributes method:
tasks.reject(&:new_record?).each do |task|

Avatar

Hola tengo un problema con este tutorial. Hice exactamente cada paso pero aplicado a mi proyecto y me sale el siguiente error.

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each

Extracted source (around line #2):

1: var versions = new Array();
2: <% for producto in @productos -%>
3: versions.push(new Array(<%= version.producto_id %>, '<%=h version.verprod %>', <%= version.id %>));
4: <% end -%>
5:

Avatar

Dear Rayn (and Tim Cooper).

Thanks for the work you put into this is helps a lot.

I am having the same problems as tim (i.e. the category id is not being saved into the product model).

any ideas what this might be?

Avatar

Since Rails 2.1 markaby doesn't work anymore :(

Avatar

Love the screencast. Just to say I did something similar, the difference is that mine returned a sql string and a 'human string'.

  def self.create_search(search, admin = false)
    @search = search
    @admin = admin
    @human_string = 'all properties'
    dept_condition self.methods(false).grep(/_conditions$/).map { |m| logger.debug "Searching #{m} "
      self.send(m) }
    return @condition.join(' AND '), @human_string
  end

with one of the private methods

  def self.price_range_conditions
    if @search[:min].to_i > 0 and @search[:max].to_i > @search[:min].to_i
      @criteria << replace_bind_variables("#{table_name}.`price` BETWEEN ? AND ?", [@search[:min].to_i, @search[:max].to_i])
      @human_string += ", costing between " + number_to_currency(@search[:min]) + ' and ' + number_to_currency(@search[:max])
    end
  end

with both the sql and human string I then do the find and let the user save the search into the database for later use if neaded.

PS keep up the good work.

Avatar

Hi Ivan, i never faced such a problem. But i would like to ask to you that if you did just like the example, could you explain where the object, mapped into you case, Task ryan uses at line

page.insert_html :bottom, :tasks, :partial => 'task', :object => Task.new

comes from?

He does a Task.new and i cant figure out what i have to do in the model to make this work.

Thanks,

--
Guerra

Avatar

Now RGhost 0.8 with English Documentation. For PDF, PS, TIF, etc

The Ghost's speed!!

see http://rghost.rubyforge.org

Avatar

A big issue i've come accross is that performing joins are not merged when using named scopes. In this example where we're doing a complex search, joining to other tables to filter results is really common. Just think of say, tags or particular codes.

Ryan, I noticed you posted in response to:
http://blog.teksol.info/2008/5/26/why-are-activerecord-scopes-not-merged

There aren't any active tickets in lighthouse for this unfortunately.

Avatar

This works great but I have a problem. My partial is an invoice item field with amount, price and total sum (and of course there can be many of these). I'd like the total sum to be calculated by Rails when amount or price is changed. Because the form fields have no unique id's, I cant' get this to work with remote_function.

It works if I calculate with JavaScript by traversing trough the DOM like so:

<% item_form.text_field :amount, :onkeyup =>
";$(this).up('.invoice_item').down('.sum').value=$(this).up('.invoice_item').down('.price').getValue()*$(this).getValue();" %>

But I can't use the $(this).up... thing in remote_function :update => ... and can't use RJS because there's no unique id for the fields.

Is there any solution for this?

Avatar

Nice episode, Ryan. But your voice *does* sound strange :)

Someone wants this functionality on Twitter.com? Check out http://userscripts.org/scripts/show/24398

Avatar

Just a follow up on the tracking of the statistics of the ajax pagination and hence more accurately see your site usage... I found an article at http://www.google.com/support/analytics/bin/answer.py?hl=en-nz&answer=55519 that explains how to do it if anyone has the same concerns.

Avatar

Hi people!

I'm in problem a few days. I suppose the mass assignment is my problem.
I' using this 'technique' with a Interview and EmployeeData model.
I have a join table and model InterviewsEmployeeDatas (using has_many like in #47 episode).
everything is doing fine except submit action. When I try to save interview I got a response: 500 Internal Server Error.
Do you have similar problem, or I have to write more about code structure I used.
Is OK to to do this in mass ass.
interview_employee_datas.build(attributes)
or have to deal with employee_datas , instead

Thanks in advance!

Avatar

The window position code does indeed not work in IE6.

Here is some (hopefully) browser agnostic code to calculate how close to the bottom one is (using pageHeight() from the example):

var pos = pageHeight()-(document.viewport.getScrollOffsets()[1]+document.viewport.getHeight())

pos is the number pixels the bottom of the page is from being visible. A Negative number means your page is shorter the your browser window.

Avatar

Its a good idea to focus on why auto-pagination is a good idea from a UI perspective. The default of numbered page links, while convenient for coding, imposes an interface using numbers that have no real meaning in terms of the data being reviewed. Page 4 is just an ordinal index to the data being presented. Rarely will you know how to find what you are looking for by using ordinal pagination of this type, particularly for VERY long lists, and for more modest lists, its simply an annoying interference with linear browsing through the table.

Auto paging interfaces, I think pioneered by Aza Raskin in his Humanized Reader, provides a more natural, more humane approach to browsing data. The list, if too long for sequential browsing may require additional effort, but ordinal pagination is not the right vehicle for that. Sorting filters of various kinds, or an index that has some semantic relationship with the data being searched/browsed is the way to go.

The only question is how to get it done. I'm astonished at how easy it was to do this.

Next step is to look at Raskin's admonition against using warning messages when what we really mean is to provide an undo function. Make that easy, and we have a truly killer framework.

Great presentation, Ryan. I hope this approach ris widely adopted, for it is truly A GOOD IDEA.

Avatar

Ah, CodeRay....

View source always helps, sorry bout that... :S

Avatar

Hi Ryan,

What do you use to do the syntax coloring for the code samples you post in these examples? Does the Syntax gem do that?

Avatar

@Adam, doesn't Google Analytics provide some javascript to place on your site? Maybe you could send that javascript back in the RJS call. I haven't looked into it, but you'll probably need to add the URL to that JS to point to the requested page.

Avatar

Hi Ryan,

Sometimes knowing which pages are loaded are important from a statistcal pov. Using non-ajax pagination is my preference in those cases largely due to typically using client-side JS site stat tracking like Google Analytics.

Although it's relatively trivial to track statistics for action & param based server-side tracking eg /products?page=2 etc, does someone know if there is anyway to track these AJAX pagination calls using Google Analytics? Like a ping back or a function call in the .rjs response?

Avatar

Ryan: Surely, the id is a perfect way to reference the search but if the user changes the search the others can see the change too which might be undesireable.

Avatar

@Mike, yep, got a cold. Thanks. :)

@Jason, a similar concern is graceful degradation. If someone has javascript disabled they should still be able to access the next page. Keeping the pagination links might be a good idea, or at least the "next page" link.

Avatar

I love the idea behind this but I'm concerned with SEO. The only way I can think about getting around it is to still have the pagination links but use css to hide them. Then the bots can follow the links to index the pages.

Any thoughts?

Avatar

Follow-up: i realize it still needs a lot of improvement.

In possibly-not-so-distant future I plan like to move it inside AR::Base.find to accept search clauses from :conditions, to make associations' table_name discovery more generic, etc. I would be very grateful for any kind of constructive feedback.

Avatar

Marcos, Ryan and Greg, please, check out a chunk of code i wrote tonight:

http://wild-tatar.livejournal.com/41283.html#cutid1

i use it to search anything:

Search.new(:source => 'members', :assoc => {:profile => {:height => 100..190}}).result
or
Search.new(:source => 'members', :assoc => {:favourites => true}).result
or
Search.new(:source => 'members', :root => {:first_name => 'LIKE Dennis%'}).result
or combination of anything of the above

Avatar

oops or rather

@@searches = {
:keywords => "products.name LIKE CONCAT('%', ?)",
:minimum_price => "products.price >= ?",
:maximum_price => "products.price <= ?",
:category_id => "products.category_id = ?"
}

etc.

Avatar

I'd drop the named_scope on AR::Base and do this:

# using a class variable here for convenience only...

@@searches = {
keywords => "products.name LIKE CONCAT('%', ?)",
minimum_price => "products.price >= ?",
maximum_price => "products.price <= ?",
category_id => "products.category_id = ?"
}

def find_products
@@searches.inject(Product.scoped({})) { |scope, search| search.first.blank? && scope || scope.scoped(:conditions => search.reverse) }
end

(http://pastie.org/216179)

Avatar

hey ryan, sounds like you've got a cold. if that's the case, feel better soon

that's a brilliant little implementation

Avatar

beautiful Ryan even tho I'm not gonna use this feature in my pages right now..but I know where to find it..

thanks again..

Avatar

@Victor, good points. However, I usually avoid adding plugin dependencies unless it greatly simplifies my code. Here writing the implementation from scratch is nearly as simple as using a plugin. This way I understand the code better, and it's easier for me to customize to my specific needs.

That said, sometimes it's better to not reinvent the wheel. The fact that your plugin works out of the box in IE and Opera is a good example of the power behind using an existing, well tested implementation.

I encourage any developer to look at all the options out there and find the best one that fits the requirements for their application. Here I just give another option.

Avatar

Will this still work for Rails 2.0 or is there a better way of doing it now?