Rails error while installing nokogiri with native extensions

Installing nokogiri 1.10.3 with native extensions
Errno::EACCES: Permission denied @ rb_sysopen – /Users/raghu/workspace/fleet/vendor/bundle/ruby/2.5.0/gems/nokogiri-1.10.3/LICENSE-DEPENDENCIES.md
An error occurred while installing nokogiri (1.10.3), and Bundler cannot continue.
Make sure that `gem install nokogiri -v ‘1.10.3’ –source ‘https://rubygems.org/’` succeeds before bundling.

 

the solution that worked for me is to run it with sudo bundle

Java pass by value explained

Hey friends!

pass by value means the actual value is passed on by creating a copy of it.

Pass by reference means that a number (called a memory address) is passed on, this address defines where the actual value is stored.

I will try to explain why java is pass by value by taking an example class Person.

public class Person {
  private String name;
  private int age;
  
  Person(String name, int age){
    this.name = name;
    this.age = age;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  
}

Through out this post I will modify the person object in different scenarios and explain whats happening behind the scenes.

Initial state: Let’s create a person object with name `Tom Cruise` and age 57.

public class ReferenceTest {public static void main(String[] args) {
    Person person = new Person("Tom Cruise", 57);
    System.out.println(person.getName());
  }}

Output: Tom Cruise

Scenario 1:

Now let’s try to change the name of the person object.

  Person person = new Person("Tom Cruise", 57);
  modifyPerson(person);
  System.out.println(person.getName());
  
  private static void modifyPerson(Person person) {
    person.setName("Brad Pitt");
  }

Output: Brad Pitt

No surprises.

Scenario 2:

Let’s now modify the state of person. I will try to nullify the object.

//excluded other code for brevity
private static void modifyPerson(Person person2) {
 person2 = null;
}

Output: Tom Cruise

What ? Yes that’s because java passes a copy of reference (don’t get confused this with physical memory address reference)pointing to the actual object. So what ever you do to change the state of the reference the actual object remains untouched. The image will explain more. person is untouched as person2 is a reference copy of person.

Let’s do one last thing to confirm this.

//excluded other code for brevity
private static void modifyPerson(Person person2) {
 person2 = new Person("Johnny Depp", 56);
}

Output: Tom Cruise

Still no change. Look at the image to see whats happening.

person2 is pointing to a whole new object in the memory.

Same thing applies to primitive data types.

Last but not the least you can pass the reference of a variable by adding an & in front of the method argument, but we can just return the value instead of doing this.

Hope this helps!

Execute shell commands in Node script

If you are wondering how to execute commands in shell during a node script this is for you. We can achieve this using node’s child_process library.

declare a const for child_process

const exec = require('child_process').exec

Let’s say i want to execute a bunch of git commands like below

git config --global github.user testbot
git config --global user.name "test"
git config --global user.email bot@test.com

I also want to do something else when all of them succeeds. I wrapped a promise around exec and used Promise.all to achieve this.

Promise.all([
    execPromise('git config --global github.user testbot'),
    execPromise('git config --global user.name "test"),
    execPromise('git config --global user.email bot@test.com)
  ])
.then(console.log('git configured successfully'))
.catch(error => validationFailed(error))
function execPromise(cmd){
  console.log(cmd)
  return new Promise(function (resolve, reject) {
    exec(cmd, (error, stdout, stderr) => {
      if (error) {
        reject({cmd: cmd, message: error})
      } else{
        resolve(stdout)
      }
    })
  })
}
function validationFailed (error = {cmd: '', message: ''}) {
  console.warn(`Error occurred while ${error.cmd}:     ${error.message}`)
}

Happy coding!

Rails: Reinstall all gems again

I was messing with a gem in my ruby on rails and ended up deleting all the installed gems manually on my local machine while experimenting.

I tried to reinstall using `bundle` which did not help as it thinks that gems are already installed looking at the `Gemfile.lock` file.

After googling a little bit I found bundle install --force to forcefully reinstall all of them.

Happy coding!

Adding devise_token_auth to an existing Rails app

Ruby on rails is still the frontrunner for getting an MVP(minimum viable product) out of the door. Eventual when things go our way we want to introduce F/E framework on the customer facing side for various reasons(continue using the admin part as traditional rails app). If you are already using devise and wondering how to use it for api calls this post is for you.

The devise_token_auth is a gem on top of devise which works with tokens and leverages on devise. We can even configure it to either refresh the token for every single request or not.

Without further delay let’s jump in.

Add the following to your Gemfile. We need rack-cors to get support for Cross-Origin Resource Sharing (CORS).

gem ‘devise_token_auth’

gem rack-cors

Then install the gem using bundle:

bundle install

Assuming we are using User model run the following command to prepare for the setup.

rails g devise_token_auth:install User auth

This is going to generate the following files

create  config/initializers/devise_token_auth.rb
insert  app/controllers/application_controller.rb
gsub  config/routes.rb
create  db/migrate/20190712184641_devise_token_auth_create_users.rb
insert  app/models/user.rb

So far we have done exactly as mentioned in the docs which thinks there is no devise functionality already implemented.

As we want new token implementation to work on top of our existing admin side(traditional rails app using devise) do the following changes.

  • routes.rb: Move mount_devise_token_auth_for User, at: auth into api routes. As we only want this routes to be used in the context of api.
 mount_devise_token_auth_for 'User', at: 'auth'
namespace :api do
  namespace :v1 do
    mount_devise_token_auth_for 'User', at: 'auth', defaults: {format: 'json'}
  • application_controller.rb:                                                                                                Remove include DeviseTokenAuth::Concerns::SetUserByToken from application_controller.rb and move to your api base controller. Else this will mess up with your devise authentication.
class Api::V1::BaseController
  include DeviseTokenAuth::Concerns::SetUserByToken
  • Rename migration to xxx_devise_token_auth_update_users and make sure you remove all the existing devise module columns which you already have.
class DeviseTokenAuthCreateUsers < ActiveRecord::Migration[5.2]   def change     change_table(:users) do |t|       ## Required       t.string :provider, :null => false, :default => "email"
      t.string :uid, :null => false, :default => ""
      t.boolean  :allow_password_change, default: true

      ## Tokens
      t.json :tokens
    end
  end
end
  • User.rb: Remove the modules you already included. I don’t need omniauth at this moment so i removed it.
# Include default devise modules.
devise :database_authenticatable, :registerable,
        :recoverable, :rememberable, :trackable, :validatable,
        :confirmable
include DeviseTokenAuth::Concerns::User

Apart from the above i made the following changes to make it work.

class Api::V1::BaseController < JSONAPI::ResourceController
  include DeviseTokenAuth::Concerns::SetUserByToken

before_action :configure_permitted_parameters, if: :devise_controller?

def configure_permitted_parameters
  devise_parameter_sanitizer.permit(:sign_in, keys: [:email, :password])
end

In application_controller.rb

class ApplicationController < ActionController::Base

protect_from_forgery with: :exception
protect_from_forgery with: :null_session, if: -> { request.format.json? }

 wrap_parameters false

In development.rb

resource '*', headers: :any,
         expose: %w(etag access-token uid expiry token-type client Rate-Search-Uuid),
         methods: [:get, :post, :delete, :put, :patch, :options, :head]

Now you can send a curl command like below.

curl -X POST http://localhost:3000/api/v1/auth/sign_in
-H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'cache-control: no-cache' \
  -H 'content-type: multipart/form-data;
  -F email=email@company.com \
  -F password=password

The success response consists of access_token, uid, client from headers. Don’t forget to send these back and forth on the consecutive requests.

Drop comments if you are stuck or have any concerns.

Thanks!