RailsCasts Pro episodes are now free!

Learn more or hide this

Recent Comments

Avatar

So, if openID works with your app, but you're having problems dealing with giving your openID users, a user name, here is what i did.

Following RESTful Authentication with OpenID, allow your user to login to your site using their openID.

Redirect them to a profile page, prompting them to change their e-mail/username/whatever-else-you- want. I also added an observe_field to alert the user whether the desired name had already been taken. Then, when passing the information to the current_user, before you can current_user.save! it must have a password.

So, generate a random number, and or string. Then simply set this as the password, you can then e-mail it to the user (in theory i haven't tested this out yet), if they want to change it, but it doesn't matter if they only login using openID.

this works great for me, let me know if i'm doing something horribly wrong. This way our openID users can get and change their usernames while not allowing anyone to create a user without a password.

Avatar

Ryan, you are a Rails God.
Could you discuss :include with model finds? Especially more complicated areas of it..having problems finding concrete info on it.

Avatar

Great screencast! I was wondering how you would package this generator for use by other developers? Would you create a gem out of it, or a plugin somehow?

Avatar

Hi,
I still struggle to understand how the cached values can be processed later.

Accesing a cached model object immediately after caching works as expected. I can do something like "cached_model.id" to access the corresponding cached value.

But once I try to do the same from another action it's not possible anymore.
I get an error message "stack level too deep".
However, accessing other methods still works fine.

What am I doing or thinking wrong?

  

Avatar

Could you do:
links=Static.all.collect {|a| a.permalink}

and then only trigger that route if the permalink is an existing permalink?

Avatar

How are static pages the way you show them on the first example non-restful?

    GET /about => renders the "about us" resource

I don't see how that isn't restful (ok, so you don't allow different mime-types, add to the routes "/about.:format", and you're about done)

But saying "it's not restful" because it's not using a so-called "restful controller" makes no sense =)

Avatar

Thanks Ryan. I am working on a project that required some static pages and I had already implemented a pages controller/action based approach, but it left me feeling uneasy about the scalability and management of the static content. So in about 30 minutes I converted it over to your more dynamic and RESTFUL solution. Very awesome. I love coming here, learning something new and then revisiting my code to make things better.

Avatar

Same days ago I ran in trouble installing RedCloth. Maybe I solved the problem with creating a symlink. I discribed my problem and the possible solution here: http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/976d2c67405c83c1/d5afc15178b550b6?show_docid=d5afc15178b550b6#

Can anybody say if that's correct? Or do I hae another problem?

Avatar

Funny but for semi-static pages or fully static page I actually reached the point where I'm not going through Rails at all.

There are a bunch of very good CMS around and after taking my own stab at editing the semi-static page through Rails (and then having to extend it so different people could edit the content and then review it and that little bell and whistle) I realized: "why reinvent the wheel"...

so, on my latest project I'm playing with Apache and mod_rewrite to direct the requests either to Rails for the application or to my CMS for the static/semi-static pages.

it took a little more work from a configuration stand-point (especially the CSS part of it) but the end result is that both aspect of the site are complete without having me rewritting yet another cms.

Avatar

Maxim, Thanks for your thoughts.

Since my comment, above, I have found quite a few people who are working with both Rspec and Selenium. I did try to post a comment with links to relevant pages, but Akismet thought it was spam. If anyone is interested in working with both these tools and integrating Selenium into a BDD workflow, then just google "Rspec and Selenium" and some interesting projects come up.

Avatar

Ryan, great screencast as always, Im having some trouble when I store an array of active records in memcached store using this new method. Am I allowed to store active records in memcached? The behavior is really weird, I get the array out of memcached, loop through it, and when I try to access a member it works fine, but when I try to access a method it barfs with method doesnt exist or stack too deep. Using the debugger I see the object.. any ideas?

Avatar

@bryce, I'd say use what you know. Rails may not be the perfect tool for the job, but if it is what you're comfortable with and you know how it works, use it.

Certainly there is extra expense for choosing a heavyweight tool (Rails) for something so simple, but that expense may not be as large as learning a new language or framework. You'll have to be the judge of that.

One thing interesting about mostly-static sites is that the meat of the site isn't in the app behavior, it's in the template or HTML code. That part is easy to transition to another framework if you find out Rails doesn't fit the bill as well as you'd like.

Avatar

Thanks for another great cast Ryan. For sites that have a lot of dynamic content and little static content your approach is great.

I'm wondering though, if a site is mostly static, is rails the right framework to be developing in? Would another Ruby framework like Merb or Sinatra be more appropriate?

Avatar

Renamed /plugins/selenium-on-rails to selenium_on_rails and all ok :).

