ruby Rational Date and DateTime

If you subtract two Date or DateTime objects, the result is a Rational representing the number of days between them. What you need is:

a = Date.new(2009, 10, 13) - Date.new(2009, 10, 11)
(a * 24 * 60).to_i   # 2880 minutes

or

a = DateTime.new(2009, 10, 13, 12, 0, 0) - DateTime.new(2009, 10, 11, 0, 0, 0)
(a * 24 * 60).to_i   # 3600 minutes

ruby rails invalid byte sequence in UTF-8 when read csv [solved]

使用ruby1.9或者ruby2标准库读取csv文件的时候出现:

CSV.read(csv_file_path)
# => ArgumentError: invalid byte sequence in UTF-8

原因是因为csv文件不是utf-8格式,你只要知道文件格式然后转一下就可以,我这里是中文GB18030编码,所以解决办法如下:

CSV.read(csv_file_path, encoding: 'GB18030:utf-8')

改善Rails3中render json的性能

在上一篇文章Rails3中的JSON知道render :json => @xxx其实是调用的to_json方法,而to_json其实是ActiveSupport::JSON.encode方法。

在数据量比较小的时候,性能还可以,但是当数据量很大的时候,性能非常差,这里我们可以用一些第三方的快的json库来解析:

我使用的是oj,配合MultiJson(Rails3中已经有了,但是只在decode的时候有用到)

# Gemfile
gem 'oj'

# application.rb
MultiJson.engine = :oj

render :json => MultiJson.encode(api_response)

其他一些json库

supported-json-engines

Rails3中的JSON

Rails 3.2.12

to_json

#to_json是由gem ‘json’引入的,但是ActiveSupport重写了,和as_json联系起来

[Object, Array, FalseClass, Float,
 Hash, Integer, NilClass, String, TrueClass].each do |klass|
  klass.class_eval do
    def to_json(options = nil)
      ActiveSupport::JSON.encode(self, options)
    end
  end
end

ActiveSupport::JSON#encode

def encode(value, use_options = true)
  check_for_circular_references(value) do
    jsonified = use_options ? value.as_json(options_for(value)) : value.as_json
    jsonified.encode_json(self)
  end
end

#as_json

这个直接看api就知道了,http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html

render :json => @xxx

来看看render :json是如何实现的:

actionpack/lib/action_controller/metal/renderers

add :json do |json, options|
  json = json.to_json(options) unless json.kind_of?(String)
  json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
  self.content_type ||= Mime::JSON
  json
end

可以看到就是调用了to_json,如果是String就直接返回,如果有callback就返回jsonp的格式

另外我们可以添加新的`renderer`:

ActionController::Renderers.add :csv do |obj, options|
  filename = options[:filename] || 'data'
  str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
  send_data str, :type => Mime::CSV,
   :disposition => "attachment; filename=#{filename}.csv"
end

# 使用

render :csv => @csvable, :filename => @csvable.name

总结:尽量不要在rails3中重写to_json,如果确实需要自己构造json,重写as_json方法。

Add methods dynamically in ruby

A = Class.new

class << A
  def say(sth)
    puts sth
  end
end

A
.instance_eval do
  def do(action)
    puts "Do #{action}"
  end
end

A.say('I am ok') # => I am ok
A.do('go') # => Do go
A.new.do('go')
# => NoMethodError: undefined method `do' for #<A:0x007f9a232b3698>

Don’t be confused by the name instance_eval, it’s not execute block under A’s instance context.

instance_eval

Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj).

==========================================================

Let’s take a look at class_eval

A.class_eval do
  def make(sth)
    puts "Make #{sth}"
  end
end

A.make('cookie')
# => NoMethodError: undefined method `make' for A:Class

A.new.make('cookie') # => Make cookie

It’s confused. right? class_eval named `class` but not define `class method`.

class_eval

Evaluates the string or block in the context of mod, except that when a block is given, constant/class variable lookup is not affected.

==========================================================

Let’s take a look at  metaclass

a = Object.new

def a.dance
  puts 'I am dancing'
end

a.dance # => I am dancing

We know that the method dance isn’t defined in the class Object, because if i instance a new obj, it can not `dance`

b = Object.new

b.dance
# => NoMethodError: undefined method `dance'

So where is it? The answer is metaclass. Yehuda Katz mentioned this concept in his post(metaprogramming-in-ruby-its-all-about-the-self). Each object in Ruby also has its own metaclass – a Class that can have methods, but is only attached to the object itself. metaclass is invisible in Ruby.

What’s going on here is that we’re adding the dance method to a’s metaclass, and the a object inherits from its metaclass and then Object.

Let’s get access to the metaclass.

metaclass = class << a; self; end # => #<Class:#<Object:0x007f9a23bf0620>>
metaclass.instance_methods.grep(/dance/) #=> [:dance]

End.

[ruby] make own DSL

Let’s take a look at below example:

class A

  def learn(&block)
    instance_eval(&block)
  end

  def a
    p 'aaaa'
  end

  def b
    p 'bbbb'
  end

  def c(str)
    p str * 2
  end

end

A.new.learn do
  a
  b
  c 'c'
end
# =>
# "aaaa"
# "bbbb"
# "cc"

We can make this form string:

class A
 
  def load(recipe)
    instance_eval(recipe)
  end

end

recipe = <<-RECIPE

a
b
c 'c'

RECIPE

A.new.load(recipe)
# =>
# "aaaa"
# "bbbb"
# "cc"

[ruby] How to add new method in a class

Look at the below example:

module M
  def a
    p "aaaa"
  end
end

class A
  include M

  def self.create_method(name, &block)
    M.send(:define_method, name, &block)
  end
end

a = A.new

hello = %q{def hello() p "Hello there!"; a end}

M.module_eval(hello)

a.hello # Event i has created `a` already
# "Hello there!"
# "aaaa"

A.create_method :foo do
  puts 'I am fool'
end

a.foo # => I am fool