Chicken’s Blog ^_^

(Just code for food)

[Rails] Setup Rails 4.1 With Spring, RSpec and Guard

| Comments

Rails 4.1 allows us to use Spring to run our rails and rake commands quickly by running your application in the background and avoiding the rails startup time penalty. It took me a little while to find this information, so here is my guide to setting this up.

I’m using the RSpec testing framework with the Guard event watcher as the test runner. Spring allows us to watch the test run almost immediately after hitting save of a file in the editor.

First, let’s start with a fresh app to understand things. Upgrading an existing app is straightforward with this technique as well. Note that I am using Rails 4.1.0.beta1 on Ruby 2.1.0p0.

1
2
3
rails new myapp -T
cd myapp
vim Gemfile

After we open the Gemfile in our editor, we add the following gems

  • rspec-rails - The RSpec testing framework with Rails integrations
  • spring-commands-rspec - Adds rspec command for spring. Includes a dependency for the spring Rails application preloader.
  • guard-rspec - The Guard file watcher for running rspec
  • rb-fsevent - For OS/X only, waits for file changes from the Mac OS/X FSEvents API instead of polling the disk for changes.

Add this block to the Gemfile (You may wish to add gem versions as well.)

1
2
3
4
5
6
group :development, :test do
  gem 'spring-commands-rspec'
  gem 'rspec-rails'
  gem 'guard-rspec'
  gem 'rb-fsevent' if `uname` =~ /Darwin/
end   

Bundle again after saving to install the dependent gems.

1
bundle install

Setup Spring

The spring command runs a command through the spring application runner. By default, it is configured to run rails and rake commands only. We added a plugin gem to allow it to run rspec for us.

Spring has a binstub subcommand that updates the scripts in myapp/bin to load the spring runner. Run this command to configure these scripts (when converting to spring) and for the rspec command.

1
2
3
4
% spring binstub --all
* bin/rake: spring already present
* bin/rspec: generated with spring
* bin/rails: spring already present

You can check if spring has your application running as a background process with the status subcommand. When you run a spring-enabled command, it will start the server.

The startup for the first time will take a moment as rails initializes. Now that spring has started the app server for you, subsequent runs will use a forked process from this server instead and return faster.

1
2
3
4
5
6
7
8
9
% spring status
Spring is not running.
% rails g
...
% spring status
Spring is running:

89168 spring server | myapp | started 5 secs ago
89170 spring app    | myapp | started 5 secs ago | development mode

The spring server will shutdown automatically when your shell session is closed. You can shut it down yourself by:

1
2
% spring stop
Spring stopped.

That’s about all you need to know to get started with spring.


Setting up RSpec and Guard

This process is the same as you normally would do:

1
2
3
4
5
6
7
8
% rails g rspec:install
  create  .rspec
  create  spec
  create  spec/spec_helper.rb
% guard init
11:32:14 - INFO - Writing new Guardfile to /Users/allen/src/myapp/Guardfile
11:32:14 - INFO - rspec guard added to Guardfile, feel free to edit it
% vim Guardfile

This sets up the files for rspec and guard. Now we have to tell guard to use the spring runner when using rspec. Edit your Guardfile and change the command for rspec to spring rspec. Change this line:

1
guard :rspec do

to

1
guard :rspec, cmd:"spring rspec" do

Running

(Running with binstubs, shell extensions, alias, etc. may make executing these commands different on your system. You may need to bundle exec rails or bin/rails, or however your environment is setup.)

Check out the time gains running rspec through spring, I’ll use the time command:

1
2
3
4
5
6
7
% time spring rspec
...
spring rspec  0.12s user 0.07s system 6% cpu 3.126 total

% time spring rspec
...
spring rspec  0.12s user 0.06s system 30% cpu 0.600 total

This is on a minimal app with a single, scaffolded resource. As your app grows larger, the initial startup time will be longer, and your time savings will multiply accordingly. (Of course, you can just run the spring rspec command if you are more interested in the results rather than the timings.)

To start TDD’ing, fire up guard in a terminal session.

1
2
3
4
5
% guard
11:48:02 - INFO - Guard is using TerminalTitle to send notifications.
11:48:02 - INFO - Guard::RSpec is running
11:48:02 - INFO - Guard is now watching at '/Users/allen/src/myapp'
[1] guard(main)>

This runs guard to watch your app files and kicks off the spring rspec command to run your changed spec file or suite.

If you need to change something basic in your app, such as adding a gem, updating a local dependency that could be cached, you need to restart both guard (Ctrl-D at the console) (or a server or console process), and spring (spring stop). Restarting guard will restart spring as well, with your dependencies reloaded.

