Saturday 29 June 2013

Rails: changing templates by action

One of the main principles that I like follow when I am developing a Web application is: in your web app, the front end never must looks like your back end. That is, the admin zone and the public area must have their own CSS classes and layouts. The regular user, the admin and even the anonymous visitant to the site must feel clear the difference between one and another areas.

Typically we use Devise gem to set the actions that we wanna keep restricted and in most of the cases that areas share the same action names: new, edit, create, destroy, update, show. So in many cases those are the actions that we wanna present with the "admin" layout.

So in our app/controllers/application_controller.rb file we add the method:

# @@ - Class variable with actions 
@@actions = %w(new edit create destroy update index start show)

 # Layout for action method
 def layout_by_action
     action = params[:action]
     #raise action_name
     if @@actions.include?(action)
         self.class.layout "admin"
     else
         self.class.layout "application"
     end
end

And in the controller where we want to show different layouts we add:

 # load proper layout
  before_filter :layout_by_action

et voilà!, we have our admin restricted area. Of course you must have an admin layout like this in the views layout directory.

Wednesday 26 June 2013

First Crud in Rails+Angular JS



Get the whole code here: https://github.com/aarkerio/rails-angularjs

This a howto about how create your first R+AJS. AngularJS (AJS) is an Ecmascript framework that gives a further and deeper step into the DOM-Javascript merge. AJS is a bidirectional framework, this means that DOM elements and the Javascript code are under an uninterrupted communication and using the $scope object as the application "glue". This "magic link" will saves you many problems and will improve your productivity. This more completes and advanced features are the reason of why AJS is chosen over another options as Backbone.  

 
As you can all starts with the classic:

$ rails new myangular

Now you must download angular.min.js from the angular site currently 1.0.6 and set in the dir APP/assets/javascripts.

Now we create the dirs wee need:

$ mkdir app/assets/javascript/angular
$ mkdir app/assets/javascript/angular/controllers

The Task controller:


// file: app/assets/javasript/angular/controller/tasks_controller.js

'use strict';

Site.controller('crudController', ['$scope', 'Task', 'Tasks', 'EditTask', function($scope, Task, Tasks, EditTask) {

 $scope.orderProp = 'description';
 $scope.tasks = Tasks.get();  // initial fill

 $scope.$watch('tasks', function() {
   console.log('scope.tasks has changed');
  }, true);
 // add task
 $scope.addTask = function(task) {
    Task.save({task:task});
    console.log('addTask: getTasks function'+JSON.stringify($scope.tasks));
    $scope.task.description = '';   // clean form
    $scope.tasks = Tasks.get();  // refill list
  };

  // edit 
  $scope.EditCtrl = function(task) {
        $scope.task = EditTask.get();
        console.log('EditCtrl function'+JSON.stringify(task));
        //console.log('addTask: getTasks function'+JSON.stringify($scope.task));
        //$scope.task = {'task':task};
  };
  
  // remove
  $scope.removeTask = function(task) {
    console.log(task);
    var deleteUser = confirm('Are you absolutely sure you want to delete?'); 
    if (deleteUser) 
    {
        $scope.id = task.id;
        Task.delete({id:task.id});      // services task.js
        // $scope.tasks = Tasks.get();  // refill list
        $scope.tasks = Tasks.get();    // refill list
    }
  };
}]);

Site.controller('TaskDetailCtrl', ['$scope', '$location', '$routeParams', 'Task', 'Tasks', 'EditTask', function($scope, $location, $routeParams, Task, Tasks, EditTask) {
   console.log('TaskDetailCtrl: '+JSON.stringify($routeParams));
   $scope.task = EditTask.get($routeParams);
   // console.log('scope.task:' + JSON.stringify( $scope.task ));
   // update
    $scope.updateTask = function(task) {
      console.log('update function task:' + JSON.stringify(task));
      //console.log($routeParams);
      Task.update({id:task.id}, task);
      $location.path('/tasks');
   };

  // remove
  $scope.removeTask = function(task) {
     console.log(task);
     var deleteUser = confirm('Are you absolutely sure you want to delete?'); 
     if (deleteUser) 
     {
         $scope.id = task.id;
         Task.delete({id:task});  // services task.js
         $location.path('/tasks');
     }
  };

}]);

function TaskShowCtrl($scope, $location, $routeParams, $dialog, Movie) {"use strict";
    $scope.task = Task.show({
        task_id : $routeParams.task_id
    });
} 

The app file:

// file: app/assets/javascripts/angular/app.js
'use strict';

// Declare app level module which depends on filters, and services
var services    = angular.module("crudServices", []); 
var controllers = angular.module("crudController", []);
var Site        = angular.module('Site', ['ngResource', 'crudServices', 'crudController']);

Site.config(function ($routeProvider, $locationProvider) {
  $routeProvider
                .when('', {redirectTo: '/' })
                .when('/tasks', {templateUrl: '/assets/angular/partials/tasks/index.html', controller: 'crudController'})
                .when('/tasks/:id', {templateUrl:'assets/angular/partials/tasks/show.html', controller:TaskShowCtrl})
                .when('/tasks/:id/edit', {templateUrl:'detail.html', controller:'TaskDetailCtrl'})
                .otherwise({redirectTo:"/"});
  // configure html5 to get links working on jsfiddle
  $locationProvider.html5Mode(true);
});

// Rails Token
Site.config(['$httpProvider', function(provider) {
  provider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');  // this needs jquery
  provider.defaults.headers.post["Content-Type"]   = "application/json; charset=UTF-8";
}]);

The directive file:

// file: app/assets/javascripts/angular/directives/tasks_directives.js
Site.directive('editInPlace', function() {
  return {
    restrict: 'A',
    scope: { value:"=editInPlace" },
    template: '',
    link: function ( $scope, element, attrs ) {
      // Let's get a reference to the input element, as we'll want to reference it.
      var inputElement = angular.element( element.children()[1] );
      
      // This directive should have a set class so we can style it.
      // element.addClass( '.edit-in-place' );
      
      // Initially, we're not editing.
      $scope.editing = false;
      
      // ng-click handler to activate edit-in-place
      $scope.edit = function () {
            $scope.editing = true;
        
            // We control display through a class on the directive itself. See the CSS.
            element.addClass( 'active' );
        
            // And we must focus the element. 
            // `angular.element()` provides a chainable array, like jQuery so to access a native DOM function, 
            // we have to reference the first element in the array.
            inputElement[0].focus();
      };
      
      // When we leave the input, we're done editing.
      inputElement.prop('onblur', function() {
          $scope.editing = false;
          element.removeClass( 'active' );
      });
    }
  };
});

Create macro and create directory inside Emacs 23

These are little notes for my hard head:

1) Create an save a macro:

C-x (   Starts "recording" the macro
C-x )   Ends macro

C-x C-k n  assign a name for the last macro recorded.
M-x  macro-name  execute macro

2) Create a linux directory inside emacs:

M-x make-direct [RET]