After getting something happening with the '/create' stuck on the end of the curl command, the problem was that I was passing 'users[name ...' instead of 'user[name...'.
That pluralization stuff again !
I figured it out by looking at the Html source for the form code and also with the help of a brief curl tutorial that had an example http://curl.haxx.se/docs/httpscripting.html
At first I thought mabye it was because the web-browser created data was passing "commit"=>"Create","action"=>"create", "controller"=>"testusers"} parameters, and the curl commands wasn't, but that wasn't the problem.
I'm still wondering how your example worked ... I see that you have a "redrecting message" popup, so I assume you put a redirect inside your users.rhtml file to create .. just for the purpose of this demo ?
Mike
P.s. I also wasted a lot of time trying to get 'rake db:migrate' to work, might I suggest you do a railscast on how it works, specifically the versioning system. I found out on the railsforum that the magic commands were "rake db:migrate VERSION=0 ... folowed by rake db:migrate".
@Mike, unlike a web browser, curl doesn't automatically follow redirects. That is why you're seeing both "create" and "index" in the log when using a web browser, but only "create" when using curl.
If you're getting null values when using curl, I'm guessing it's because the attribute names aren't correct. Make sure you're naming the attributes in curl exactly how I'm doing in this episode (model name followed by the attribute name in square brackets).
Normally you don't need to bother with curl, I just used it for demonstration purposes to show how a hacker might break into a site.
@Barry: In controllers, there are lots of tricks using mocha to get rid of fixtures. For example, if you want to test your create action, you can use the any_instance method.
Product.any_instance.stubs(:valid?).returns(true)
That way you can test how the controller behaves without depending on how the model behaves (bypassing validations).
When I submit a new user through the web browser (I'm just using a barebones scaffolded interface), I see in the dev log 3 entries, one each for the Controller#new, #create, then #list actions.
But when I try using curl as per your example, I'm only getting an #index log entry when I pass ../users as the last URL element.
If I pass .../new as the last element, I get a #new log entry, but not a #create.
If I pass ../create I do get a #create log /controller entry, and the log shows the parameters ARE passed, but the SQL statements are inserting NULL values !
This is all before I add any of the modifications you suggest for protection.
@Ryan: What you you using to run those 'auto-tests'? Looks interesting.
I would like to add that I started skipping fixtures as well, mainly from the point that after a couple of weeks, I can never remember what data was where or how I named the fixture records. So I create a protected method at the bottom of the test case and simply build the records there:
Secondly, I really like using the db for all tests. I know it's slower, but I have caught db problems in the tests that I would not have seen if it was not r/w to the db.
A bit off topic, but can you post the .css code for this episode? I'm sure it's pretty simple, but I haven't done much .csss so the more examples I can see the better.
Thanks for another great Railscast. I can never stress enough (to everyone) the need for testing apps. The more info that is there to help make it easier for all of us, the better off we all are. Thanks again.
Thanks for this Railscast. I've been trying to crack the egg of fixtureless testing...looking for a primer. Now to find something that improves my controller testing experience.
@InMan, I don't use fixtures at all in the model (unit test) layer. However, I still use fixtures in the controller and view layers (functional tests) just so the site has some filler data. Those tests don't rely very heavily on the data inside the fixtures so it's not much of a problem.
This generator cast was great. I'd been waiting for something to be broadcast so clearly regarding generators. Thanks a lot Ryan. For everyone else, I am trying something a little different and ran into some trouble. Would people mind taking a look at my post at http://railsforum.com/viewtopic.php?id=7711 and helping a brother out?
The named route mentioned in the link_to just returns the URL to the action, it doesn't include the HTTP method used to access it. I suppose it would be nice if you could embed the HTTP verb inside of the URL, but that's not really possible.
Thanks for these screencasts - they are brilliant. Everybody seems to be talking about REST, but this is one of the only resources I can find that introduces RESTful Rails. More on the topic would be great.
I have one question though - how come you need to specify the fact that the complete action was a :put in the routes.rb file and then again in the link_to method? I would have thought that once it has been set up in the routes.rb file that should be enough?
Editing a product doesn't seem to allow the creation of a new category, only creating a new product. I followed the implementation given in the screencast fairly closely, so do you think you could offer a guess as to what might be going wrong? Cheers.
I'm having a bit of trouble. I'm storing my generator in the plugins dir. C:\projects\cap\vendor\plugins\app_layout\generators\app_layout
I'm getting the following when I try and run ruby script/generate app_layout.
c:/ruby/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/rails_generator/base.rb:122:in `manifest': No manifest for 'app_layout' generator. (NotImplementedError)
@vlad, good point. You would need to store the history of the edits with acts_as_versioned or something so you can compare it with the new edits and see who changed what. Then it would be possible to see the two changes didn't overlap (one changed the price and the other changed the description) so it can be resolved behind the scenes.
Alternatively you could pass the model's attributes in hidden fields in addition to the editable fields. That way you have the last history without using act_as_versioned. That might be messy though.
hmm, it seems kind of hard to try to resolve the confict "behind the scenes" without keeping any additional information about the edits.
i think you can do something like a form with radio buttons to choose between the conflicting field(s), but that won't work in case of 2 concurrent changes to the same field, which brings me back to the manual approach. i'm starting to wonder if there's a better way to resolve these dependencies without additional record-keeping.
About this episode: I've added the "Create Model Through Text Field" into
one of my test projects. It's been fast and simple thanks to your tutorial !
I've just modified the callback function to check the uniqueness of the new
record!
def create_new_category_from_name
unless new_category_name.blank?
if category = Category.find(:first, :conditions => {:name => new_category_name}) then
self.category_id = category
else
create_category(:name => new_category_name)
end
end
end
Note: in edge rails you can now store generators within RubyGems' /generators or /rails_generators folder. Similarly, plugins you can now store generators within /rails_generators as well as the existing /generators folder support.
Hi Ryan, I've recently discovered your site and can I just say what a great asset it is. Keep up the good work and apologies in advance for systematically downloading episode after episode!
@JT, I wouldn't use this technique if you have to fill in more than one attribute for the related model. That can get pretty messy in the code. Instead, fields_for seems like a better solution that way you can group it in a second hash and not have to create a virtual attribute for every related attribute. Maybe I'll make a screencast on this in the future.
@Oskar, this is on my todo list for a future episode. :)
@Ioribox, That "f" variable is a form builder which was created through form_for. I'm just passing it as a local variable to the partial which is why you don't see the form_for statement.
@jaffet, not sure what the problem is. It should work in development mode which is the mode I was in for this screencast. What version of Rails are you using?
First of all THANK YOU for your great work !!!
There is one thing I can't understand in your sample:
In the view I use " text_field 'form', 'id' "...
what is " f.text_field 'id' " instead?
How about the other way around. When I create a category I would like to create a couple of products belonging to that category as well. I've realised there is something like category.products.create but I dont know how to use it. Any pointers?
I never got this to work. I can see in the console, that the tasks_count has value when adding new tasks, but when finding all projects (with tasks) using project.tasks.size -method, I still see the "SELECT count(*) AS count_all FROM..." with as many ids as the projects. I have the counter_cache => true value in model. This is strange? (Is this only working in production -state?)
@Mike, glad you got it working. I the redirect is done in the controller after the model saves. It isn't done in an rhtml file.
Ryan,
I got it working ;)
After getting something happening with the '/create' stuck on the end of the curl command, the problem was that I was passing 'users[name ...' instead of 'user[name...'.
That pluralization stuff again !
I figured it out by looking at the Html source for the form code and also with the help of a brief curl tutorial that had an example http://curl.haxx.se/docs/httpscripting.html
At first I thought mabye it was because the web-browser created data was passing "commit"=>"Create","action"=>"create", "controller"=>"testusers"} parameters, and the curl commands wasn't, but that wasn't the problem.
I'm still wondering how your example worked ... I see that you have a "redrecting message" popup, so I assume you put a redirect inside your users.rhtml file to create .. just for the purpose of this demo ?
Mike
P.s. I also wasted a lot of time trying to get 'rake db:migrate' to work, might I suggest you do a railscast on how it works, specifically the versioning system. I found out on the railsforum that the magic commands were "rake db:migrate VERSION=0 ... folowed by rake db:migrate".
@Mike, unlike a web browser, curl doesn't automatically follow redirects. That is why you're seeing both "create" and "index" in the log when using a web browser, but only "create" when using curl.
If you're getting null values when using curl, I'm guessing it's because the attribute names aren't correct. Make sure you're naming the attributes in curl exactly how I'm doing in this episode (model name followed by the attribute name in square brackets).
Normally you don't need to bother with curl, I just used it for demonstration purposes to show how a hacker might break into a site.
@Mahmoud, yep. The default scaffolding will be RESTful in Rails 2.0.
@Barry: In controllers, there are lots of tricks using mocha to get rid of fixtures. For example, if you want to test your create action, you can use the any_instance method.
Product.any_instance.stubs(:valid?).returns(true)
That way you can test how the controller behaves without depending on how the model behaves (bypassing validations).
Ryan,
Thanks for the link.
Another q, might be a curl related issue.
When I submit a new user through the web browser (I'm just using a barebones scaffolded interface), I see in the dev log 3 entries, one each for the Controller#new, #create, then #list actions.
But when I try using curl as per your example, I'm only getting an #index log entry when I pass ../users as the last URL element.
If I pass .../new as the last element, I get a #new log entry, but not a #create.
If I pass ../create I do get a #create log /controller entry, and the log shows the parameters ARE passed, but the SQL statements are inserting NULL values !
This is all before I add any of the modifications you suggest for protection.
Any ideas ?
Mike
That does mean a default RESTful design of applications ??
@Mahmoud, the scaffolding in edge Rails uses what is currently called scaffold_resource. This doesn't do any pagination.
@Mike, the CSS code is very similar to the one I use in the episode about the generator script. You can find the episode and the code here:
http://railscasts.com/episodes/58
The file is in "app_layout_generator.zip".
Thanks Ryan, this is the only thing I needed when I decided to move to the edge Rails!
Just a curiosity question: What the scaffolding in rails 2.0 will generate to paginate pages?
Karl, It's ZenTest probably.
@Ryan: What you you using to run those 'auto-tests'? Looks interesting.
I would like to add that I started skipping fixtures as well, mainly from the point that after a couple of weeks, I can never remember what data was where or how I named the fixture records. So I create a protected method at the bottom of the test case and simply build the records there:
protected
def create_broadcast_month(options = {})
BroadcastMonth.create({
:end_date => Date.new(2007,12,13),
:start_date => Date.new(2007,1,1)
}.merge(options))
end
Secondly, I really like using the db for all tests. I know it's slower, but I have caught db problems in the tests that I would not have seen if it was not r/w to the db.
Thanks for another great rcast!
Ryan,
A bit off topic, but can you post the .css code for this episode? I'm sure it's pretty simple, but I haven't done much .csss so the more examples I can see the better.
thx
Mike
Thanks for another great Railscast. I can never stress enough (to everyone) the need for testing apps. The more info that is there to help make it easier for all of us, the better off we all are. Thanks again.
Thanks for this Railscast. I've been trying to crack the egg of fixtureless testing...looking for a primer. Now to find something that improves my controller testing experience.
Great! I was hoping for some testing tips soon. Any chance we can look forward to some more? ;)
@InMan, I don't use fixtures at all in the model (unit test) layer. However, I still use fixtures in the controller and view layers (functional tests) just so the site has some filler data. Those tests don't rely very heavily on the data inside the fixtures so it's not much of a problem.
Im using version 1.2.3
Excelent!
Something new for me :).
Well, its easier to read some tests...
With fixtures, its faster to make tests :P.
How about you.. using fixtures or doing like you showed? :).
good,mocha!
manifest not mainfest, derrr. Creating this generator is the sickest thing I've done yet with rails. Thanks Ryan. Railscasts.com is the bomb!!!!!!!
This generator cast was great. I'd been waiting for something to be broadcast so clearly regarding generators. Thanks a lot Ryan. For everyone else, I am trying something a little different and ran into some trouble. Would people mind taking a look at my post at http://railsforum.com/viewtopic.php?id=7711 and helping a brother out?
Thanks,
Bryce
Thanks Ryan, that's what I thought, so I should specify "priority NOT IN (?)", in the string.
@Bob, I don't think that's possible. You'll have to use a string for the conditions.
Any idea how to specify exclusion criteria? For example, what is the syntax for excluding records with priority 1 & 3 using the hash for input.
I realised I had a required field in the Category model which wasn't being passed a value, fixing that sorted it out. Thanks for the help though.
The named route mentioned in the link_to just returns the URL to the action, it doesn't include the HTTP method used to access it. I suppose it would be nice if you could embed the HTTP verb inside of the URL, but that's not really possible.
@Benedict, check the code inside of the Product model. Make sure you set the callback up so it creates a Category model when it is saved.
Hi Ryan,
Thanks for these screencasts - they are brilliant. Everybody seems to be talking about REST, but this is one of the only resources I can find that introduces RESTful Rails. More on the topic would be great.
I have one question though - how come you need to specify the fact that the complete action was a :put in the routes.rb file and then again in the link_to method? I would have thought that once it has been set up in the routes.rb file that should be enough?
thanks again,
DAZ
Editing a product doesn't seem to allow the creation of a new category, only creating a new product. I followed the implementation given in the screencast fairly closely, so do you think you could offer a guess as to what might be going wrong? Cheers.
@rudy, does your app layout generator have a "manifest" method?
I'm having a bit of trouble. I'm storing my generator in the plugins dir. C:\projects\cap\vendor\plugins\app_layout\generators\app_layout
I'm getting the following when I try and run ruby script/generate app_layout.
c:/ruby/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/rails_generator/base.rb:122:in `manifest': No manifest for 'app_layout' generator. (NotImplementedError)
Any Suggestions?
Yep, it's a plugin by Rick Olson:
http://svn.techno-weenie.net/projects/plugins/acts_as_versioned/
oh, so there's an acts_as_versioned? awesome.
@LoriBox, as an alternative you can use find_or_create_by_name method:
http://pastie.caboo.se/79856
@vlad, good point. You would need to store the history of the edits with acts_as_versioned or something so you can compare it with the new edits and see who changed what. Then it would be possible to see the two changes didn't overlap (one changed the price and the other changed the description) so it can be resolved behind the scenes.
Alternatively you could pass the model's attributes in hidden fields in addition to the editable fields. That way you have the last history without using act_as_versioned. That might be messy though.
dudzjosh: he's using iTerm
hmm, it seems kind of hard to try to resolve the confict "behind the scenes" without keeping any additional information about the edits.
i think you can do something like a form with radio buttons to choose between the conflicting field(s), but that won't work in case of 2 concurrent changes to the same field, which brings me back to the manual approach. i'm starting to wonder if there's a better way to resolve these dependencies without additional record-keeping.
@dudzjosh : I think he's using ITerm
Sorry this is just a bit off topic just wondering what tab terminal software you are using?
Thank you so much for your replay!
About this episode: I've added the "Create Model Through Text Field" into
one of my test projects. It's been fast and simple thanks to your tutorial !
I've just modified the callback function to check the uniqueness of the new
record!
def create_new_category_from_name
unless new_category_name.blank?
if category = Category.find(:first, :conditions => {:name => new_category_name}) then
self.category_id = category
else
create_category(:name => new_category_name)
end
end
end
THANK YOU AGAIN
Good show. Locking can be a real pain to understand and implement!
Note: in edge rails you can now store generators within RubyGems' /generators or /rails_generators folder. Similarly, plugins you can now store generators within /rails_generators as well as the existing /generators folder support.
Hi Ryan, I've recently discovered your site and can I just say what a great asset it is. Keep up the good work and apologies in advance for systematically downloading episode after episode!
@JT, I wouldn't use this technique if you have to fill in more than one attribute for the related model. That can get pretty messy in the code. Instead, fields_for seems like a better solution that way you can group it in a second hash and not have to create a virtual attribute for every related attribute. Maybe I'll make a screencast on this in the future.
@Oskar, this is on my todo list for a future episode. :)
@Ioribox, That "f" variable is a form builder which was created through form_for. I'm just passing it as a local variable to the partial which is why you don't see the form_for statement.
@jaffet, not sure what the problem is. It should work in development mode which is the mode I was in for this screencast. What version of Rails are you using?
First of all THANK YOU for your great work !!!
There is one thing I can't understand in your sample:
In the view I use " text_field 'form', 'id' "...
what is " f.text_field 'id' " instead?
Great episode
How about the other way around. When I create a category I would like to create a couple of products belonging to that category as well. I've realised there is something like category.products.create but I dont know how to use it. Any pointers?
Thanks
This is great for simple models, but does it expand well for more complex models?
How many fields is this wise to do with before you should consider a fields_for option?
I never got this to work. I can see in the console, that the tasks_count has value when adding new tasks, but when finding all projects (with tasks) using project.tasks.size -method, I still see the "SELECT count(*) AS count_all FROM..." with as many ids as the projects. I have the counter_cache => true value in model. This is strange? (Is this only working in production -state?)