[Rails] How to Create a Rails App

| Comments

How To Create a Rails App

A step by step guide to prepare your app.

First there was “rails new Foo” – the next thing you know you’re adding “group :test, :development do gem ‘rspec-rails’ end” to your Gemfile and editing your database.yml file to reflect the correct localhost IP. While learning Rails for the first time, it’s difficult to remember these various minutiae of setting up your app; moreover, it’s even more difficult to remember which order they should be performed in. This can often mean running into some time consuming errors along the way. Here is a step by step guide I created, and very often use, to make sure I’m not missing any steps in setting up a working Rails app.

The 17.5 Steps

Step 1
1
rails new --database=postgresql OR rails new -T --database=postgresql

At Launch Academy we tend to use postgresql. So to save yourself a little extra work, make sure you include the –database flag when executing rails new.

The -T flag is short for –skip-test-unit (which is helpful since we usually delete the test directory anyway).

Step 2
1
remove turbolinks in Rails 4.x -- for Rails 3.x, make sure you remove the public/index.html

How to remove Turbolinks:

⋅⋅* Remove the gem 'turbolinks' line from your Gemfile

⋅⋅* Remove the //= require turbolinks from your app/assets/javascripts/application.js.

⋅⋅* Remove the two "data-turbolinks-track" => true hash key/value pairs from your app/views/layouts/application.html.erb.
Step 3
1
git init

This command sets up your local git repository. You’ll need to do this command first in order to commit your changes later.

Step 4

edit config/database.yml

1
host: 127.0.0.1

This file holds some settings for your postgresql database. You will probably want to remove the username on your databases. For my system I also needed to add the line host: 127.0.0.1 to make postgresql work.

Step 5

Gemfile updates These are some common gems I’ve used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
gem ‘twitter-bootstrap-rails’
gem ‘simple-form’

   group :test, :development do
   gem 'rspec-rails'
   gem 'capybara'
   gem 'launchy'
   end

   group :test do
   gem 'factory_girl_rails'
 gem ‘shoulda-matchers’
   end

   group :development do    #access at rails/routes
   gem 'sextant'
   end
Step 6
1
rake db:create

I usually do this before bundle, as I’ve run into some errors otherwise. This command creates a local instance of the postgresql database.

Step 7
1
bundle install
Step 8
1
git add * ; git commit -m ‘message’

A good chance to commit those changes! git add * will add all changed files to your commit. When you do git commit -m ‘message’, those changes are then saved to your local git repository. This is useful if you ever need to go back to a stable version of your app. This command will also be very important to Git collaboration.

Step 9
1
rails g rspec:install (rails g bootstrap:install static)

Install any gem generated scaffolds you may be using. Rspec is a must! Other examples could include bootstrap, or devise.

Step 10
1
remove ~/test

Remove that ~/test dir once you have Rspec setup. A clean app is a happy app. If you used the -T flag while creating the rails app, you can skip this step.

Step 11

add to spec_helper.rb

1
2
3
4
5
#spec_helper.rb
  require 'capybara/rails'
  require 'capybara/rspec'

  config.include FactoryGirl::Syntax::Methods

Add this helpful lines to make sure everything works! I like to include the FactoryGirl line as well. That way I don’t have to type FactoryGirl.build, but rather just: build.

Step 12
1
create factories.rb file in spec/support

You’ll need to create the spec/support directory, where you’ll place a new file that will create your factories.

Step 13
1
git add * ; git commit -m ‘message’

Commit

Step 14
1
create models / scaffold / migrations

Time to create any generated code. If you’re not using generated code, you can mostly skip to step 16

Step 15
1
git add * ; git commit -m ‘message’

Commit before migrating: JUST IN CASE!

Step 16
1
rake db:migrate ; rake db:rollback ; rake db:migrate

Use this command to double check that your tables / migrations are correctly configured. This is a best practices sort of deal. If you’re using zsh, I’d highly recommend making an alias for this command!

Step 16.5
1
rake db:test:prepare

This command generates the test environment that Rspec uses. You’ll need to redo this command if you make any new migrations.

Step 17
1
git add * ; git commit -m ‘message’

Don’t forget to commit after you Migrate!

Extras: add database.yml and secret_token.rb to your .gitginore

This is just a good habit to get into. When others fork your repos, having special database settings can give them a headache when trying to check out your code. Do them a favor and tell git to ignore that file. Another file to add is the secret_token.rb – it’s called that for a reason! It’s supposed to be secret! Protect your users by hiding this file.

