Rails and Ionic Make Love Flashcards

1
Q

Setup Project

A

$mkdir blog_app_project

$cd blog_app_project

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Setup Basic Rails App

A
$rails new BlogApp
$cd BlogApp
$mv BlogApp web
$bundle install
$rails g scaffold BlogEntries title:string content:text
$rake db:migrate
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Active Model Serializers: use the AMS gem when building RESTful API’s in Rails
blog_entry_seriailizer.rb

A
#/web/app/serializers/blog_entry_seriailizer.rb
class BlogEntrySerializer
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Active Model Serializers
active_model_serializer.rb
Ionic uses Angular,by default receive data w/o root node. Need to disable the root node in the JSON output.

A
#/web/config/initializers/active_model_serializer.rb
ActiveSupport.on_load(:active_model_serializers) do
  # Disable for all serializers (except ArraySerializer)
  ActiveModel::Serializer.root = false
  # Disable for ArraySerializer
  ActiveModel::ArraySerializer.root = false
end
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Test the Rails JSON API

A

$rails s

localhost: 3000/blog_entries/new
localhost: 3000/blog_entries.json

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Setup Rack Cors

Installin Gem #/web/Gemfile

A

gem ‘rack-cors’, :require => ‘rack/cors’

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Setup Rack Cors

Configuring the middleware #/web/config/application.rb

A

config.middleware.insert_before 0, “Rack::Cors” do
allow do
origins ‘
resource ‘
’, :headers => :any, :methods => [:get, :put, :delete, :post, :options]
end
end

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Create The Ionic App ionic start ionic_blog_app tabs

A

mv ionic_app mobile

    • blog_app_project
    • web
    • … rails app
    • mobile
    • … ionic app
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Angular $resource

Ionic and RESTful resources we tend to use the Angular $resource service.

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Angular $resourceAnd inject ngResource into your app module:

A

/ /mobile/www/js/app.js /

angular.module(‘starter’, [‘ionic’, ‘starter.controllers’, ‘starter.services’, ‘ngResource’])

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q
add the BlogEntry factory
Note: for production apps we wouldn't hardcode these URL's, but rather have a module that determines whether to fire requests to the production, staging or the local environment.
A