Avatar

You may want to look at a plugin called Comatose, which handles what you covered. It's basically a micro CMS that's packaged as a plugin. I've used it on many projects successfully.

Avatar

@StarTrader, excellent point, these static pages are a prime candidate for page caching. Regarding showing the admin links on these pages, I recommend using an "admin" subdomain and showing the links there. With some Apache rewrite rules you can get page caching to not apply to this admin domain.

Avatar

Hey

/rorApp/application/vendor/rails/activesupport/lib/active_support/dependencies.rb:115:in `qualified_const_defined?': "Plugins::Selenium-on-rails::Lib" is not a valid constant name! (NameError)

Any ideas? Rails 2.1 with ryanb modified selenium plugin.

Avatar

Great episode as always.

I have a couple thoughts. First, someone needs to mention that page caching for static pages can be a huge benefit. NginX and Apache can both server cached pages directly without having to even touch the framework. My guess is that others can as well, I just have not used them. (c.f. Episode 115)

@Johannes: I use a similar system fro the help pages at antarestrader.com. I use this code to replace [[wiki links]] with Markdown Links:
  txt= txt.gsub(/\[\[(.*?)\]\]/) do |link|
    "[#{$1}][/help/" + $1.downcase.gsub(/\s/,'_') + "]"
  end

(I'm not sure how this will render in Ryan's page) This code dated from an old version of RedCloth, and it would now be a better to add a rule to the parser. See the RedCloth docs.

Finally, I would be interested in an episode that showed how to use JavaScript to add dynamic content to mostly static pages. For example, adding the edit and destroy links only for logged-in administrators.

Thanks Again,

StarTrader

Avatar

I accomplish semi-static pages by having a 'page' boolean attribute in my Post model (which defaults to false). I then have a class method Post.find_page(name) which takes the permalink name as an argument, as well as Post.find_pages(opts = {}). I like my static pages to have URLs like '/identity', '/contact', etc., so in routes.rb:

map.page '/:page', :controller => 'posts', :action => 'page'

Quite simple, really.

Avatar

@Adam, Including the id in the URL is a good solution for many cases because it can make your routes clean and simple. Usually this is implemented with overriding the to_param method in the model. See episode 63 for details (now linked in the show notes).

That said, I think semi-static pages are an exception. I prefer to have the URL very clean here without the ID. Not sure why exactly.

@Gavin, oh yeah, I knew I forgot something! Linked in the show notes now. Thanks.

@sintaxi, right, I should have mentioned the acts_as_textiled plugin for doing this which is now linked in the show notes. Regarding the line breaks, this is the big reason I prefer the latest version of RedCloth (3.301) because it handles single line breaks much better. Even with the option specified in the older version I found it never quite worked right.

@Johannes, with the first approach each page has a named route, so it would be about_path, contact_path, etc. With the second approach you would use the static_path('about'), static_path('contact'), or static_path(page.permalink).

@Wincent, I would like to see this too. Please consider submitting a ticket on rails.lighthouseapp.com if there isn't one already. Link it here if you want afterwards. :)

@Clemens, I left this off on purpose as it's a global way to change how the params are made, and here I prefer to keep the id for the admin interface. However, I should have mentioned episode 63 which covers how to use to_param. That's now in the show notes. Thanks.

Avatar

One thing I'm definitely missing here is overriding the to_param method of a model. That's pretty much invaluable when talking about permalinks.

Other than that - solid work as usual.

Avatar

sintaxi - maybe will be better to use some sort of caching which could be better and cleaner.

Avatar

I've often wanted Rails to give me bang methods like "find_by_permalink!" to save me the work of manually raising an ActiveRecord::RecordNotFound exception.

Avatar

I've used a similar approach to create a really small embedded wiki. However, I was never happy with the internal links between the pages. Any thoughts on how to best include links in the semi-static pages?

Avatar

Hi

I found I always get false on assertTextPresent (assert_text_present) and verifyTextPresent (verify_text_present) with selenium_on_rails while the same test passes with Selenium-IDE.

Do you have any ideas ?

I'm trying on both the original selenium_on_rails with rails-2.0.2 and your git version with rails-2.1. But the results are same...

Anyway, this episode is great.
Thank you very much.

Avatar

Good work Ryan. A few things to add:

I think it would be wise to create a content_html field in your database and apply the RedCloth filter in the model upon saving instead of in the view. This will give faster page loads because we are only applying the filter on write(not reads) and we keep our views dumb, as they should be.

Also, by default RedCloth ignores single line brakes and converts double line brakes to a new paragraph. So, to convert single line brakes to 'br' tags do like so...

RedCloth.new( @page.content, [:hard_breaks] ).to_html

Avatar

Hi Ryan,

Another great episode. Quick note for the show- your nifty generators gem:

gem install ryanb-nifty-generators --source=http://gems.github.com

Avatar

Should specify that the appended “nice link” is ignored by my route, and is only present for readability and the google-types

Avatar

Many thanks for the casts.
i am a newbie and i have a problem with this if someone could help i'l appreciate it.
I am using this code and added a select to search for different stuff and i would like to have only 1 method to do that, but i get a mysql error. the code is:
view -->
<% form_tag ('find', :method => 'get') do %>
  
  <p>
    <%= text_field_tag :search, params[:search] %>
    <%= select :user, :type, User::USER_ATTRIBUTES %>
<%= submit_tag "Buscar", :name => nil %>
</p>
controller -->
if params[:search]
   
    @users =User.find(:all, :conditions => ["%#{params[:user][:type]}% LIKE ?", "%#{params[:search]}%"])

i get this:
Mysql::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%login% LIKE '%ro%')' at line 1: SELECT * FROM `users` WHERE (%login% LIKE '%ro%'

and parameters
{"user"=>{"type"=>"login"},
 "search"=>"ro"}

Avatar

Hi Ryan,

Nice episode. I would like to know your opinion on something – when I face this issue, I still use the base show action (find(params[:id])) and create a helper for hyperlinks that append a “clean name/permalink” after the /show/:id.

For example: page title is “About us” and then my helper will change it to /show/1/about_us, or whichever trailing route I specify. I have used this method for a while and believe it would be more efficient since the find would be quicker db-wise and is not dependant on permalink, which can be dangerous in some instances if someone breaches the idea of a permalink (ie; a client not understanding the concept). Do you see any disadvantages besides the fact the ID is still present in the URL and, I guess, the possible length of some URLs?

Cheers,
Adam

Avatar

Sorry, previous post is not correct, this is what Firebug tells me:

document.observe is not a function
endless_page.js?1215380276()()

[document.observe('dom:loaded', checkScroll);

Avatar

Looks like there is an error in the endless.js file

TypeError: Value undefined (result of expression document.observe) is not object.

Line 24:
document.observe('dom:loaded', checkScroll());

What could be wrong?

Thnx.

Avatar

Hi, first, thanks for all those great videos!
I'm still curious though, that means that any user would be able to create a user, only providing a name field?

Avatar

James, I think you can kind of do BDD testing with selenium, as long as you first prototype what your front end looks like. If you make the views you want to use, or at least stay consistent with you ID values of ways of referencing fields you can write selenium cases for them before actually implementing anything. Though, then you will need to write everything by hand and the Selenium IDE will not be as useful. My development process tends to be just that, first prototype the front end, then make selenium cases for it, and then hook it up to the back end. Hope that helps.

Avatar

Thank you Ryan!

I would be interested if anyone has any thoughts of how and when to integrate Selenium alongside a BDD workflow, especially when using Rspec and Rails.

I've been preparing to write some Rspec user stories for my app, but my first impressions of Selenium are making me think again. Selenium seems to be much quicker, more intuitive and more comprehensive by including the browser into the equation.

The only thing that's holding me back from jumping headfirst into Selenium right this minute is the fact that its geared up for testing rather than specing and I'm rather keen on the BDD conventions.

Avatar

bummer that the git stuff doesn't play well with svn.

Avatar

Is this really a Find Through Association or a has_many / belongs_to.

What about a many to many 'find through' association?

Avatar

Hi Ryan!

Very good job! All of your screencasts are excellent! Maybe you want to tag all of your Mocha related casts with 'mocha'. For me, this would be helpful since I'm currently very interested in testing and mocking.

I'm a researcher at the University of Bonn, Germany where I and my colleagues are leaders of our Agile Software Development / Extreme Programming Lab.

Best regards,
Mark

Avatar

And also, what about doing the same thing for Gmail type file upload ?

In that case, your previous code, in which you used to set should_destroy? would work better.

Avatar

BUG: In case number of attributes is more than one in the partial, browser does not groups it right and sends wrong data.
Example:
Data expected :
new_part_attributes[]["ordering"]=>"1",
new_part_attributes[]["inline"]
=> "false"
new_part_attributes[]["wfile"]=>#<ActionController::UploadedStringIO:0xb602ea20>

new_part_attributes[]["ordering"]=>"2"
new_part_attributes[]["inline"]=>"true"
new_part_attributes[]=><ActionController::UploadedStringIO:0xb602cd38>

Data Send By Browser:
"new_part_attributes"=>[{"ordering"=>"1", "inline"=>"false", "wfile"=>#<ActionController::UploadedStringIO:0xb602ea20>}, {"ordering"=>"2", "inline"=>"true"}, {"wfile"=>#<ActionController::UploadedStringIO:0xb602cd38>}]

Possible solution is send index also in the array, so that it groups it right.

Avatar

I've upgraded to Rails 2.1.0 and I can't get the logger to change, so I can't group by controller actions. Without that its really hard to make anything useful out of the log. I tried Peter's pastie code for Rails > 2.0 but Active record crashes with this code in my environment.rb
Any help would be great, because this looks like a great tool.

Avatar

Another great screen cast. Keep up the nice work!

Avatar

Great screencast. I've been using Selinium for about a year now, good product and getting better all the time. About 2 weeks ago I posted a small description of how I'm testing www.graphsy.com with selenium. If anyone is interested you can check it out at:
http://www.blog.graphsy.com/?p=18
In that post I try to show how mouse events can be simulated and tested for in selenium.

Thanks for a great screencast, more people need to know about selenium.

Avatar

Great screencast, lots of info. Thanks for the heads up on the update.

Avatar

Very Cool.

Nice changes to the 2.1 git version.

I've watched almost all of your casts and look forward to them every week.

Thank you very much
Ben

PS i love the new intro sound, ;-)

Avatar

Nice!
JQuery:
http://github.com/jney/jquery.pageless/tree/master/jquery.pageless.js

Avatar

On rails 2.1 - updates I get:

can't convert String into Integer

app/models/poll.rb:47:in `[]'
app/models/poll.rb:47:in `existing_choice_attributes='
app/models/poll.rb:46:in `each'
app/models/poll.rb:46:in `existing_choice_attributes='
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2361:in `send'
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2361:in `attributes='
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2360:in `each'
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2360:in `attributes='
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2261:in `update_attributes'

If I update existing_choice_attributes's choice.id.to_s to choice.id it works, but will destroy my choices when updating the same form twice.

Avatar

If you're using file_store (such as in development mode), you can serialize to and from YAML easily.

Instead of:

def self.all_cached
  Rails.cache.fetch('Thing.all') { all }
end

Use this:

def self.all_cached
  yml = Rails.cache.fetch('Thing.all') { all.to_yaml }
  YAML::load(yml)
end

There are ways to make it smart enough to only serialize to YAML if the cache is configured to use file_store, to avoid the overhead of serialization when using memory_store or mem_cache_store.