@Ryan, Arya's suggestion should work. Alternatively you can try overriding the "site" class method:
def self.site
"http://#{name}:#{pass}@example.com"
end
I don't know if that is supported, so you'll want to thoroughly test it.
@nicolash, great question! From my understanding ActiveResource isn't really designed to work with other APIs which don't follow the same REST interface. I believe it will take too much customization to the point you lose most of the benefits of ActiveResource. You're probably better off using a plugin designed specifically for that API or writing one yourself. Someone please correct me if I'm wrong.
@Rome, there is a :path_prefix option for map.resources which should do the job:
map.resources :articles, :path_prefix => '/blog'
@Wesley, yes, the partial is assumed to be in the controller with the same name (no matter what controller you're currently in). Usually this is an acceptable assumption, but if you don't want this behavior it's best to define the location manually.
@Anton, if you want the show, edit, and new actions to use all the same view you can create just one (show.html.erb) and define that in the other actions:
def new
#...
render :action => 'show'
end
If there are minor differences then I recommend you stick with a partial.
The best way I can think of doing that is having a method that simply redefines the site variable for every username/password. For example in your active resource model, you could have a method:
def assign_to_user(username, password)
self.class.site = "http://#{username}:#{password}..."
end
and something similar as a class method too. As of now, when site is assigned, it immediately sends it to the URI class to parse which unfortunately means having something like a proc that gets called at the time of the actual request would require quite a few changes.
I hope I was helpful. :)
Thanks so much Ryan Bates! Your screen casts are very helpful, I particularly liked the refactoring screen cast.
Thanks for the reply Ryan - I'll check this out when I get the chance - I think I might be using different form helper methods, which might explain why the parameters aren't passed...
Thank you for the insights...
If it is not to advanced it would be nice if you show us how to communicate with apis that don't follow the best-practise of REST.
First example that comes into my mind is flickr. The first stumbling block is that the url you need to generate doesn't include the ".xml", or the resource (nor the id where appropriate). Basically what you need is a fixed url with everything! else in the querystring...
I guess you would have to overwrite the url generation for ActiveResource but i don't even know where to start. Beside i guess it is just the beginning of the problems as i guess the next step would be to parse the response xml by hand... Do you intend to go into these kinds of problems or do you consider this as to advanced/specialized problems?
(BTW,Put the champagne into the fridge you deserve to treat yourself and be proud for the work you've done so far for the community.. we are looking forward for an other 1000;-) screencasts once you finalized with the first 100. THANK YOU so far)
@Jean, I think returning 404 is an excellent solution, especially if you have a User model setup. You can then fetch resources through the user model and rails will handle the 404 automatically for you. For example, let's say a User has many Projects and you only want the user to have access to his own project. In the controller show action you can do this:
current_user.projects.find(params[:id])
This way the user can only fetch the project he owns. If he doesn't own it, he will receive a 404.
I still have not resolved that question in my mind... if someones accesses a URL they should not, is redirecting the correct action?
I mean, it indicates your application will respond again and again to it.
On one project I did I simply determined to return a 404 (not found) when a protected resource was accessed. Indicating to the client they should not come back to that URI.
@oin, that's very strange. I'm using the Animation codec for the .mov files, so that might be a problem. The .m4v is H.264 so I would imagine that would work. I exported them using QuickTime Pro.
@David, are you referring to some kind of separate "advanced search" page? With this you can create a custom REST action as shown here:
http://railscasts.com/episodes/35
I think it's acceptable to create a "search" action which behaves similar to index except it is dedicated to searching.
@PJ, good point. Honestly I haven't looked into the security risks. It's probably not the safest thing if the sessions are stored in the cookie as is the default with Rails 2. Alternatively you can do this:
@Ned, will_paginate should automatically pass the params. Make sure to use GET request in your search form along with the helper methods which end in "_tag" (such as "text_field_tag" and "check_box_tag"). See this episode for details:
http://railscasts.com/episodes/37
@Sean, for fully RESTful controllers I usually start with very minimal tests. It's actually more minimal than what I'm showing here. Here's an example:
http://pastie.caboo.se/159422
For the most part I'm just testing the response. Even though this willl likely result in 100% test coverage with rcov, it's not really. For example I'm not actually testing the update of the attributes in the update action. I feel it's okay to assume this kind of behavior for a standard RESTful controller. This allows me to focus more on the exceptions to the normal REST than the expected behavior.
For example, if you have some kind of authorization then it's fairly easy to add some tests for this. But if the starting specs are too complex then this exceptional behavior can easily become lost in the specs.
@Damian, the colors are from rspec. But if you want them with test/unit then I think there's a gem called redgreen which can do this.
Would anyone know how to use this in conjunction with form parameters? I have a form, the parameters of which go into my find / paginate function. This works fine for the first results page, but when a link is followed to subsequent pages, the search parameters are lost. Is there a way to pass a parameters hash via the will_paginate helper method in the view???
The problem is explained in more detail here:
http://www.railsforum.com/viewtopic.php?id=14604
@drozzy, I suppose I've been a little stressed lately. But good news is I'm off work this week so I have time to clear off the stuff that's been building up on my plate. Thank for your concern.
@mike b, in Ruby, instance variables stick around as long as the object exists. In Rails this controller object sticks around for the entire web request. So if you call current_user multiple times during that one request the User.find call will only happen once.
@Wallen, as far as I know there's no "if" option. So if you want to disable caching entirely you should look into a plugin as mentioned above. Actually, I've never tried returning nil for the cache path so that might work too.
Ryan, spectacular job on the Webcasts. Helps demystify the rails magic. This is a great tip but I am not clear on how it works? I thought instance variables only live as long as the routine is called. So every time you call current_user, isn't @current_user destroyed? Why isn't it? Is that rails or ruby at work? I assume rails.
One thing to note is that if your code is
dependent on the id of a model( I know, this is probably a bad implementation, but for right now, that is the way I have it), and so you put the id in the fixture so that your test will pass, for some reason, the fixture that belongs_to needs to have the point to the id of the fixture and not use the new shortcut of the name. i.e.
Model Timecard
belongs_to :hour_type
...
end
Model HourType
has_many: timecards
def vacation_hours
return self.hours if self.hour_type_id = 2
....
end
then in your fixtures
##RAILS_ROOT/rspec/fixtures/hour_types.yml
regular:
name: Regular
id: 1
vacation:
name: Vacation
id: 2
##RAILS_ROOT/rspec/fixtures/timecards.yml
weekla:
hours: 40
<b>hour_type_id: 1</b>
......
-------------------------------------
If you change the implementation you have to do this, but I thought I would mention the problem I had with this. It there is another solution to this, please let me know.
@Phil, good question, what if you already have a Product model? Looking at the source it appears it's possible to set the element_name or collection_name like this in the class:
self.element_name = 'product'
That way you can give the class another name such as RemoteProduct.
@oin, I'll look into providing the movies in an alternative (flash?) format. Thanks for the feedback.
same problem adam and tellman, have a has_many/belongs_to relationship between restaurant and hour objects. it looks like this:
restaurants.yml
rest:
name: rest
hours.yml
lunch:
restaurant: rest
when i run it, i'm told there's no column in lunch called restaurant, so it doesn't know to add the id. oddly, if i change the line to restaurants: rest, it knows to hash the value to an id. of course, it still complains that there is no column because it's then looking for restaurants_id instead of restaurant_id.
Could you please, pretty please use some other program to encode your videos? I don't own a Mac and every player I try to play your screencasts with fails to read its index and so I can't
Great tutorial. However, if a validation error occurs on one of the fields on the same page and the page is re-rendered with an error message the state drop down gets messed up. The prompt message is no longer there and the first state in the DB is there instead. Any ideas....?
Any idea how you'd manage validation for a check box like this, or more specifically, how check boxes could be highlighted if none are ticked?
If you put something like:
validates_presence_of :category_ids
in the products model ( to ensure that each product had at least one category box ticked) it's possible to display error messages when a form is submitted without any of the category boxes ticked, but how would you *highlight* the offending tickboxess?
Does ActiveResource know that its looking for a Product controller because its called Product.rb? what if you already had a product model? how could you tell ActiveResorce to look for a product controller at that site?
Thanks for that episode.
Just yesterday I was thinking about how to do the communication between two rails applications. So this is a really big help for me :-)
I'm looking forward to the next episode on this topic :-)
@Brad, the plugin I was referring to is called Action Cache. However I'm not entirely sure if it works with Rails 2.0, there are alternative solutions as Andrew mentioned.
http://wiki.rubyonrails.org/rails/pages/Action+Cache+Update+Plugin
@Ted, yep, that should work. I prefer the more descriptive names but to each his own. :)
@Mitchell, in order to get this to work with pagination you'll need to include the page number in the name of the cache. You can use the dynamic technique as shown at the end of this episode to put the params[:page] value in the name of the cache and it should work.
@Andrew, you're right, the domain name gets chopped when passing a string as the path. This behaves the same in fragment caching as well. I can see how either behavior could be desirable. If you do want the domain with the port in the path then you can include that in the string.
@Ryan, Arya's suggestion should work. Alternatively you can try overriding the "site" class method:
def self.site
"http://#{name}:#{pass}@example.com"
end
I don't know if that is supported, so you'll want to thoroughly test it.
@nicolash, great question! From my understanding ActiveResource isn't really designed to work with other APIs which don't follow the same REST interface. I believe it will take too much customization to the point you lose most of the benefits of ActiveResource. You're probably better off using a plugin designed specifically for that API or writing one yourself. Someone please correct me if I'm wrong.
@Rome, there is a :path_prefix option for map.resources which should do the job:
map.resources :articles, :path_prefix => '/blog'
@Wesley, yes, the partial is assumed to be in the controller with the same name (no matter what controller you're currently in). Usually this is an acceptable assumption, but if you don't want this behavior it's best to define the location manually.
@Anton, if you want the show, edit, and new actions to use all the same view you can create just one (show.html.erb) and define that in the other actions:
def new
#...
render :action => 'show'
end
If there are minor differences then I recommend you stick with a partial.
Congratz on the year anniversary! Your screencasts trump every Ruby/Rails book I've yet read... and for FREE no less! Thanks!
Ok never mind I got it.
We need to have an object "State" in models/state.rb and I don't have such an object in my project.
The pastie for textmate helped me to understand http://pastie.caboo.se/144239 ;)
I have a problem when trying to use this tutorial :(
I get this error message :
uninitialized constant StateSweeper::State
RAILS_ROOT: C:/developement/ruby/minotaur
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:478:in `const_missing'
app/sweepers/state_sweeper.rb:3
-e:2:in `load'
-e:2
I double checked and I don't see what I forgot :(
Sweeping doc is not really helpful either:
http://rails.rubyonrails.com/classes/ActionController/Caching/Sweeping.html
any idea ?
@Ryan Townsend
The best way I can think of doing that is having a method that simply redefines the site variable for every username/password. For example in your active resource model, you could have a method:
def assign_to_user(username, password)
self.class.site = "http://#{username}:#{password}..."
end
and something similar as a class method too. As of now, when site is assigned, it immediately sends it to the URI class to parse which unfortunately means having something like a proc that gets called at the time of the actual request would require quite a few changes.
I hope I was helpful. :)
Thanks so much Ryan Bates! Your screen casts are very helpful, I particularly liked the refactoring screen cast.
Thanks for the reply Ryan - I'll check this out when I get the chance - I think I might be using different form helper methods, which might explain why the parameters aren't passed...
Thank you for the insights...
If it is not to advanced it would be nice if you show us how to communicate with apis that don't follow the best-practise of REST.
First example that comes into my mind is flickr. The first stumbling block is that the url you need to generate doesn't include the ".xml", or the resource (nor the id where appropriate). Basically what you need is a fixed url with everything! else in the querystring...
I guess you would have to overwrite the url generation for ActiveResource but i don't even know where to start. Beside i guess it is just the beginning of the problems as i guess the next step would be to parse the response xml by hand... Do you intend to go into these kinds of problems or do you consider this as to advanced/specialized problems?
(BTW,Put the champagne into the fridge you deserve to treat yourself and be proud for the work you've done so far for the community.. we are looking forward for an other 1000;-) screencasts once you finalized with the first 100. THANK YOU so far)
If your storing the user and pass in the site within the model, how would you dynamically change that based upon what user is logged into to the app?
So for example being able to do:
self.site = 'http://' + un + ':' + pw + '@localhost:3000/'
Was waiting for this screencast :) I had no more railscast to see and I am almost finishing the design patterns on ruby book :)
ActiveResource seems like a very powerful backend to data models and I think we got a keeper here.
Other frameworks will copy this in no time :)
How would I use a path prefix to my restful routes?
ex.
Instead of "http://mysite.com/articles/1...
Have "http://mysite.com/blog/articles/1...
Do i make a separate controller?
script/generate controller Blog::Articles ?
Or is it something simple that I'm completely overlooking?
Is there any reason the code you have on this screen cast shouldn't work with rails 2.0?
My code matches your code to the character and i get an rjs error.
@Jean, I think returning 404 is an excellent solution, especially if you have a User model setup. You can then fetch resources through the user model and rails will handle the 404 automatically for you. For example, let's say a User has many Projects and you only want the user to have access to his own project. In the controller show action you can do this:
current_user.projects.find(params[:id])
This way the user can only fetch the project he owns. If he doesn't own it, he will receive a 404.
Ryan - thanks again!
You have a knack for making episodes about exactly the problem I'm tackling.
Loved the tutorial... Great. I am using many of your videos to learn more about rails... excellent idea of teaching stuff...
cheers
raghav..
I still have not resolved that question in my mind... if someones accesses a URL they should not, is redirecting the correct action?
I mean, it indicates your application will respond again and again to it.
On one project I did I simply determined to return a 404 (not found) when a protected resource was accessed. Indicating to the client they should not come back to that URI.
You got your thoughts on this dilemma?
@oin, that's very strange. I'm using the Animation codec for the .mov files, so that might be a problem. The .m4v is H.264 so I would imagine that would work. I exported them using QuickTime Pro.
@David, are you referring to some kind of separate "advanced search" page? With this you can create a custom REST action as shown here:
http://railscasts.com/episodes/35
I think it's acceptable to create a "search" action which behaves similar to index except it is dedicated to searching.
@PJ, good point. Honestly I haven't looked into the security risks. It's probably not the safest thing if the sessions are stored in the cookie as is the default with Rails 2. Alternatively you can do this:
session[:admin] = true
Which I believe will work okay.
@Mikael, yep! See Sake:
http://errtheblog.com/posts/60-sake-bomb
@Ned, will_paginate should automatically pass the params. Make sure to use GET request in your search form along with the helper methods which end in "_tag" (such as "text_field_tag" and "check_box_tag"). See this episode for details:
http://railscasts.com/episodes/37
@Sean, for fully RESTful controllers I usually start with very minimal tests. It's actually more minimal than what I'm showing here. Here's an example:
http://pastie.caboo.se/159422
For the most part I'm just testing the response. Even though this willl likely result in 100% test coverage with rcov, it's not really. For example I'm not actually testing the update of the attributes in the update action. I feel it's okay to assume this kind of behavior for a standard RESTful controller. This allows me to focus more on the exceptions to the normal REST than the expected behavior.
For example, if you have some kind of authorization then it's fairly easy to add some tests for this. But if the starting specs are too complex then this exceptional behavior can easily become lost in the specs.
@Damian, the colors are from rspec. But if you want them with test/unit then I think there's a gem called redgreen which can do this.
@oin, fixed. Thanks for the suggestion.
@Ryan: I don't think the format is the problem, since I can seek perfectly in other .movs or .m4vs, but not yours
I forgot to say: thanks for the great railscasts!
you should really put rspec in it's name, as i searched for it quite a while before actually diving in to see what it was about
Would anyone know how to use this in conjunction with form parameters? I have a form, the parameters of which go into my find / paginate function. This works fine for the first results page, but when a link is followed to subsequent pages, the search parameters are lost. Is there a way to pass a parameters hash via the will_paginate helper method in the view???
The problem is explained in more detail here:
http://www.railsforum.com/viewtopic.php?id=14604
Very useful! I just used your tutorial to automize a huge time leech in my creation of projects. Thank you.
Is it possible to define "global" rake tasks, which are available to all projects, a la ~/.rails/generators? ~/.rails/tasks isn't searched.
I'm a little on the new side but I thought you were never supposed to store a password in the session? Isn't that a big security no-no?
@drozzy, I suppose I've been a little stressed lately. But good news is I'm off work this week so I have time to clear off the stuff that's been building up on my plate. Thank for your concern.
@mike b, in Ruby, instance variables stick around as long as the object exists. In Rails this controller object sticks around for the entire web request. So if you call current_user multiple times during that one request the User.find call will only happen once.
@Wallen, as far as I know there's no "if" option. So if you want to disable caching entirely you should look into a plugin as mentioned above. Actually, I've never tried returning nil for the cache path so that might work too.
Excellent tutorial! I did not feel cheated ;).
Why bother caching the admin page? Is it not possible to cache_action :if ?
Ryan, spectacular job on the Webcasts. Helps demystify the rails magic. This is a great tip but I am not clear on how it works? I thought instance variables only live as long as the routine is called. So every time you call current_user, isn't @current_user destroyed? Why isn't it? Is that rails or ruby at work? I assume rails.
thanks again for your dedication to the community
I am having the same problem as Jonathan with the NameError.
One thing to note is that if your code is
dependent on the id of a model( I know, this is probably a bad implementation, but for right now, that is the way I have it), and so you put the id in the fixture so that your test will pass, for some reason, the fixture that belongs_to needs to have the point to the id of the fixture and not use the new shortcut of the name. i.e.
Model Timecard
belongs_to :hour_type
...
end
Model HourType
has_many: timecards
def vacation_hours
return self.hours if self.hour_type_id = 2
....
end
then in your fixtures
##RAILS_ROOT/rspec/fixtures/hour_types.yml
regular:
name: Regular
id: 1
vacation:
name: Vacation
id: 2
##RAILS_ROOT/rspec/fixtures/timecards.yml
weekla:
hours: 40
<b>hour_type_id: 1</b>
......
-------------------------------------
If you change the implementation you have to do this, but I thought I would mention the problem I had with this. It there is another solution to this, please let me know.
Perhaps it would also be worth mentioning that you can combine conditions like this:
Task.find_all_by_complete_and_category_id(false, 1)
@Phil, good question, what if you already have a Product model? Looking at the source it appears it's possible to set the element_name or collection_name like this in the class:
self.element_name = 'product'
That way you can give the class another name such as RemoteProduct.
@oin, I'll look into providing the movies in an alternative (flash?) format. Thanks for the feedback.
same problem adam and tellman, have a has_many/belongs_to relationship between restaurant and hour objects. it looks like this:
restaurants.yml
rest:
name: rest
hours.yml
lunch:
restaurant: rest
when i run it, i'm told there's no column in lunch called restaurant, so it doesn't know to add the id. oddly, if i change the line to restaurants: rest, it knows to hash the value to an id. of course, it still complains that there is no column because it's then looking for restaurants_id instead of restaurant_id.
anyone know what's wrong/i'm doing wrong?
cheers, h
seek inside the video at all. I tried mplayer/xine/vlc on Linux
Could you please, pretty please use some other program to encode your videos? I don't own a Mac and every player I try to play your screencasts with fails to read its index and so I can't
Rails is so cool!
Ryan, do you want to do a railscast about TimeZones like TzTime or the new edge zone?
Wow, that was really interesting.
It's good to know it can be so easily implemented.
Thanks once again Ryan.
This is pretty cool - I hadn't heard of ActiveResource before.
Hey Ryan,
Great tutorial. However, if a validation error occurs on one of the fields on the same page and the page is re-rendered with an error message the state drop down gets messed up. The prompt message is no longer there and the first state in the DB is there instead. Any ideas....?
Any idea how you'd manage validation for a check box like this, or more specifically, how check boxes could be highlighted if none are ticked?
If you put something like:
validates_presence_of :category_ids
in the products model ( to ensure that each product had at least one category box ticked) it's possible to display error messages when a form is submitted without any of the category boxes ticked, but how would you *highlight* the offending tickboxess?
Does ActiveResource know that its looking for a Product controller because its called Product.rb? what if you already had a product model? how could you tell ActiveResorce to look for a product controller at that site?
I also think that ActiveResource is a great way to access remote data. In fact I am using it to store and access data in Amazon's SimpleDB webservice.
If anybody wants to dig into this, check out my little Tutorial over at the Amazon AWS site:
http://developer.amazonwebservices.com/connect/entry.jspa?categoryID=152&externalID=1242
Thanks for that episode.
Just yesterday I was thinking about how to do the communication between two rails applications. So this is a really big help for me :-)
I'm looking forward to the next episode on this topic :-)
Sorry for the delayed response everyone.
@Brad, the plugin I was referring to is called Action Cache. However I'm not entirely sure if it works with Rails 2.0, there are alternative solutions as Andrew mentioned.
http://wiki.rubyonrails.org/rails/pages/Action+Cache+Update+Plugin
@Ted, yep, that should work. I prefer the more descriptive names but to each his own. :)
@Mitchell, in order to get this to work with pagination you'll need to include the page number in the name of the cache. You can use the dynamic technique as shown at the end of this episode to put the params[:page] value in the name of the cache and it should work.
@Andrew, you're right, the domain name gets chopped when passing a string as the path. This behaves the same in fragment caching as well. I can see how either behavior could be desirable. If you do want the domain with the port in the path then you can include that in the string.
I found the multibutton form can be work only for form_for, but not for form_remote_for
stupid transposition of column type and name. sorry.