In your .gitignore file add the lines:

1
2
/config/database.yml
/config/secret_token.rb
From here you can do whatever you’d like, and you shouldn’t break your app too much. Have fun!

Fixing: PG::UniqueViolation: ERROR: Duplicate Key Value Violates Unique Constraint ‘Your_table_name_pkey’

| Comments

Issue

Postgresql maintain a internal value which is the max value of the id column of a given table. And uses this to generate values for primary keys when you insert new records. Sometimes this internally stored value can get messed up. And that can cause the error above when you are trying to insert a new record to the table.

How to fix ????

You need to basically correct the above said value.

Access to your database and run :

1
pg_dump -U user_name database_name -f database_backup_name.sql

Change your rails app and run :

1
2
cd /your/rails/app/root/
rails db production

Update your_table_id_sql

1
SELECT setval('your_table_id_seq', (SELECT MAX(id) FROM your_table));

replace your_table with whatever the table name you are using. And the code above assumes your primary key column name is id.

Good lucks to you :)

Crytal: Syntax and Semantics Part 2

| Comments

5.Control expressions

5.1.Truthy and falsey values

A truthy value is a value that is considered true for an if, unless, while or until guard. A falsey value is a value that is considered false in those places.

The only falsey values are nil, false and null pointers (pointers whose memory address is zero). Any other value is truthy.

5.2.if

An if evaluates the then branch if its condition is truthy, and evaluates the else branch, if there’s any, otherwise.

1
2
3
4
5
6
7
8
9
10
11
12
13
a = 1
if a > 0
  a = 10
end
a #=> 10

b = 1
if b > 2
  b = 10
else
  b = 20
end
b #=> 20

To write a chain of if-else-if you use elsif:

1
2
3
4
5
6
7
if some_condition
  do_something
elsif some_other_condition
  do_something_else
else
  do_that
end

After an if, a variable’s type depends on the type of the expressions used in both branches.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
a = 1
if some_condition
  a = "hello"
else
  a = true
end
# a :: String | Bool

b = 1
if some_condition
  b = "hello"
end
# b :: Int32 | String

if some_condition
  c = 1
else
  c = "hello"
end
# c :: Int32 | String

if some_condition
  d = 1
end
# d :: Int32 | Nil

Note that if a variable is declared inside one of the branches but not in the other one, at the end of the if it will also contain the Nil type.

Inside an if’s branch the type of a variable is the one it got assigned in that branch, or the one that it had before the branch if it was not reassigned:

1
2
3
4
5
6
7
a = 1
if some_condition
  a = "hello"
  # a :: String
  a.length
end
# a :: String | Int32

That is, a variable’s type is the type of the last expression(s) assigned to it.

If one of the branches never reaches past the end of an if, like in the case of a return, next, break or raise, that type is not considered at the end of the if:

1
2
3
4
5
6
7
8
if some_condition
  e = 1
else
  e = "hello"
  # e :: String
  return
end
# e :: Int32

5.3.unless

An unless evaluates the then branch if its condition is falsey, and evaluates the else branch, if there’s any, otherwise. That is, it behaves in the opposite way of an if:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unless some_condition
  then_expression
else
  else_expression
end

# The above is the same as:
if some_condition
  else_expression
else
  then_expression
end

# Can also be written as a suffix
close_door unless door_closed?

5.4.case

A case is a control expression that allows a sort of pattern matching. It allows writing a chain of if-else-if with a small change in semantic and some more powerful constructs.

In its basic form, it allows matching a value against other values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ase exp
when value1, value2
  do_something
when value3
  do_something_else
else
  do_another_thing
end

# The above is the same as:
tmp = exp
if value1 === tmp || value2 === tmp
  do_something
elsif value3 === tmp
  do_something_else
else
  do_another_thing
end

Note that === is used for comparing an expression against a case’s value.

If a when’s expression is a type, is_a? is used. Additionally, if the case expression is a variable or a variable assignment the type of the variable is restricted:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
case var
when String
  # var :: String
  do_something
when Int32
  # var :: Int32
  do_something_else
else
  # here var is neither a String nor an Int32
  do_another_thing
end

# The above is the same as:
if var.is_a?(String)
  do_something
elsif var.is_a?(Int32)
  do_something_else
else
  do_another_thing
end

You can invoke a method on the case’s expression in a when by using the implicit-object syntax:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
case num
when .even?
  do_something
when .odd?
  do_something_else
end

# The above is the same as:
tmp = num
if tmp.even?
  do_something
