Databound provides Javascript a simple API to the Ruby on Rails CRUD.

Used by

API

where

User.where({ name: 'John' }).then(function(users) {
 print('Users called John');
});

find

User.find(15).then(function(user) {
 print('User no. 15: ' + user.name);
});

findBy

User.findBy({ name: 'Peter' }).then(function(user) {
print('Peter from ' + user.city);
});

create

User.create({ name: 'Peter' }).then(function(user) {
 print('I am ' + user.name + ' from database');
});

update

User.update({ id: 15, name: 'Nikki' }).then(function(user) {
  // I'm not Peter anymore, I'm Nikki
  print("I'm not Peter anymore, I'm " + user.name);
});

destroy

User.destroy(15).then(function(resp) {
  if (resp.success) print('User no. 15 is now gone');
});
ALL API DOCS
all, take & takeAll methods, API_URL, seed records and how to use it without jQuery.

Features

Columns

By default no columns are modifiable.

It will raise an error if an unpermitted column is requested.

All table columns are allowed if :table_columns is specified.

:all can be used with DSL to convert non-existing field into another.

ActiveRecord and Mongoid supported.

class UsersController < ApplicationController
  databound do
    model :user
    columns :name, :city
  end
end

# if you don't have a controller
# you can also specify permitted columns in

# config/routes.rb
Rails.application.routes.draw do
  databound :users, columns: [:name, :city]
end

Permit actions

You can check for the record edit rights.

It will raise an error if permit block resolves to false.

CRUD actions are supported:

create

read

update

destroy

class ProjectsController < ApplicationController
  databound do
    model :project
    columns :name, :city

    permit(:update, :destroy) do |params, record|
      record.user_id == current_user.id
    end
  end
end

DSL

You can setup Databound to treat certain Javascript side input values as DSL.

strict setting:

set false to make both dsl values and other values valid.

Javascript
Project = new Databound('/projects', {
  user_id: 'current'
});

Project.create({ title: 'Ship' })
  .then(function(project) {

  alert('Ship of current user: ' + project.user_id);
});
app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
  databound do
    model :project
    columns :title, :user_id

    dsl(:user_id, :current) do |params|
      current_user.id
    end
  end
end

Model

You can specify model both in controller and in routes.rb.

class UsersController < ApplicationController
  databound do
    model :project
  end
end

# if you don't have a controller
# you can also specify permitted columns in

# config/routes.rb
Rails.application.routes.draw do
  databound :users, model: :project
end

# you can also specify it in this style
class UsersController < ApplicationController
  databound(:project) do
  end
end

Friendly errors in development

If consider_all_requests_local is on, Databound will show friendly errors to the Javascript.

Its default is true in development and false in production for Ruby on Rails.

Friendly error types:

Unpermitted columns

Permit violations

ORM errors

config/environments/development.rb
Rails.application.configure do
  config.consider_all_requests_local = true
end
Javascript
// Request includes unpermitted columns: email
User.update({ id: 15 email: [email protected]' })

// Request for destroy not permitted
User.destroy(15)

// Couldn't find record with id: 1
User.find(1)
config/environments/production.rb
Rails.application.configure do
  config.consider_all_requests_local = false
end
Javascript
// Error in the backend with status 500
User.update({ id: 15 email: [email protected]' })

// Error in the backend with status 500
User.destroy(15)

// Couldn't find record with id: 1
User.find(1)

Scope

You can specify default attributes that will be inserted on every query to the backend.

It helps to keep the code dry, because most of the time you know some model attributes when you define a connection.

MiamiUser = new Databound('/users', { city: 'Miami' });

MiamiUser.create({ name: 'Nikki' }).then(function(user) {
  // Miami
  print(user.city);
});

Extra where scopes

You can default connection attribute values if you are constantly repeating them.

They are only used when selecting and not when creating.

var User = new Databound('/users',
  { city: 'Miami' },
  { extra_where_scopes: [{ city: 'New york' }] }
);

User.create({ name: 'Nikki' }).then(function() {
  // ['Miami', 'New york']
  print(User.takeAll());
});

Security

Which parts can Javascript modify?

Specify columns.

By default - no columns are modifiable.

How to secure the relation values?

You can use dsl(:your_column, :expected_value) to only allow certain dsl values and convert them to relation ids in the backend.

How to protect the scope of the records?

If you need a reference to the record being modified, use permit. It will give you a parsed record.

It also works with 3rd party libraries.

class ProjectsController < ApplicationController
  databound do
    model :project
    columns :name, :city

    # CanCanCan gem
    permit(:create) do
      authorize! :create, current_user
    end

    # Pundit
    permit(:update) do
      authorize current_user
    end

    # Plain Ruby
    permit(:destroy) do
      current_user.god?
    end
  end
end

Which parts can Javascript show?

Use Active Model Serializers to serialize the record.

If you don't want to use that, you can overwrite as_json method on the model.

Version support and dependencies

Works with:

Ruby on Rails 3+

Ruby 2.0+

It can work with Angular as a better solution over ngResource.

ActiveRecord or Mongoid

Rails API

Active Model Serializers

Chrome any, Firefox any, Opera any, IE 8+

Depends on:

Lo-Dash (should work with any version)

jQuery 1.5+

jQuery is used for making requests and promises.

You can use your own library instead. Read API docs on how to override those.

Installation

1 Gemfile

gem 'databound', '3.1.3'

2 Run generator to add Databound to application.js

rails g databound:install

3 Add a route to config/routes.rb

Rails.application.routes.draw do
databound :users, columns: [:name, :city]
end

4 - optional Controller is autogenerated from route

But if you already have a controller, you can include Databound and specify the model yourself.

class UsersController < ApplicationController
  databound do
    model :user
    columns :name, :city
  end
end

5 Install dependencies

Easiest way is to use the official Ruby gems or include them from CDNs.

Lo-Dash

lodash-rails gem or CDN

jQuery (already installed by default on Rails)

jquery-rails gem or CDN

6 Use it in the Javascript

var User = new Databound('/users');

1 Gemfile

gem 'databound', '3.1.3'

2 Download the databound.js and load it up

<script src='assets/databound.js'></script>

3 Add a route to config/routes.rb

Rails.application.routes.draw do
databound :users, columns: [:name, :city]
end

4 - optional Controller is autogenerated from route

But if you already have a controller, you can include Databound and specify the model yourself.

class UsersController < ApplicationController
  databound do
    model :user
    columns :name, :city
  end
end

5 Install dependencies

Include them from CDNs.

Lo-Dash
<script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js'>
</script>
jQuery (already installed by default on Rails)
<script src='https://code.jquery.com/jquery-2.1.3.min.js'>
</script>

6 Use it in the Javascript

var User = new Databound('/users');

1 Gemfile

gem 'databound', '3.1.3'

2 Download Javascript part with npm or bower

npm install databound

OR

bower install databound

3 Add a route to config/routes.rb

Rails.application.routes.draw do
databound :users, columns: [:name, :city]
end

4 - optional Controller is autogenerated from route

But if you already have a controller, you can include Databound and specify the model yourself.

class UsersController < ApplicationController
  databound do
    model :user
    columns :name, :city
  end
end

5 Require and use it in the Javascript

var Databound = require('databound');
var User = new Databound('/users');