rails and backbone working together
Before anything else I'd suggest taking a look at thoughtbot's Backbone.js on Rails book, which is a great starting point, although aimed at an intermediate to advanced audience. I bought this book having already worked with rails but as a total backbone.js beginner and it has served me very well.
Beyond that, there are some fundamental issues with combining these frameworks which go beyond the details covered in this book and other books. Below are some things I'd suggest you think about, from my own experiences pairing RoR and backbone.js. This is a long answer and strays a bit from the specifics of your question, but I hope it might help you out in the "big picture" sense of understanding the problem you're facing.
Rails: Web Framework vs API
The first thing you confront when using backbone.js on top of a rails application is what to do about views, but this is really just the surface of a much deeper issue. The problem goes to the very heart of what it means to create a RESTful web service.
Rails is set up out of the box to encourage its users to create RESTful services, by structuring routing in terms of a set of resources accessed at uniform URIs (defined in your routes.rb
file) through standard HTTP actions. So if you have a Post
model, you can:
- Get all posts by sending
GET
request to/posts
- Create a new post by sending a
GET
request to/posts/new
, filling out the form and sending it (aPOST
request) to/posts
- Update a post with id
123
by sending aGET
request to/posts/123/edit
, filling out the form and sending it (aPUT
request) toposts/123
- Destroy a post with id
123
by sending aDELETE
request to/posts/123
Post
simply by sending a POST
request with a valid form data to the correct URI, say /posts
. Of course there are caveats: I may need to be logged in (have a session cookie identifying me), but in essence Rails doesn't really care what I was doing before I sent that request. I could follow it up by updating another post, or by sending a valid action to whatever other resources are made available to me.This aspect of how Rails is designed makes it relatively easy to turn a (Javascript-light) Rails web application into an API: the resources will be similar or the same, the web framework returning HTML pages while the API (typically) returns data in JSON or XML format.
Backbone.js: A new stateful layer
Backbone is also based on RESTful resources. Whenever you create, update or destroy a backbone.js model, you do so via the standard HTTP actions sent to URIs which assume a RESTful architecture of the kind described above. This makes it ideal for integrating with RESTful services like RoR.
But there is a subtle point to be stressed here: backbone.js integrates seamlessly with Rails as an API. That is to say, if you strip away the HTML views and just use Rails for serving RESTful resources, integrating with the database, performing session management, etc., then it integrates very nicely with the structure that backbone.js provides for client-side code. Many people argue that there's nothing wrong with using rails this way, and I think in many ways they are right.
The complications arise though from the issue of what to do with that other part of Rails that we've just thrown away: the views and what they represent.
Stateful humans, stateless machines
This is actually more important than it may initially seem. HTML views represent the stateless interface that humans use for accessing the RESTful resources your service provides. Doing away with them leaves you with two access points:
- For humans: a rich, client-side interface provided by the backbone.js layer (stateful)
- For machines: a resource-oriented RESTful API provided by the rails layer (stateless)
- HTML resources for humans (stateless)
- JSON/XML resources (API) for machines (stateless)
Working together
This might all seem very abstract and beside the point, I know. To try to make it more concrete, consider the following problem, which gets back to your question about getting rails and backbone.js to work together. In this problem, you want to:
- Create a web service with a rich client-side experience using backbone.js, with rails as the back end serving resources in JSON format.
- Use
pushState
to give each page in the app a URL (e.g./posts/123
) which can be accessed directly (by entering it into the browser bar). - For each of these URLs, also serve an HTML page for clients without javascript.
- Stateful client-side interface (backbone.js templates and views)
- Stateless HTML resources (Rails HTML views)
As another possible reference for doing that, I'd suggest having a look at O'Reilly's RESTful Web Services. It might seem odd to be recommending a book on REST in a question about Rails and Backbone.js, but actually I think this is the key piece that fits these very different frameworks together, and understanding it more fully will help you take advantage of the strengths of both.
Backbone JS and Rails: Trial Run
I don't have much experience with Rails, but that would definitely be possible. The best way to play around with Backbone for a small piece would be to add a new Backbone view to your app. Leave an element in your app that you can tack on a Backbone View with and play around with.
How to separate backbone.js APP and rails APP
Having your rails app provide a RESTful API is a good idea here. Your standalone front-end app can then interact with the API over HTTP(S).
If you want the front-end app within the rails app but need repository separation (i.e. don't want the front-end developer to access the code of the rails app), using a git submodule may work but probably needs some organisational thought.
This is what I'd do:
First clone your rails app from GitHub or Bitbucket (or git init one locally) and then configure a git submodule.
git clone git@github.com:pathto/myawesomerailsapp.git
cd myawesomerailsapp
git submodule add git@github.com:pathto/mystandalonejsapp.git app/assets/standalone
Now when you
cat .gitmodules
you'll notice there's a new submodule configured in your repo.
Good luck!
How to make third-party request in backbone
You needn't to override the fetch method; you just need to define your url and you also need to override the parse method which is automatically called after fetching to parse the returned response and assign it to the collection http://backbonejs.org/#Collection-parse
class App.Collections.Datas extends Backbone.Collection
model: App.Models.Data
initialize: ->
@url = 'Enter URL here' # The fetch method will use this url by default
parse:(response, options)->
super(response.data, options)
PSthe word data is the plural of datum
Rails : Backbone + Turbolinks Hybrid Routing
I disagree about not mixing Turbolinks and Backbone. They fulfil different roles and there are occasions where you might like to use both.
In fact, I understand Basecamp uses both.
You can use this (now infamous) post as a starting point: http://www.goddamnyouryan.com/blog/rails-4-turbolinks-and-backbone
However I still had the problem you're having. Here's how I solved it (provisionally, I haven't tested this in the wild):
window.MyApp =
Models: {}
Collections: {}
Views: {}
Routers: {}
initialize: ->
# Build it up
@router = new MyApp.Routers.Pages(data)
Backbone.history.start()
close: ->
# Tear it down
@router.close(false)
@router = undefined
# Turbolinks exposes the before-change event when a page change is initiated
$(document).on 'page:before-change', ->
# If the app has been initialized
if MyApp.router?
# Stop the history and clean up the app
Backbone.history.stop()
MyApp.close()
# We're leaving the domain of the backbone app, check for Turbolinks
if Turbolinks?.supported
# Push the last known state of the app to the Turbolinks history
window.history.replaceState {turbolinks: true, url: window.location.href}, window.title, window.location.href
$(document).on 'page:change', ->
# If it's the page with our backbone app
if $('#pages').size() > 0
MyApp.initialize()
Clarifications about Rails and Node.js
There aren't enough differences between Node.js and Rails for it to practically matter.
A lot of what Node.js can do can be pulled off in Rails with things like EventMachine and Pusher. So unless you are really familiar with Rails' limitations, and know you'll be pushing the boundaries, you'd be hard pressed to make something a seasoned Rails developer couldn't do.
Having built apps in Node and Express I can say that they alone aren't enough to make a sexy application. They can seem just as old and stale if you don't have an outstanding frontend UI to facilitate the backend possibilities. Instead of comparing backend servers, I think the real future of doing amazing things is in front-end JavaScript frameworks like Backbone.js that use Express/Rails/Node.js on the backend.
I have chosen to go in the direction of Backbone.js with Rails as my backend API server. Because it's so easy to rapidly create a very nice RESTful backend server in Rails. Rails also makes working with CoffeeScript and precompiling/organizing Backbone code a breeze. There are already decent Backbone.js gems out there for Rails.
The Rails core is also able to acknowledge and embrace the fact that frontend JS MVCs are logically a good next step, and they have been working to strengthen the bond between the two. For those same reasons they have also worked to make Rails an even better API server so that it can work with frontend JS easier. Node.js and Express aren't putting as much effort to coordinate with frontend JavaScript MVCs as the Rails community is.
Being good with a JavaScript frontend MVC and Rails as a backend makes you also great for both worlds in terms of getting a job. You will easily be able to hop onto a Node.js project and add value to that team with your superior frontend experience, and you can also roll with the punches on a Ruby on Rails team and add value to them as well.
good example applications combining rails 3 and sproutcore
The method you describe is the same way that you integrate backbone.js into a rails app, and it seems to work very well
https://github.com/codebrew/backbone-rails
This stores backbone in
app/assets/javascripts/backbone/
app/assets/javascripts/backbone/app/models
app/assets/javascripts/backbone/app/controllers
And then there is a script tag in the view that just initializes backbone
<script type="text/javascript">
$(function() {
// Blog is the app name
window.router = new Blog.Routers.PostsRouter({posts: <%= @posts.to_json.html_safe -%>});
Backbone.history.start();
});
</script>
I imagine a similar process for sproutcore would make sense
Related Topics
Sha1 Hashes Not Matching Between My Rails and Cocoa Apps
Where Are Keywords Defined in Ruby
Sinatra Not Persisting Session with Redirect on Chrome
Ruby Fails on Osx Lion with Rbenv
Setting Up Configuration Settings When Writing a Gem
Rvm Can No Longer Install 1.8.7-P352 on MAC Os X Mountain Lion
Why Does Including This Module Not Override a Dynamically-Generated Method
Use Pry in Gems Without Modifying The Gemfile or Using 'Require'
How to Host Gem in Github and Use It
Do Ruby Objects Have a Size Limit
In Ruby, Is Truthiness Idiomatic for a Method Name Ending with a Question Mark
Ruby Interpreter (Cui) 1.9.2P180 [I386-Mingw32] Has Stopped Working (I Am Not Using MySQL )
Giving an Example of a Cycle in a Directed Graph