Browsing archives for 'ruby'

Use Paperclip in rack app like sinatra and how to write test

Tips,ruby 8 十一月 2011 | View Comments

You’ll find rack parse uploaded file in different format to Rails

Here is the format in rack app.

"image"=>{:type=>"image/jpeg", :filename=>"listing[image]", :tempfile=>#<File:/tmp/RackMultipart20111107-16008-erra0z-0>, :head=>"Content-Disposition: form-data; name=\"listing[image]\"; filename=\"listing[image]\"\r\nContent-Type: image/jpeg\r\n", :name=>"listing[image]"}

You can simply covert it to right format.

post "some_api" do
yourModel = YourModel.new(:image => to_paperclip(params['image']))
yourModel.save
end
def to_paperclip(image)
  paperclip = {}
  paperclip['tempfile'] = image[:tempfile]
  paperclip['filename'] = image[:filename]
  paperclip['content_type'] = image[:type]
  paperclip['size'] = image[:tempfile].size
  paperclip
end

How to test

 filename = Rails.root.join "spec/fixtures/rails.png"
 file = Rack::Test::UploadedFile.new(filename, "image/png")
 post "/api/v1/some_api", {:image => file}
 # should have image uploaded


Tagged in , , ,

Fix gcc issue after install xcode 4.2

iOS Dev,ruby 28 十月 2011 | View Comments

I can not install gem after installed xcode 4.2. It throws bus error.

the problem is xcode replace gcc with llvm-gcc

How to fix it:
1. Install standalone gcc
http://cloud.github.com/downloads/kennethreitz/osx-gcc-installer/GCC-10.7-v2.pkg

2. add gcc environment in your bash

export CC=/usr/bin/gcc-4.2


Tagged in ,

Tips – Use Named Routes outside of Controller and View

RubyOnRails,Tips,ruby 27 四月 2011 | View Comments

class UrlWriterSingleton
  include Singleton
  include ActionController::UrlWriter

  def self.default_url_options
    {:host => 'www.seravia.com'}
  end
end

module NameRouteHelper
  def self.included(base)
    base.send(:include, InstanceMethods)
  end

  module InstanceMethods
    def name_path_for(name, options = {})
      UrlWriterSingleton.instance.send("#{name}_path", options)
    end
  end
end

Usage:

Class TestNamedRouteHelper
  include NameRouteHelper
end

TestNamedRouteHelper.new.name_path_for("root")


Tagged in , ,

Rspec Tips – Implicit Subject “its”

ruby 7 四月 2011 | View Comments

Rspec has a convenient way to write simple test

describe [1, 2, 3, 3] do
  its(:size) { should == 4 }
  its("uniq.size") { should == 3 }
end

Tagged in , ,

Testing Tips – Prepare Test in Setup

ruby 20 三月 2011 | View Comments

Let’s look at a bad example first:

  context "generate index table" do
    should "success" do
      input_file_path  = "input.json" # have 1,000 lines
      output_file_path = "output.json"
        expact_file_path = "expect.json"
        DataTransform::Mongo::IndexTable.run(input_file_path, output_file_path)
        expect_lines = open(expect_file_path).readlines
        real_lines = open(real_file_path).readlines
      real_lines.each_with_index do |line, index|
      assert_equal JSON.parse(expact_lines[index],line)
      end
    end
  end

What’s the problem in previous test code?

  1. I can not fix this test if it fail
  2. I don’t know what is this test testing

How to refactor this test?

  1. Prepare data in setup
  2. Naming test better
  context "generate index table from standard input" do
    setup do
      @input_file_path  = "input.json"
      @output_file_path = "output.json"
      data = [
        {:a => 1, :b => 1},
        {:a => 2, :b => 3},
        {:a => 3, :b => 4}
      ]

      File.open(@input_file_path) do |f|
        data.each do |line|
          f.puts(JSON.encode(line))
        end
      end
    end
    should "tranform 'a' to 'keyword', 'b' to 'owner'" do
      DataTransform::Mongo::IndexTable.run(@input_file_path, @output_file_path)
      out_file = File.new(@output_file_path)
      expect_data = [
        {:keyword => 1, :owner => 1},
        {:keyword => 2, :owner => 3},
        {:keyword => 3, :owner => 4}
      ]
      assert_equal expect_data.size, outfile.lines.size
   
      real_data = []
      out_file.each_line do |line|
        real_data << JSON.decode(line)
      end
      assert_equal expect_data,real_data
      end
    end
  end

Tagged in , ,

How to write a command-line tool in Ruby

Tips,ruby 2 一月 2011 | View Comments

This is a guest blog posted on rubylearning.com

Introduction

Ruby, as a dynamic language, is always used for quick processing command-line tool for its simplicity and productivity.

This article talks about three ways to write a command-line tool.

Before we start, there are a few things you need to know:

  1. Put line #!/usr/bin/env ruby into the first line of your command-line file which will tell shell execute your file use Ruby
  2. Make sure your file is executable, run chmod u+x FILE_PATH
  3. Print help text if user use it in wrong way

Other people will not sure how to execute your command-line tool.

Conventions

I’ll use three definitions:

  1. Command-line file name
  2. Command
  3. Option

For example there is a command: ‘server start -e development’

  1. Command-line file name is ‘server’
  2. Command is the first argument ‘start’
  3. Option is the reset of argument pair ‘-e development’

Let’s go

We start from a simple example: write a command-line tool to start and stop the server.

Without any lib

case ARGV[0]
when "start"
  STDOUT.puts "called start"
when "start"
  STDOUT.puts "called stop"
else
  STDOUT.puts <<-EOF
Please provide command name

Usage:
  server start
  server stop
EOF
end

ARGV, all arguments will stored as a array in this variable.

What if you need to pass some option?

def parse_options
  options = {}
  case ARGV[1]
  when "-e"
    options[:e] = ARGV[2]
  when "-d"
    options[:d] = ARGV[2]
  end
  options
end

case ARGV[0]
when "start"
  STDOUT.puts "start on #{parse_options.inspect}"
when "stop"
  STDOUT.puts "stop on #{parse_options.inspect}"
else
  STDOUT.puts <<-EOF
Please provide command name

Usage:
  server start
  server stop
 
  options:
    -e ENVIRONMENT. Default: development
    -d daemon, true or false. Default: true
EOF
end

This code is simple but it has some disadvantages:

  • Writing option parser and help text in different places will bring you troubles when they are not matched.
  • Using array index to get options from ARGV, these magic numbers will create maintenance problem.

OptionParser

OptionParser is build-in ruby lib help you parse arguments.

we can refactor our code like this:

require 'optparse'

options = {}

opt_parser = OptionParser.new do |opt|
  opt.banner = "Usage: opt_parser COMMAND [OPTIONS]"
  opt.separator  ""
  opt.separator  "Commands"
  opt.separator  "     start: start server"
  opt.separator  "     stop: stop server"
  opt.separator  ""
  opt.separator  "Options"
 
  opt.on("-e","--environment ENVIRONMENT",
"Which environment you want server run. Default: development") do |environment|
    options[:environment] = environment
  end
 
  opt.on("-d","--daemon","running on daemon mode? Default: true") do
    options[:daemon] = true
  end
 
  opt.on("-h","--help","help") do
    puts opt_parser
  end
end

opt_parser.parse!

case ARGV[0]
when "start"
  puts "call start on options #{options.inspect}"
when "stop"
  puts "call stop on options #{options.inspect}"
else
  puts opt_parser
end

Try to execute this file without argument, you’ll find it prints very nice help text.

opt_parser.parse! is the method extract options from ARGV, extracted value will be deleted from ARGV.

OptionParser is better than that.

You can define options value type, then OptionParser will convert value to the type you defined like this:

opt.on("-e","--environment ENVIRONMENT",Numeric,
       "which environment you want server run") do |environment|
  options[:environment] = environment
       end
opt.on("--delay N", Float, "Delay N seconds before executing") do |n|
  options[:delay] = n
end
opt.on("-j x,y,z","--jurisdictions x,y,z", Array,
       "which jurisdiction will start") do |jurisdictions|
  options[:jurisdictions] = jurisdictions
       end
server_list = %w[a b c]
opt.on("-s SERVERS","--servers SERVERS", server_list,
       "which server will start between #{server_list.join(',')}") do |servers|
  options[:servers] = servers
       end

You can mark whether value of the option is mandatory.

# Mandatory argument.
opts.on("-r", "--require LIBRARY",
        "Require the LIBRARY before executing your script") do |lib|
  options.library << lib
        end

# Optional argument; multi-line description.
opts.on("-i", "--inplace [EXTENSION]",
        "Edit ARGV files in place",
        "  (make backup if EXTENSION supplied)") do |ext|
  options.inplace = true
  options.extension = ext || ''
  options.extension.sub!(/\A\.?(?=.)/, ".")  # Ensure extension begins with dot.
        end

For more details your can see this article and ruby rdoc

Benefit of OptionParser is: we don’t need to use array index to retrieve options and we write help text along with option definition.

Disadvantage of OptionParser is: since different commands need using the same option parser, you cannot define different option parsers for different commands. To solve this problem, you can resort to Thor.

Thor

As you know Thor is a replacement of Rake. Let’s see how we use Thor to refactor our command-line tool.

require 'rubygems'
require 'thor'

class ThorExample < Thor
  desc "start", "start server"
  method_option :environment,:default => "development", :aliases => "-e",
:desc => "which environment you want server run."
  method_option :daemon, :type => :boolean, :default => false, :aliases => "-d",
:desc => "running on daemon mode?"
  def start
    puts "start #{options.inspect}"
  end

  desc "stop" ,"stop server"
  method_option :delay,  :default => 0, :aliases => "-w",
:desc => "wait server finish it's job"
  def stop
    puts "stop"
  end
end

ThorExample.start
  • desc defines command name and long description
  • method_option define option parser for this command
  • ThorExample.start is method to start parse argument

Execute it without argument, the output is:

Tasks:
  thor_example help [TASK]  # Describe available tasks or one specific task
  thor_example start        # start server
  thor_example stop         # stop server

Execute it with argument help start, you’ll get help text for command start:

Usage:
  thor_example start

Options:
  -e, [--environment=ENVIRONMENT]  # which environment you want server run.
                                   # Default: development
  -d, [--daemon]                   # running on daemon mode?

start server

As you can see, it’s very clean and easy to write.

For more detailed usage, you can visit Thor github page and its rdoc

Summary

Of course there are more ways to write command-line tool. Choose what best fit your requirement rather than the most powerful or latest one.

All the sample code is on github https://github.com/allenwei/ruby_command_line_sample

Tagged in , , ,

Tips – Move Eval from Runtime to Parse Time

Tips,ruby 1 十二月 2010 | View Comments

As we eval some code on runtime, but do you know eval is slow?

A best practice is move eval from uuntime to parse time

Here is the benchmark:

                user     system      total        real
runtime     0.180000   0.020000   0.200000 (  0.201734)
parse time  0.270000   0.030000   0.300000 (  0.323348)

Here is the code

require 'benchmark'

Benchmark.bm(10) do |x|
  x.report("runtime") {
    class PersonA
      10_000.times do |i|
        define_method "method_#{i}" do
          eval "#{i}"
        end
      end
    end
  a = PersonA.new
  10_000.times do |i|
    a.send("method_#{i}")
  end
  }
  x.report("parse time") {
    class PersonB
      10_000.times do |i|
        eval "define_method 'method_#{i}' do
        #{i}
    end"

      end
    end

  b = PersonB.new
  10_000.times do |i|
    b.send("method_#{i}")
  end
  }
end

Tagged in , , ,

Ads Plugin created by Jake Ruston's Wordpress Plugins - Powered by and football database.