elsif tmp.odd?
  do_something_else
end

Finally, you can ommit the case’s value:

1
2
3
4
5
6
7
8
9
10
11
12
13
case
when cond1, cond2
  do_something
when cond3
  do_something_else
end

# The above is the same as:
if cond1 || cond2
  do_something
elsif cond3
  do_something_else
end

This sometimes leads to code that is more natural to read.

5.5.while

A while executes its body as long as its condition is truthy

1
2
3
while some_condition
  do_this
end

In the above form, the condition is first tested and, if truthy, the body is executed. That is, the body might never be executed.

To execute the body at least once and then continue executing it while the condition is truthy, write the while as a prefix:v

1
do_this while some_condition

If you need to execute multiple expressions, group them between begin and end:

1
2
3
4
begin
  do_this
  do_that
end while some_condition

A while’s type is always Nil.

Similar to an if, if a while’s condition is a variable, the variable is guaranteed to not be nil inside the body. If the condition is an var.is_a?(Type) test, var is guaranteed to be of type Type inside the body. And if the condition is a var.responds_to?(:method), var is guaranteed to be of a type that responds to that method.

The type of a variable after a while depends on the type it had before the while and the type it had before leaving the while’s body:

1
2
3
4
5
6
7
8
a = 1
while some_condition
  # a :: Int32 | String
  a = "hello"
  # a :: String
  a.length
end
# a :: Int32 | String

5.6.until

An until executes its body until its condition is truthy. An until is just syntax sugar for a while with the condition negated:

1
2
3
4
5
6
7
8
until some_condition
  do_this
end

# The above is the same as:
while !some_condition
  do_this
end

An until can also be used in its suffix form, causing the body to be executed at least once. break and next can also be used inside an until.

5.7.&&

An && (and) evaluates its left hand side. If its truthy, it evaluates its right hand side and has that value. Otherwise it has the value of the left hand side. Its type is the union of the types of both sides.

You can think an && as syntax sugar of an if:

1
2
3
4
5
6
7
8
9
some_exp1 && some_exp2

# The above is the same as:
tmp = some_exp1
if tmp
  some_exp2
else
  tmp
end

5.8.||

An || (or) evaluates its left hand side. If its falsey, it evaluates its right hand side and has that value. Otherwise it has the value of the left hand side. Its type is the union of the types of both sides.

You can think an || as syntax sugar of an if:

1
2
3
4
5
6
7
8
9
some_exp1 || some_exp2

# The above is the same as:
tmp = some_exp1
if tmp
  tmp
else
  some_exp2
end

I will present about types and methods in part 3. Thanks!

Work With Chef Server

| Comments

In previous post I have wrote how to use Chef Solo to setup and deploy a Server with PHP stuff. Today I’d like to use Chef Server for storing Chef’s configurations (Cookbooks, Roles, Enviroments, Data Bags) and Bootstrap a server use Chef Server.

Setup Chef Server

The easiest way to get started with a Chef server is to use Opscode’s Hosted Chef–it’s free for up to 5 nodes. Once you exceed 5 nodes you can decide whether to pay a monthly fee or install your own Chef server.

  • Register your free trial of hosted Chef

  • Get pem key from profile

  • Generate Knife config

1
https://github.com/code-for-food/php-cooking.git # fork from github
  • Move to php-cooking and copy client key and validation key to .chef folder.

  • Replace the knife.rb in .chef folder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
current_dir = File.dirname(__FILE__)
log_level                :info
log_location             STDOUT
node_name                "codeforfoods"
client_key               "#{current_dir}/codeforfoods.pem" # replace by your key
validation_client_name   "chicken-validator"
validation_key           "#{current_dir}/chicken-validator.pem" # replace by your key
chef_server_url          "https://api.opscode.com/organizations/chicken"
cache_type               'BasicFile'
cache_options( :path => "#{ENV['HOME']}/.chef/checksums" )


cookbook_path    ["cookbooks", "site-cookbooks"]
node_path        "nodes"
role_path        "roles"
environment_path "environments"
data_bag_path    "data_bags"
encrypted_data_bag_secret "#{current_dir}/encrypted_data_bag_secret"
  • Upload cookbooks to Chef server

      knife cookbook upload --all
    

View on Chef server

  • Upload Roles to Chef Server

       knife role from file roles/myapp.json 
       knife role from file roles/myapp_deploy.json
    

View on Server

  • Create new enviroment called staging by create staging.json file in enviroments folder
