Friday, November 16, 2018

Simple Dockerfile and docker-compose.yml for Rails 5 App

Dockerfile contents:

FROM ruby:2.5

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

RUN mkdir /budget
WORKDIR /budget

COPY Gemfile /budget/Gemfile
COPY Gemfile.lock /budget/Gemfile.lock

RUN bundle install
COPY . /budget

LABEL maintainer="Bala Paranj "

CMD puma -C config/puma.rb

docker-compose.yml:

version: '3'
services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    ports:
      - "3000:3000"

Run:

docker-compose build
docker-compose up

Simple CircleCI Configuration File to Build Rails 5 Project

I am working on the landing page for Budget Scape. Here is the simplest circleci/config.yml:

version: 2
jobs:
  build:
    working_directory: ~/budget
    docker:
      - image: circleci/ruby:2.4.1
        environment:
          RAILS_ENV: test
    steps:
      - checkout

      - run: sudo apt-get update && sudo apt-get install nodejs
      # Bundle install dependencies
      - run: bundle install

      # Run the tests
      - run: bundle exec rake

I am using sqlite as the production database.  The home page is a lead capture page with bullet list of benefits for a user.

Wednesday, November 07, 2018

Developing REPL from Scratch in Ruby

Read Evaluate Print Loop

One liner:

loop { p eval gets }

Readable:

loop do
  print "$ "
input = gets.chomp!
result = eval(input)
puts "=> #{result}"
end


Persist Local Variable

myirb_binding = binding()
loop do
  print "$ "
input = gets.chomp!
result = myirb_binding.eval(input)
puts "=> #{result}"
end

Code written by Chris Wanstrath:

loop do
  print ENV['REPL_PROMPT'] || "#{ARGV[0]}>> "

  begin
    line = $stdin.gets.chomp
  rescue NoMethodError, Interrupt
    exit
  end

  if from_stdin
    run = "echo \"%s\" | #{command}" % [ line, nil ]
  else
    run = "#{command} %s" % [ line, nil ]
  end
  puts "$ #{run}" if debug
  system run
  warn "Use Ctrl-D (i.e. EOF) to exit" if line =~ /^(exit|quit)$/
end

Difference Between IRB and Code in Ruby File

One of the differences is that any method defined at the top level in a Ruby file becomes a private method in Object.

def greet
  p 'hi'
end

o = Object.new
o.greet

NoMethodError: private method ‘greet’ called for #. You can verify this:

 def greet
   p 'hi'
 end 

 p self.private_methods.grep(/greet/)

But in IRB it becomes a public method.

$ irb
2.3.3 :001 > def greet
2.3.3 :002?>   p 'hi'
2.3.3 :003?>   end
 => :greet
2.3.3 :004 > o = Object.new
 => # 
2.3.3 :005 > o.greet
"hi"
 => "hi"
 
Hey, you can also use self to call the greet method like this:

self.greet

or just

greet


Tuesday, November 06, 2018

Moving a Repo Quickly from Gitlab to Github

If you don't mind losing the git log in the new repo, you can move a repo easily in three steps:

1. Remove the existing git files in the repo:

rm -rf .git

2. Add a new origin:

git remote add new-origin git@github.com:your-user:your-project.git

3. Push it to the new repo

git push --all new-origin

Managing SSH Keys in Ubuntu

sudo apt-get install keychain

In ~/.bashrc, add:

keychain id_rsa
. ~/.keychain/`uname -n`-sh

In the terminal:

source ~/.bashrc

 * keychain 2.8.2 ~ http://www.funtoo.org
 * Found existing ssh-agent: 5059
 * Known ssh key: /home/bparanj/.ssh/id_rsa

I had already generated the ssh key using ssh-keygen and ran:

$ eval "$(ssh-agent -s)"
Agent pid 5059 
 



Monday, November 05, 2018

kazam on Ubuntu to record screencast

It works. But the size of the files that it creates is huge. It quickly filled up 100 GB hard disk space allocated to the Ubuntu 18.04 running on VirtualBox quickly. Started getting out of disk space errors that I was scratching my head to fix for a few days.

How to find available disk space in Ubuntu

df -h --total

This command will show you:

Filesystem      Size  Used Avail Use% Mounted on
total           105G  8.6G   92G   9% - 
 
 

Sunday, October 28, 2018

Ruby Closure - Outline for Presentation #2


Part 1

Required Concepts 