/ /mobile/www/js/services/js /
.factory(‘BlogEntry’, function($resource) {
return $resource(“http://localhost:3000/blog_entries/:id.json”);
})

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Update Ionic Controller and Route
pull our blog entries JSON from the Rails app and display it in the Ionic app
first ‘Status’ tab. This Status tab is named as the DashCtrl and tab-dash.html t

A

/ /mobile/www/js/controllers.js: /
.controller(‘DashCtrl’, function($scope, BlogEntry) {
BlogEntry.query().$promise.then(function(response){
$scope.blog_entries = response;
}); })

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Injecting the BlogEntry factory into the DashCtrl.
Query the BlogEntry service, returns an Angular $promise as the asynchronous toRails app.
Rails app responds and this promise is resolvedreturned collection to $scope.blog_entries and available to our view.
blog_entries con the scope, can interate over the collection in tab-dash.html.

A

/ /mobile/www/templates/tab-dash.html /

  <div class="list card">
    <div class="item item-divider">{{blog_entry.title}}</div>
        <div class="item item-body">
           <div> {{blog_entry.content}} </div>
        </div>
      </div>
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Install Devise in your Rails App

$rake db:migrate

A

gem ‘devise’
$bundle install
$rails generate devise:install
$rails generate devise User

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Changes to Devise

A
# web/config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Adding our custom Devise controllers to the routes.

A
# web/app/config/routes.rb:
devise_for :users, :controllers => {sessions: 'user/sessions', registrations: 'user/registrations', passwords: 'user/passwords' }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Create the three new controllers to handle JSON in Devise

Sessions Controller

A
# web/app/controllers/user/sessions_controller.rb
class User::SessionsController
18
Q

Create the three new controllers to handle JSON in Devise

Registrations Controller

A
# web/app/controllers/user/registrations_controller.rb
class User::RegistrationsController
19
Q

Create the three new controllers to handle JSON in Devise

Password Controller

A
# web/app/controllers/user/passwords_controller.rb
class User::PasswordsController
20
Q

Authentication across Rails & Ionic.

Disable CSRF protection

A
# /web/app/controllers/application_controller.rb
# protect_from_forgery with: :exception
21
Q

Create a new user

A

$bundle exec rails s -p 3000

locahost:3000/users/sign_up

22
Q

Implementing Devise login in Ionic
Hook up Ionic to login through the Devise JSON controller we just added.
Four things to make all of this work on the Ionic end: -A UserSession factory, to abstract away some of our API implementation

A
  • A login form
  • A login controller
  • A login route
23
Q

Define this UserSession factory in services.js

A
// /mobile/www/js/services.js: 
   .factory('UserSession', function($resource) {
      return    $resource("http://localhost:3000/users/sign_in.json");
})
24
Q

Creating the login form template

  • binding a data object, with the attributes password and email, onto our $scope
  • assign an ng-click directive to our Login button, call the login() in controller.
A

<div>

<span>Email</span>

<span>Password</span>

</div>

 <div class="padding">
   Login
 </div>
25
Q

The login() function composes almost all of this controller.

  • This login() method is called when a user clicks on the Login button. It does the following:
  • Creates a new user_session, with the data from our login form.
  • Attempts to save this user_session, hitting the Devise JSON end-point
  • If successful, we save the userId and userName to localStorage, and redirect the user to the Dash path
A
// /mobile/www/controllers.js
.controller('LoginCtrl', function($scope, $location,    UserSession, $ionicPopup, $rootScope) {
  $scope.data = {};
  $scope.login = function() {
    var user_session = new UserSession({ user: $scope.data });
    user_session.$save(
       function(data){
          window.localStorage['userId'] = data.id;
          window.localStorage['userName'] = data.name;
          $location.path('/tab/dash');
                             },
       function(err){
           var error = err["data"]["error"] || err.data.join('. ')
           var confirmPopup = $ionicPopup.alert({
              title: 'An error occured',
              template: error });
                             }
                                         );
 }  })
26
Q

Wire the login template w/ LoginCtrl (adding a new state in our app.js)

A
// /mobile/www/js/app.js
.state('login', {
  url: '/login',
  templateUrl: 'templates/login.html',
  controller: 'LoginCtrl'
})
27
Q

Change the default route in app.js

A
// /mobile/www/js/app.js
$urlRouterProvider.otherwise('/login');
28
Q

Persist current_user:
Rails forgets who you are in just a single request!
To persist each request needs a session cookie

A
// /mobile/www/js/app.js
   .config(function($stateProvider,  $urlRouterProvider,             $httpProvider) {
    $httpProvider.defaults.withCredentials = true;
// ... routes etc below
29
Q

Increase the time it takes for the Devise login to expire

A
# web/initializers/devise.rb
config.remember_for = 20.years
30
Q

Making the Leap to Production -Check for authenticated users on each state change. and re-direct t if they were not authenticated.

A
  • No registrations or passwords through Ionic and into our Rails app
  • we have temporarily disabled CSRF protection forgery. You’d want to re-enable this with something like this strategy. Note: Cloudspace have released a nice and simple library that you might like to check out to supplement this info: angular_devise
31
Q

Implement Capybara

A

gem ‘capybara’
gem ‘selenium-webdriver’
gem ‘chromedriver-helper’
gem ‘capybara-angular’

32
Q

Set capybara

-Capybara.raise_server_errors = false. (
Once you have it up and running you will want to switch this to true, but setting it to false makes it easier to get started.)

-We ensure that a version of the ionic app is running on PORT=5000 and if thats not the case then we start an instance.

-We have to hack ActiveRecord a little bit to ensure that it will share db connections across threads, otherwise rails records wont be visible in ionic.
Basic setup with rails running on port 4321 and the ionic app running on port 5000

A

Capybara runs on port 4321
web/test/test_helper.rb

Dir[Rails.root.join(“test/helpers/*/.rb”)].each { |f| require f }
require ‘capybara/rails’

Capybara.register_driver :selenium do |app|
Capybara::Selenium::Driver.new(app, :browser => :chrome)
end

Capybara.always_include_port = true
Capybara.server_port = 4321
Capybara.raise_server_errors = false
class ActionController::TestCase
  # Helpful if using Devise for user auth
  # include Devise::TestHelpers
end

class ActionDispatch::IntegrationTest
# Make the Capybara DSL available in all integration tests
include Capybara::DSL
include Capybara::Angular::DSL

def setup
super
end

def teardown
  super
  Capybara.reset_sessions!
  Capybara.use_default_driver
  end
end

Ensure that node server is running ionic app
unless lsof -i :5000.include?(‘node’)
puts ‘Starting ionic node server’
Dir.chdir(“../mobile”) do
system “PORT=5000 nohup node server.js > /dev/null 2>&1 &”
end
end

# Force capybara to share db connections between threads.
class ActiveRecord::Base
mattr_accessor :shared_connection
@@shared_connection = nil

def self.connection
@@shared_connection || retrieve_connection
end
end

ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection

33
Q

Create a helper toto make tests more readable.

A

web/test/helpers/ionic_helper.rb

module IonicHelper
  def on_ionic_app
    Capybara.app_host = 'http://localhost:5000'
       visit('/')
       begin
       yield
       rescue => error
       puts error
       ensure
       Capybara.app_host = 'http://localhost:4321'
    end
  end
end
34
Q

Creating tests

A

web/test/integration/blogentriestest.rb

require “test_helper”

class BlogEntriesTest

35
Q

Running ionic app in background

A

in /mobile/server.js
var express = require(‘express’),
app = express();
app.use(express.static(‘www’));
app.set(‘port’, process.env.PORT || 5000);
app.listen(app.get(‘port’), function () {
console.log(‘Express server listening on port ‘ + app.get(‘port’));
});

36
Q

Installing ExpressJS for tests

A

cd rails_and_ionic_make_love_part_three/mobile

npm install express

37
Q

Ionic app needs to know if its in test mode so that it can talk to our test server instead of the development or production server.

A

mobile/www/js/services.js
.factory(‘api’, function() {
return {
url: function(path) {
return this.base() + path;
},
base: function() {
if ( this.isTestMode() ) {
return “http://localhost:4321/”
} else if ( this.isLocalhost() ) {
return “http://localhost:4444/”
} else {
return “https://production-url.com/”
}
},
isLocalhost: function() {
return ionic.Platform.platform() === “macintel” && !this.isHttps();
},
isTestMode: function() {
return location.port && location.port == “5000”;
},
isHttps: function() {
return window.location.origin.split(‘:’)[2] == “https”;
}
}
})

38
Q

BlogEntries resource

A

.factory(‘BlogEntry’, function($resource, api) {
return $resource(api.url(“blog_entries/:id.json”));
})

39
Q

Poltergeist Gem (to make tests go faster)

A

gem ‘poltergeist’

web/test/test_helper.rb
require ‘capybara/poltergeist’
if ENV[‘VIEW_IN_BROWSER’] == “true”
Capybara.register_driver :selenium do |app|
Capybara::Selenium::Driver.new(app, :browser => :chrome)
end
else
Capybara.javascript_driver = :poltergeist
end

40
Q

Run tests

A

run using:
$ rake test a (for headless poltergeist driver.)
to debug a test: switch over to
$bundle exec rake test VIEW_IN_BROWSER=true