staging.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "name": "staging",
  "default_attributes": {
    "apache2": {
      "listen_ports": [
        "80",
        "443"
      ]
    },
    "php": {
      "ext_conf_dir": "/etc/php5/apache2/conf.d"
    },
    "myapp": {
      "user": "ubuntu",
      "root_path": "/var/www/codeforfoods",
      "server_name": "phpdev.codeforfoods.net"
    }
  },
  "json_class": "Chef::Environment",
  "description": "",
  "chef_type": "environment"
}
  • Upload Enviroments to Chef Server

      knife environment from file environments/dev.json 
      knife environment from file environments/staging.json 
    

View on Server

  • Create an encrypted data bag

    • Create encrypted data bag key, the key would help to decrypted the data bag

        openssl rand -base64 512 | tr -d 'rn' > ~/.chef/encrypted_data_bag_secret
        chmod 600 ~/.chef/encrypted_data_bag_secret
      
    • Add below line to .chef/knife.rb

        encrypted_data_bag_secret "#{current_dir}/encrypted_data_bag_secret"
      
    • Create the encrypted data bage

         knife data bag create --secret-file ~/.chef/encrypted_data_bag_secret secrets myapp
      

    View on Server

Setup Vagrant use chef-client as provision

Vagrantfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# -*- mode: ruby -*-
# vi: set ft=ruby :

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
CHEF_PATH = "."
SYNC_PATH = "./www"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  config.vm.box = "ubuntu14.04"
  config.vm.box_url = "https://oss-binaries.phusionpassenger.com/vagrant/boxes/latest/ubuntu-14.04-amd64-vbox.box"
  config.vm.network "private_network", ip: "192.168.34.100"
  config.vm.hostname = "devphp.congdang.com"
  config.ssh.forward_agent = true
  config.ssh.forward_x11   = true

  config.vm.provider "virtualbox" do |vb|
    vb.customize(["modifyvm", :id, "--natdnshostresolver1", "off"  ])
    vb.customize(["modifyvm", :id, "--natdnsproxy1",        "off"  ])
    vb.customize(["modifyvm", :id, "--memory",              "1024" ])
  end



  config.omnibus.chef_version = '11.16.0'

  # Your organization name for hosted Chef 
  orgname = "chicken"

  # Set the Chef node ID based on environment variable NODE, if set. Otherwise default to vagrant-$USER
  node = ENV['NODE']
  node ||= "vagrant-codeforfoods"

  config.vm.provision :chef_client do |chef|
    chef.chef_server_url = "https://api.opscode.com/organizations/#{orgname}"
    chef.validation_key_path = "#{CHEF_PATH}/.chef/#{orgname}-validator.pem"
    chef.validation_client_name = "#{orgname}-validator"
    chef.encrypted_data_bag_secret_key_path = "#{CHEF_PATH}/.chef/encrypted_data_bag_secret"
    chef.node_name = "#{node}"
    chef.provisioning_path = "/etc/chef"
    chef.log_level = :debug
    #chef.log_level = :info

    chef.environment = "dev"
    chef.add_role("myapp")

    #chef.json.merge!({ :mysql_password => "foo" }) # You can do this to override any default attributes for this node.
  end


  config.vm.synced_folder("#{SYNC_PATH}", "/vagrant",
                          :owner => "vagrant",
                          :group => "vagrant",
                          :mount_options => ['dmode=777','fmode=777'])
end

Starting Vagrant

    vagrant up

Chef the webapp on browser

Vagrantfile
1
http://192.168.34.100

Bootstrap a Server

Cloudshare provides a easy way to create new node for testing More detail here

  • Get a Linux machine to bootstrap

  • Bootstrap your node

      knife bootstrap uvo1vinqr3ty38kwnpn.vm.cld.sr -x sysadmin -P Oi9YB7HTfP --sudo -N node1 -r 'role[myapp]' -E staging
    

** If you meet the error while creating the node, please see the comment in this https://groups.google.com/forum/#!topic/learnchef-fundamentals-webinar/LYe_i_QIWdU