1. yield
2. yield with value
3. block variable
4. Migration file uses both Object Oriented and Functional style of programming.

In a Rails project the migration file looks like this:

class CreateArticles < ActiveRecord::Migration

  def change
    create_table :articles do |t|
       t.string :title
       t.text :body

       t.timestamps
     end
  end
end


The final solution for the presentation:

module ActiveRecord
  class Migration
    def change
      puts 'change'
    end

    def create_table(name)
      table = Table.new(name)
      yield table
    end
  end
end

class Table

  def initialize(name)
    @name = name
  end

  def string(column_name)
    puts "String column #{column_name} for table #{@name}"
  end
end

class CreateArticles < ActiveRecord::Migration
  def change
    create_table :articles do |t|
      t.string :title
    end
  end
end

c = CreateArticles.new.change

Implementing the text and timestamps are left as exercises for the attendees.

Part 2

Required Concepts

1. Changing the value of self.
2. Executing code in a different context
3. Using instance_eval to change self
4. yield vs instance_eval
5. Taking block in the argument of a method
6. Using ampersand in the argument of a method
7. The significance of &block in the method argument

Routes file looks like this:

Rails.application.routes.draw do
  get '/home' => 'welcome#home'
end


Step 1


module Rails
  def draw
    puts 'routes file' 
  end
end

class Tester
  include Rails
end

t = Tester.new.draw

Step 2

module Rails
  def draw
    yield
  end
end

class Tester
  include Rails
end

Tester.new.draw do
  get '/home' => 'welcome#home'
end

Step 3

module Rails
  class Router
    def get(hash)
      p 'Method to handle HTTP get request'
    end  
  end

  def draw
    router = Router.new
    yield router
  end
end

class Tester
  include Rails
end

Tester.new.draw do |router|
  router.get '/home' => 'welcome#home'
end

The older versions of Rails used the block variable. How to get rid of it and simplify the DSL?

Step 4

Let's move the draw method into the Router class.

module Rails
  class Router
    def get(hash)
      p 'Method to handle HTTP get request'
    end  
    
    def draw
      yield self
    end
  end
end

Rails::Router.new.draw do |router|
  router.get '/home' => 'welcome#home'
end

Step 5

module Rails
  class Router
    def get(hash)
      p 'Method to handle HTTP get request'
    end  
    
    def draw(&block)
      instance_eval(&block)
    end
  end
end

Rails::Router.new.draw do 
  get '/home' => 'welcome#home'
end

We have replaced the yield with instance_eval that evaluates the given block in the context of the Router instance. It switches the value of self from main Object to Router object.

Part 3


Symbol to proc trick, for instance : ['ruby', 'powerful'].each(&:upcase) uses the ampersand colon trick. 

Concepts Required

1. Coercion
2. Using send for sending messages to an object. Why use send?
3. Significance of ampersand as the prefix to an object in a method argument
4. How does to_proc get invoked?
5. *args in the method argument.
6. Providing default value for block variable.
7. Using send to message an object.

1.

result = ['hi', 'how'].map {|x| x.upcase }
p result

2.

#  {|x| x.upcase }
result = ['hi', 'how'].map(&:upcase)
p result

3.

Goal :

map {|x| x.upcase } ----> map(&:upcase)

4.

The & in the argument converts the object to a Proc object by calling to_proc on it. The object is symbol. We need to implement to_proc in Symbol object.

class Symbol
  def to_proc
    Proc.new {|o| o.upcase }
  end
end

result = ['hi', 'how'].map(&:upcase)
p result

5. 
  def to_proc
    Proc.new {|o| o.send(self) }
  end

6. Multiple block variables

output = [1,2,3,4].inject(0) {|result, element| result + element }
p output

===

output = [1,2,3,4].inject(&:+)
p output

will break the existing to_proc implementation.

Fix:

  def to_proc
    Proc.new {|o, args| o.send(self, args) }
  end

But this will break:

result = ['hi', 'how'].map(&:upcase)
p result

Fix:

  def to_proc
    Proc.new {|o, args=nil| o.send(self, *args) }
  end

Now both one block variable and two block variables can be used.

Closure

A closure is in it's simplest form a block of code that can be passed around as a value, and that can reference variables in the scope it was created in even after exiting from that scope.

The variables are allocated on the heap. Because normally local variables live in the stack and are gone after a method returns.

References

How to Implement Closures
Closure in Crystal