@rupert: I ended up just faking it by always tracking the currently loaded URL in an attribute on the body tag, and then returning false from window.onpopstate if the loaded URL is the same as the popped location.
thank you for covering the basics, most tutorials forget about the existence of noobness.
too bad there is no asciicast for this, those of us with limited english mastery feel like they should complete phonetics tutorials first.
When I use back and forward buttons I get a lot of requests in the log. In between 50 and 150 to be exact. The returns are 200 codes for the url in question.
I'm trying to do this in a Sinatra app (made some adjustments) but something's not working.
The first time I click the link in question the whole page is loaded (ie not just the partial) on subsequent clicks (in the new nested page) it works as expected.
You describe how to create new Article and then add Comments.
What if i need to create new Article with Comment at the same time? For this app it's weird but in my case i don't know how to show form for new Article with fields for Comment.
Could you help me with this?
Someone else has issues with Back button displaying the js/html returned with the ajax request when using the back button instead of the full pages? How to fix?
Small note: from the looks of the jQuery API docs and source, it doesn't appear that jQuery.support.localStorage actually works -- they note that the feature detection is mostly aimed at a small set of features needed for jQuery core and plugins.
Modernizr is a nice straightforward solution for simple yet pretty comprehensive feature detection that I'm finding very handy for graceful HTML5 degradation:
I have built a tool that does that too. It's called http://unused-css.com This service will explore any site and find unused selectors/rules. You will then be able to download the clean CSS file.
Is there a way to setup a default subdomain for all helpers?
Let's say I have www.domain.com and foo.domain.com . I would like to all helper (links, forms, urls, ...) render by default with the www subdomain, despite the request was targeted to either host.
I tried set default_url_options to :subdomain => false (performing all needed steps) but couldn't make it.
def self.total_on(date)
where("date(purchased_at) = ?", date).sum(:total_price)
end
part (like the syntax of the where 'date(purchased_at) = ?', date especially). I get what it's doing and it sounds awesome from such a short line but I tried it in the rails console and it doesn't seem to work for me...
For getting rid of this 'certificate verify failed (OpenSSL::SSL::SSLError)' when using jquery-rails gem please you need to install a certificate and point to it in your open_uri.rb file. Please read more about it at http://bit.ly/fHXtHn
A little off topic, you mentioned that you transfered it to your iphone for the supermarket. How do you transfer the files there and viewing them offline? Is this possible to an ipad?
Well i started as a RoR developer now active PHP developer (but with rails 3 i definitely consider returning) i use hidden fields to accomplish this task. Since you are submitting the values going back and forward the model object is being populated with the previous values with one difference you always have to have all of the fields hidden (showing only the once that are getting populated by the step). Sorry for this late response but i hope it helps.
Anyway thanks a lot for screencasting about rails, if it wasn't for railscasts i dont think that rails would get this popularity which definitely deserves.
Great episode, as usual, just a bit sad it's the last of the offline series : I would have been glad to see you covering web sql database.
Maybe for a next episode !
Rupert, check my blog post i mentioned in an earlier comment for a way to fix the problem with the missing state for the initial page load: http://www.railstoolkit.com/posts/html-5-ajax-history-and-adress-bar-integration
@Nick: Thanks but that doesn't work for me on Chrome 10, unfortunately.
Under what circumstances will e.state be undefined? As far as I can see, on an initial page load, e.state is defined with a value of null.
If I replace //actions here with console.log('hello'), I get hello on the console under the following circumstances:
* Initial page load
* Use of back/forward buttons, after some AJAX calls
* On any other action in the controller, such as show.
Also, so far as I can see, the event from onpopstate is identical to event.originalEvent when using bind.
According to this source, http://www.splefty.com/js/popstate.html, the popstate event is supposed to fire on every page load for various reasons. This means in order to get rid of the unnecessary requests, we need to hook onto the popstate event such that our code only runs when the popstate fires AND the user has clicked the back/forward buttons.
So far it looks like the events are identical (and therefore indiscernible) unless you pass an object to the first argument of pushState. This object gets put onto the state property of the event object but only when the back/forward button is pressed and not on page load.
Unfortunately, an object only gets associated with a page when pushState is called and when you first visit a page, the state will be null. Thus if a user clicks on two links which make calls to pushState (with an object passed to state), and then clicks back twice, the first popstate event will have a state object but the second will still be null. This is because you will be back on the original page and the original page was accessed through a normal page load, not push state.
This means we still can't categorically distinguish between a page that has been reached after pushState and one that has been reached normally.
In Google Chrome 10, I'm getting a problem where an extra get request is made for every single action on my controller for a JS template. This doesn't result in any visible errors but the extra request can be seen in, for example, Firebug or Mongrel. The server returns a 500 error on every action apart from the index action. Only the JS request is made when an AJAX call is made.
The problem is that the code inside the function binded to "popstate" is being executed on page load. This can be seen more clearly if you replace the getScript with console.log('hello').
Is anybody else experiencing this behaviour? I'll post again if I find a fix.
Hi,
I've been using Prawnto / Prawn and followed your railcast (great job btw, been subscribed since day one almost).
I seem to have hit a hurdle which I believe is to do with the recent rails releases (I am using 2.3.8 with ruby 1.8.7.
when adding :format => 'pdf' (:pdf) the url generated is :
localhost:3000/controller/action/id?format=pdf
and not
localhost:3000/controller/action/id.pdf
browsing directly to the id.pdf url works fine but with the link_to command the file that I download is named - unknown - which is not the desired result. it doesn't have a pdf extension even so a rename is needed before i can view it offline.
Hi All, I cannot get my custom /lib/tasks/test.rake :greet task to run like in the railscast. From the app's root "/var/www/id360/", when I issue "rake greet" I get:
(in /var/www/id360)
rake aborted!
Don't know how to build task 'greet'
It seems that rake is not looking in /lib/tasks/ for my "test.rake".
I'm unsure how the set document title works. Because you're doing an ajax call and it's the same ajax response which sets the title, however you're getting the title in the next line for the pushState function... how do you ensure that your ajax call returns BEFORE you ask for the title? Does $.getScript isn't async?
What a cliff hanger! I can't wait to see part II of this series.
I think offline apps might be really interesting when paired with single site browsers. Right now users don't expect web pages to do anything when they're offline. But if you have an app that is actually just a single site browser then the user expects functionality even when offline. It would also make packaging and distribution easy- an optimized experience for mobile Safari with a webkit SSB and an optimized experience for Windows desktops with an internet explorer SSB.
Chrome renders index.js.erb and it is ok, while firefox renders index.js.erb and after it renders index.html.erb too and it is not ok.
I've tried jquery.ba-bbq.js and it works well with Chrome and with Firefox.
Metin, I totally agree with your definition of an "offline application", but remember that's a "part 1".
Moreover, it's good to be able to use the application to build up your grocery list while connected (at home, for example) then bring it to the supermarket with you while not connected.
I'm sorry, but I'm missing the point of accessing a single page offline when you cannot actually use the app (ie, add records, see the changes etc). The real deal would be using the app with all bells and whistles via offline database and sync it when connection comes back.
Like Roberto Ruiz, I'm wondering if the issue raised with the manifest file not being invalidated after the database is updated, could not be handled by a sort of cache.
When the database stage changes, we want the application to issue a new manifest file, with a new ID within.
Hello, I 'm creating FaceBook like application and everything is centered around Person model. Status, Posts, Comments, Emails, Education, Employments, etc, are referenced to a Person model. Now I'm have an Account model created via devise to handle authentication. I'm not so sure how should I link between Person and Account! Should I replace Person with Account model and make reference to all other models? But, then difficulty I'm having is that all the actions will be generated by a Person, not an Account. I really like to separate Peron and Account. Account is suppose to really meant for authentication purposes only. One thing I'm thinking is to create a record in Person model, whenever an Account is registered. But then how do I override devise controller to insert a record in Person model? Any advise is greatly appreciated how to handle this situation.
It seems that with iceweasel there are problems using history.pushState.
My javascript code is:
$(function () {
$('.pagination a').live('click', function () {
$.getScript(this.href);
history.pushState(null, "", this.href);
return false;
});
$(window).bind("popstate", function() {
$.getScript(location.href);
});
});
with chrome it calls, for example, index.js.erb and it is right, while with iceweasel it calls index.js.erb and then also index.html.erb.
Problem solved, the method on the User model create_with_omniauth was not working with the create! block, I had to use the one that passes the parameters
On rails 3, "category_ids=" setter method is already implement for has_many :through relationships, because I can do on the rails console
p.category_ids=[1,3] to update product categories, but when I check a checkbox doesn't update on DB. How could I solve it?
Can i use this, for clear cache?
@rupert: I ended up just faking it by always tracking the currently loaded URL in an attribute on the body tag, and then returning false from window.onpopstate if the loaded URL is the same as the popped location.
Full file here: https://gist.github.com/782275
thank you for covering the basics, most tutorials forget about the existence of noobness.
too bad there is no asciicast for this, those of us with limited english mastery feel like they should complete phonetics tutorials first.
Ryan, why didn't you use ||= in current_user and current_user_session?
When I use back and forward buttons I get a lot of requests in the log. In between 50 and 150 to be exact. The returns are 200 codes for the url in question.
Any suggestion how to solve this?
Thanks in advance!
I'm trying to do this in a Sinatra app (made some adjustments) but something's not working.
The first time I click the link in question the whole page is loaded (ie not just the partial) on subsequent clicks (in the new nested page) it works as expected.
Any clues on how to solve this?
Thanks in advance!
You describe how to create new Article and then add Comments.
What if i need to create new Article with Comment at the same time? For this app it's weird but in my case i don't know how to show form for new Article with fields for Comment.
Could you help me with this?
Someone else has issues with Back button displaying the js/html returned with the ajax request when using the back button instead of the full pages? How to fix?
Great stuff Ryan! Nice to have so many options with Authentication
Small note: from the looks of the jQuery API docs and source, it doesn't appear that jQuery.support.localStorage actually works -- they note that the feature detection is mostly aimed at a small set of features needed for jQuery core and plugins.
Modernizr is a nice straightforward solution for simple yet pretty comprehensive feature detection that I'm finding very handy for graceful HTML5 degradation:
http://www.modernizr.com/
I have built a tool that does that too. It's called http://unused-css.com This service will explore any site and find unused selectors/rules. You will then be able to download the clean CSS file.
Is there a way to setup a default subdomain for all helpers?
Let's say I have www.domain.com and foo.domain.com . I would like to all helper (links, forms, urls, ...) render by default with the www subdomain, despite the request was targeted to either host.
I tried set default_url_options to :subdomain => false (performing all needed steps) but couldn't make it.
Hi Ryan, I was wondering if you could explain the
def self.total_on(date)
where("date(purchased_at) = ?", date).sum(:total_price)
end
part (like the syntax of the where 'date(purchased_at) = ?', date especially). I get what it's doing and it sounds awesome from such a short line but I tried it in the rails console and it doesn't seem to work for me...
I especially loved the quicky mini-explanations on the JavaScript---Wow Wow! Wow!!
What an adventure!
Thank You Thank You Thank You
Thanks Ryan, very timely as ever.
You might want to take a look at js-model to help manage your model and persistence. You can find it here: https://github.com/benpickles/js-model
Erm, Antony, Prototype used to be the default rails js library. Many used it and I guess still many do... ;-)
Really excellent. Related to what Bruno and Brian asked, any chance of a part III focused on authenticated apps?
Great screencast - as always!
Thank you very much, you really save me much time with almost every episode...
btw: I don't know ANY (no, really, not one single person!) person who is using prototype with Rails...
For getting rid of this 'certificate verify failed (OpenSSL::SSL::SSLError)' when using jquery-rails gem please you need to install a certificate and point to it in your open_uri.rb file. Please read more about it at http://bit.ly/fHXtHn
Very nice screencasts!
A little off topic, you mentioned that you transfered it to your iphone for the supermarket. How do you transfer the files there and viewing them offline? Is this possible to an ipad?
Well i started as a RoR developer now active PHP developer (but with rails 3 i definitely consider returning) i use hidden fields to accomplish this task. Since you are submitting the values going back and forward the model object is being populated with the previous values with one difference you always have to have all of the fields hidden (showing only the once that are getting populated by the step). Sorry for this late response but i hope it helps.
Anyway thanks a lot for screencasting about rails, if it wasn't for railscasts i dont think that rails would get this popularity which definitely deserves.
Excellent episode. Like Bruno asked, do you have any advice for dealing with user authentication for off-line apps.
I really like what I see here. I hope it comes to pass
Great episode, as usual, just a bit sad it's the last of the offline series : I would have been glad to see you covering web sql database.
Maybe for a next episode !
Very good, how is the safe way to do it in authenticated app?
Thanks.
Rupert, check my blog post i mentioned in an earlier comment for a way to fix the problem with the missing state for the initial page load: http://www.railstoolkit.com/posts/html-5-ajax-history-and-adress-bar-integration
@Nick: Thanks but that doesn't work for me on Chrome 10, unfortunately.
Under what circumstances will e.state be undefined? As far as I can see, on an initial page load, e.state is defined with a value of null.
If I replace //actions here with console.log('hello'), I get hello on the console under the following circumstances:
* Initial page load
* Use of back/forward buttons, after some AJAX calls
* On any other action in the controller, such as show.
Also, so far as I can see, the event from onpopstate is identical to event.originalEvent when using bind.
@rupert: I saw the same issue and replaced the popstate handling code as follows to resolve it:
window.onpopstate = function(e) {
if (!(typeof e.state == 'undefined')) {
// actions here
}
}
I had to use window.onpopstate instead of jQuery's bind method because otherwise the event object wasn't being passed properly.
According to this source, http://www.splefty.com/js/popstate.html, the popstate event is supposed to fire on every page load for various reasons. This means in order to get rid of the unnecessary requests, we need to hook onto the popstate event such that our code only runs when the popstate fires AND the user has clicked the back/forward buttons.
So far it looks like the events are identical (and therefore indiscernible) unless you pass an object to the first argument of pushState. This object gets put onto the state property of the event object but only when the back/forward button is pressed and not on page load.
Unfortunately, an object only gets associated with a page when pushState is called and when you first visit a page, the state will be null. Thus if a user clicks on two links which make calls to pushState (with an object passed to state), and then clicks back twice, the first popstate event will have a state object but the second will still be null. This is because you will be back on the original page and the original page was accessed through a normal page load, not push state.
This means we still can't categorically distinguish between a page that has been reached after pushState and one that has been reached normally.
In Google Chrome 10, I'm getting a problem where an extra get request is made for every single action on my controller for a JS template. This doesn't result in any visible errors but the extra request can be seen in, for example, Firebug or Mongrel. The server returns a 500 error on every action apart from the index action. Only the JS request is made when an AJAX call is made.
The problem is that the code inside the function binded to "popstate" is being executed on page load. This can be seen more clearly if you replace the getScript with console.log('hello').
Is anybody else experiencing this behaviour? I'll post again if I find a fix.
@ewall - thanks for the code on making this work with BBQ (post 34). It works great.
@Ryan - Big fan of your Railscasts. Thanks a ton!
OK for those who switched to rails 3 and still have problemwith cropper.rb file: here is a fix a friend gave me.
https://gist.github.com/770789
Hi,
I've been using Prawnto / Prawn and followed your railcast (great job btw, been subscribed since day one almost).
I seem to have hit a hurdle which I believe is to do with the recent rails releases (I am using 2.3.8 with ruby 1.8.7.
when adding :format => 'pdf' (:pdf) the url generated is :
localhost:3000/controller/action/id?format=pdf
and not
localhost:3000/controller/action/id.pdf
browsing directly to the id.pdf url works fine but with the link_to command the file that I download is named - unknown - which is not the desired result. it doesn't have a pdf extension even so a rename is needed before i can view it offline.
any thoughts?
thanks,
Liad
Hi All, I cannot get my custom /lib/tasks/test.rake :greet task to run like in the railscast. From the app's root "/var/www/id360/", when I issue "rake greet" I get:
(in /var/www/id360)
rake aborted!
Don't know how to build task 'greet'
It seems that rake is not looking in /lib/tasks/ for my "test.rake".
How can I correct this?
Thanks,
Bill
I was very excited to see this almost working but I get the following error after the callback:
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
The application trace is empty, and the last call in the full trace is:
oa-oauth (0.1.6) lib/omniauth/strategies/oauth.rb:24:in `callback_phase'
Any ideas?
I'm unsure how the set document title works. Because you're doing an ajax call and it's the same ajax response which sets the title, however you're getting the title in the next line for the pushState function... how do you ensure that your ajax call returns BEFORE you ask for the title? Does $.getScript isn't async?
Great screencast!
history.js does not work.
One suggest is to add user and password as env variable
$ export GMAIL_USER=email@email.com
$ export GMAIL_PASSWORD=password
$ export MY_DOMAIN=pablocantero.com
ActionMailer::Base.smtp_settings = {
:address => 'smtp.gmail.com',
:port => 587,
:domain => ENV['MY_DOMAIN'],
:user_name => ENV['GMAIL_USER'],
:password => ENV['GMAIL_PASSWORD'],
:authentication => 'plain',
:enable_starttls_auto => true
}
If you are using heroku
$ heroku config:add GMAIL_USER=email@email.com
$ heroku config:add GMAIL_PASSWORD=password
$ heroku config:add MY_DOMAIN=pablocantero.com
What a cliff hanger! I can't wait to see part II of this series.
I think offline apps might be really interesting when paired with single site browsers. Right now users don't expect web pages to do anything when they're offline. But if you have an app that is actually just a single site browser then the user expects functionality even when offline. It would also make packaging and distribution easy- an optimized experience for mobile Safari with a webkit SSB and an optimized experience for Windows desktops with an internet explorer SSB.
Thanks for the great video.
It seems to me that with Firefox history.pushState doesn't work very well.
With Chrome it's ok but with Firefox it does not.
Here is the code:
$(function () {
$('.pagination a').live('click', function () {
$.getScript(this.href);
history.pushState(null, "", this.href);
return false;
});
$(window).bind("popstate", function() {
$.getScript(location.href);
});
});
Chrome renders index.js.erb and it is ok, while firefox renders index.js.erb and after it renders index.html.erb too and it is not ok.
I've tried jquery.ba-bbq.js and it works well with Chrome and with Firefox.
I've found that jquery.ba-bbq.js works well than history.pushState
Metin, I totally agree with your definition of an "offline application", but remember that's a "part 1".
Moreover, it's good to be able to use the application to build up your grocery list while connected (at home, for example) then bring it to the supermarket with you while not connected.
what would be the advantage of using with_options? does pose any additional security threats? or is it safe to use?
I'm sorry, but I'm missing the point of accessing a single page offline when you cannot actually use the app (ie, add records, see the changes etc). The real deal would be using the app with all bells and whistles via offline database and sync it when connection comes back.
Like Roberto Ruiz, I'm wondering if the issue raised with the manifest file not being invalidated after the database is updated, could not be handled by a sort of cache.
When the database stage changes, we want the application to issue a new manifest file, with a new ID within.
Is that possible or the problem is even bigger ?
Hello, I 'm creating FaceBook like application and everything is centered around Person model. Status, Posts, Comments, Emails, Education, Employments, etc, are referenced to a Person model. Now I'm have an Account model created via devise to handle authentication. I'm not so sure how should I link between Person and Account! Should I replace Person with Account model and make reference to all other models? But, then difficulty I'm having is that all the actions will be generated by a Person, not an Account. I really like to separate Peron and Account. Account is suppose to really meant for authentication purposes only. One thing I'm thinking is to create a record in Person model, whenever an Account is registered. But then how do I override devise controller to insert a record in Person model? Any advise is greatly appreciated how to handle this situation.
Best REgards,
AM.
Hi Ryan,
This is a great episode. I'm looking forward to watching next part.
Could you do an episode for calling mysql stored procedure by ruby on rails?
I'm googling it. not get any good answers. :(
Thanks
It seems that with iceweasel there are problems using history.pushState.
My javascript code is:
$(function () {
$('.pagination a').live('click', function () {
$.getScript(this.href);
history.pushState(null, "", this.href);
return false;
});
$(window).bind("popstate", function() {
$.getScript(location.href);
});
});
with chrome it calls, for example, index.js.erb and it is right, while with iceweasel it calls index.js.erb and then also index.html.erb.
Problem solved, the method on the User model create_with_omniauth was not working with the create! block, I had to use the one that passes the parameters
On rails 3, "category_ids=" setter method is already implement for has_many :through relationships, because I can do on the rails console
p.category_ids=[1,3] to update product categories, but when I check a checkbox doesn't update on DB. How could I solve it?
thanks!!