Error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
54.79.58.34   knife ssl check -c /etc/chef/client.rb
54.79.58.34 ```
54.79.58.34
54.79.58.34 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
54.79.58.34
54.79.58.34 Starting Chef Client, version 11.16.4
54.79.58.34
54.79.58.34 ================================================================================
54.79.58.34 Chef encountered an error attempting to load the node data for "testnode"
54.79.58.34 ================================================================================
54.79.58.34
54.79.58.34 Authentication Error:
54.79.58.34 ---------------------
54.79.58.34 Failed to authenticate to the chef server (http 401).
54.79.58.34
54.79.58.34 Server Response:
54.79.58.34 ----------------
54.79.58.34 Failed to authenticate as 'testnode'. Ensure that your node_name and client key are correct.
54.79.58.34
54.79.58.34 Relevant Config Settings:
54.79.58.34 -------------------------
54.79.58.34 chef_server_url   "https://api.opscode.com/organizations/cd-chef"
54.79.58.34 node_name         "testnode"
54.79.58.34 client_key        "/etc/chef/client.pem"

SSH to server and do this command

    sudo rm -rf /etc/chef/client.pem

Rails Protocol

| Comments

A guide for writing great web apps.

Set Up Laptop

Set up your laptop with :

Create App

Get Suspenders.

1
gem install suspenders

Create the app.

1
suspenders app --heroku true --github organization/app

Set Up App

Get the code

1
git clone git@github.com:organization/app.git

Set up the app’s dependencies

1
2
cd project
./bin/setup

Use Heroku config to get ENV variables. (https://github.com/ddollar/heroku-config)

1
heroku config:pull --remote staging

Delete extra lines in .env, leaving only those needed for app to function properly. For example: BRAINTREE_MERCHANT_ID and S3_SECRET.

Use Foreman to run the app locally (https://github.com/ddollar/foreman)

1
foreman start

It uses your .env file and Procfile to run processes like Heroku’s Cedar stack. (https://devcenter.heroku.com/articles/cedar)

Git protocol

https://github.com/thoughtbot/guides/tree/master/protocol/git

Product Review

https://github.com/thoughtbot/guides/tree/master/protocol/product-review

Code Review

https://github.com/thoughtbot/guides/tree/master/code-review

  • Review data integrity closely, such as migrations that make irreversible changes to the data, and whether there is a related todo to make a database backup during the staging and production deploys.

  • Review SQL queries for potential SQL injection.

  • Review whether dependency upgrades include a reason in the commit message, such as a link to the dependency’s ChangeLog or NEWS file.

  • Review whether new database indexes are necessary if new columns or SQL queries were added.

  • Review whether new scheduler (cron) tasks have been added and whether there is a related todo in the project management system to add it during the staging and production deploys.

Deploy

View a list of new commits. View changed files.

1
2
3
git fetch staging
git log staging/master..master
git diff --stat staging/master

If necessary, add new environment variables.

1
heroku config:add NEW_VARIABLE=value --remote staging

Deploy to Heroku

1
git push staging

If necessary, run migrations and restart the dynos.

1
2
heroku run rake db:migrate --remote staging
heroku restart --remote staging

Introspect to make sure everything’s ok.

1
watch heroku ps --remote staging

Test the feature in browser.

Deploy to production.

1
2
3
4
5
6
7
8
git fetch production
git log production/master..master
git diff --stat production/master
heroku config:add NEW_VARIABLE=value --remote production
git push production
heroku run rake db:migrate --remote production
heroku restart --remote production
watch heroku ps --remote production

Watch logs and metrics dashboards.

Close pull request and comment Merged.

Set Up Production Environment

Scaling Ruby on Rails by Caching Your Database Queries

| Comments

This article would help you to scaling your app with caching your database queries.

Its pretty good to use active-record in ruby on rails, relationships, find_by methods, where queries everything makes your life much more simpler as a developer but sooner or later your application becomes slow. At that time you need to work on optimizations. There are lot of work around for optimizations and one of them in caching the queries. The company I work for is StudyPad and we have a product ie splashmath and that has over 9 millions users using it across multiple platforms. The amount of traffic that we get is pretty huge and caching one of the thing that helped us to solve out scaling problems. Here is quick idea to cache your queries in memcache.

Basic requirements

There are few requirements for this. First is the dalli gem and memcache installed on your machine.

Basic structure of the application.

We have lot of static/seed data which change very less and the queries on those things are pretty high. So first thing to handle that static data.

1
2
3
4
5
6
7
class Grade < ActiveRecord::Base
  has_many :skills
end

class Skills < ActiveRecord::Base
  belongs_to :grade
end

So in order to get skills for a grade, its pretty simple

1
2
grade = Grade.first
skills = grade.skills

Now for that matter every time we are fetching skills for a grade its gonna hit the database and get the results. So its time to get this query and its results cached.

Assuming you have configured dalli gem easily and you have memcached up and running. Here is a simple method to cache the results

1
2
3
4
5
def cached_skills
  Rails.cache.fetch([self.class.name, id, :skills], expires_in: 240.hours) {
    skills.to_a
  }
end

So instead of fetching skills like

1
grade.skills

we are gonna get it like

1
grade.cached_skills

So whats its gonna do. Its gonna fetch the results first time from the database and store it in cache. Next time its gonna return the results from the cache. Note few things here

  • Understand the pattern of the key. [self.class.name, id, :skills] is your key here.

  • Cache will expire in 240.hours. You can customize it as per your needs. Better keep a constant for this somewhere in your application.

  • In cached_skills methods we are keep records not the active-record relations that why we have to convert into array by using to_a else active-record-relation will be cached and database query will be executed.

1
skills.to_a

Expiring the cache.

We are caching the query results but we are not expiring the results. What if some skill has changed. Grade object is not getting any notification for that, so cache is stale, we need to expire it. So we can write a after_commit hook for skill to expire its grade object’s cache

1
2
3
4
5
6
# in skill model
after_commit :flush_cache

def flush_cache
  Rails.cache.delete([self.grade.class.name, self.grade_id, :skills])
end

This is enough to make sure you cache is never stale. There is another way to handle the expiring cache. Lets see that.

Another way

We redefine the models like this

1
2
3
4
5
6
7
class Grade < ActiveRecord::Base
  has_many :skills
end

class Skills < ActiveRecord::Base
  belongs_to :grade, touch: true
end

Note we have added touch: true in skills, and now we redefine our cached_skills method again:

1
2
3
4
5
def cached_skills
  Rails.cache.fetch([self.class.name, updated_at.to_i, :skills], expires_in: 240.hours) {
    skills.to_a
  }
end

Now just caching this we don’t need to expire the cache manually, when ever skills get updated it will touch its parent object object grade, that will update its updated_at value and that specific cache will be never used, as key attribute updated_at has been changed.

The problem with second approach

But there is a problem. Assume you have 10 different has_many relationships for grade and you are caching it all, now everytime a skill has be changed all the other cache keys for grade relationships will be useless too. For example: Grade has_many topics

1
2
3
4
5
6
7
8
9
10
11
class Grade < ActiveRecord::Base
  has_many :skills
end

class Skill < ActiveRecord::Base
  belongs_to :grade, touch: true
end

class Topic < ActiveRecord::Base
  belongs_to :grade, touch: true
end
1
2
3
4
5
6
7
8
9
10
11
12
# In Grade model
def cached_skills
  Rails.cache.fetch([self.class.name, updated_at.to_i, :skills], expires_in: 240.hours) {
    skills.to_a
  }
end

def cached_topics
  Rails.cache.fetch([self.class.name, updated_at.to_i, :topics], expires_in: 240.hours) {
    topics.to_a
  }
end

Now in this case changing any skill will make topics cache useless, but that’s not the case when you are trying to expire it manually. So both approach has pros and cons, first will ask you write more code and second expire cache more frequently. You have to make that choice as per your needs.

What else?

Using the same base principle you can cache lot of queries like

1
2
Grade.cached_find_by_id(1)
Skill.first.cached_grade

This approach helped us to reduce the load on RDS and make things pretty fast. I hope this will help you too. Let me know you feedback or some tips that made you system more faster

Crytal: Syntax and Semantics Part 1

| Comments

1. Comments

1
# This is a comment

2.Local variables

Local variables start with lowercase letters. They are declared when you first assign them a value.

1
2
name = "Crystal"
age = 1

Their type is inferred from their usage, not only from their initializer. In general, they are just value holders associated with the type that the programmer expects them to have according to their location and usage on the program.

For example, reassinging a variable with a different expression makes it have that expression’s type:

1
2
3
4
5
var = "Hello"
# At this point 'var' is a String

var = 1
# At this point 'var' is an Int32

Underscores are allowed at the beginning of a variable name, but these names are reserved for the compiler, so their use is not recommended (and it also makes the code uglier to read).

3.Global variables

Global variables start with a dollar sign ($). They are declared when you first assign them a value.

1
$year = 2014

Their type is the combined type of all expressions that were assigned to them. Additionally, if you program reads a global variable before it was ever assigned a value it will also have the Nil type.

4.Assignment

Assignment is done with the equal (=) character.

1
2
3
4
5
6
7
8
9
10
11
# Assigns to a local variable
local = 1

# Assigns to an instance variable
@instance = 2

# Assigns to a class variable
@@class = 3

# Assigns to a global variable
$global = 4

Some syntax sugar that contains the = character is available:

1
2
3
4
5
6
7
local += 1  # same as: local = local + 1

# The above is valid with these operators:
# +, -, *, /, %, |, &, ^, **, <<, >>

local ||= 1 # same as: local || (local = 1)
local &&= 1 # same as: local && (local = 1)

A method invocation that ends with = has syntax sugar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# A setter
person.name=("John")

# The above can be written as:
person.name = "John"

# An indexed assignment
objects.[]=(2, 3)

# The above can be written as:
objects[2] = 3

# Not assignment-related, but also syntax sugar:
objects.[](2, 3)

# The above can be written as:
objects[2, 3]

The = operator syntax sugar is also available to setters and indexers. Note that || and && use the []? method to check for key prescence.

1
2
3
4
5
6
7
8
9
person.age += 1        # same as: person.age = person.age + 1

person.name ||= "John" # same as: person.name || (person.name = "John")
person.name &&= "John" # same as: person.name && (person.name = "John")

objects[1] += 2        # same as: objects[1] = objects[1] + 2

objects[1] ||= 2       # same as: objects[1]? || (objects[1] = 2)
objects[1] &&= 2       # same as: objects[1]? && (objects[1] = 2)

I will present about control expressions in part 2. Thanks!

Crystal: Getting Started

| Comments

Introduction:

Crystal is a programming language that resembles Ruby but compiles to native code and tries to be much more efficient, at the cost of disallowing certain dynamic aspects of Ruby.

Installation

1. On Debian and Ubuntu

1
2
3
4
curl http://dist.crystal-lang.org/apt/setup.sh | sudo bash
apt-key adv --keyserver keys.gnupg.net --recv-keys 09617FD37CC06B54
echo "deb http://dist.crystal-lang.org/apt crystal main" > /etc/apt/sources.list.d/crystal.list
sudo apt-get install crystal

2. On Mac OSX using Homebrew

1
2
brew tap manastech/crystal
brew install crystal

Hello World

This is the simplest way to write the Hello World program in Crystal:

1
puts "Hello World"

First create a file hello.cr containing your code. Then type in the console:

1
2
chmod +x hello.cr
crystal hello.cr

That’s all! Hope you guys love it :D I will have another post about syntax and semantics :) Thanks

[Rails] Get Youtube Download Link

| Comments

Installation:

1
gem install viddl-rb

Usage:

Download a video: viddl-rb http://www.youtube.com/watch?v=QH2-TGUlwu4
Viddl-rb supports the following command line options:

1
2
3
4
5
6
7
8
9
10
11
-e, --extract-audio              Extract the audio track of the download to a separate file
-a, --abort-on-failure           Abort if one of the videos in a list fails to download
-u, --url-only                   Just display the download url without downloading the file.
-t, --title-only                 Just display the title without downloading the file.
-f, --filter REGEX               Filters a video playlist according to the regex (Youtube only right now)
-s, --save-dir DIRECTORY         Specifies the directory where videos should be saved
-d, --downloader TOOL            Specifies the tool to download with. Supports 'wget', 'curl', 'aria2c' and 'net-http'
-q, --quality QUALITY            Specifies the video format and resolution in the following way: width:height:format (e.g. 1280:720:mp4)
                                 The width, height and format may be omitted with a *.
                                 For example, to match any quality with a height of 720 pixels in any format specify --quality *:720:*
-h, --help                       Displays the help screen

Library Usage:

1
2
3
4
require 'viddl-rb'

download_urls = ViddlRb.get_urls("http://www.youtube.com/watch?v=QH2-TGUlwu4")
download_urls.first     # => "http://o-o.preferred.arn06s04.v3.lscac ..."

The ViddlRb module has the following module public methods:

get_urls_names(url)

– Returns an array of one or more hashes that has the keys :url which points to the download url and :name which points to the name (which is a filename safe version of the video title with a file extension). Returns nil if the url is not recognized by any plugins.

get_urls_exts(url)

– Same as get_urls_names but with just the file extension (for example “.mp4”) instead of the full filename, and the :name key is replaced with :ext. Returns nil if the url is not recognized by any plugins.

get_urls(url)

– Returns an array of download urls for the specified video url. Returns nil if the url is not recognized by any plugins.

get_names(url)

– Returns an array of filenames for the specified video url. Returns nil if the url is not recognized by any plugins.

io=(io_object)

– By default all plugin output to stdout will be suppressed when the library is used. If you are interested in the output of a plugin, you can set an IO object that will receive all plugin output using this method.

Requirements:

  • curl/wget/aria2c or the progress bar gem
  • Nokogiri
  • ffmpeg if you want to extract audio tracks from the videos