Halaman

Kamis, 30 Juni 2011

Additional Range method

class Range
  # return true if 2 range overlapped
  def overlap? other_range
    include?(other_range.begin) || other_range.include?(self.begin)
  end

  # return a new intersection range between two ranges
  def & other_range
    raise TypeError, "exclusive range can't be intersected with non-exclusive one" unless self.exclude_end? == other_range.exclude_end?
    new_begin = [self.begin, other_range.begin].max
    new_end = [self.end, other_range.end].min
    new_begin <= new_end ? Range.new(new_begin, new_end, self.exclude_end?) : nil
  end

  # return true if other_range is a subset in self
  def contains? other_range
    if !exclude_end?
      include?(other_range.begin) && include?(other_range.end)
    elsif self.begin.respond_to?('>=')
      self.begin <= other_range.begin && self.end.send(other_range.exclude_end? ? '>=' : '>', other_range.end)
    else
      raise TypeError, "can't determine inclusion of range with this type of members"
    end
  end

  # return true if self is a subset of other_range
  def within? other_range
    other_range.contains? self
  end

  # return length of the range (only for subtract-able range)
  def length
    self.end.respond_to?('-') ? self.end - self.begin : nil
  end

  # return next range with same span (only for subtract-able and add-able range)
  def succ allow_touch=false
    return nil unless self.end.respond_to?('-') && self.end.respond_to?('+')
    new_begin = allow_touch || exclude_end? || !self.end.respond_to?(:succ) ? self.end : self.end.succ
    new_end = new_begin + length
    Range.new(new_begin, new_end, exclude_end?)
  end

  # return previous range with same span (only for subtract-able and add-able range)
  def pred allow_touch=false
    return nil unless self.end.respond_to?('-') && self.begin.respond_to?('+')
    new_end = allow_touch || exclude_end? || !self.begin.respond_to?(:pred) ? self.begin : self.begin.pred
    new_begin = new_end - length
    Range.new(new_begin, new_end, exclude_end?)
  end
end

Selasa, 28 Juni 2011

array flatten with level for ruby 1.8.7

straight to the code:
class Array
  alias_method :orig_flatten, :flatten
  
  def flatten level=nil
    if level.is_a?(Integer)
      temp_arr = clone
      level.times{ |i| temp_arr = temp_arr.inject([]){|s,v| v.is_a?(Array) ? s.concat(v) : s << v} }
      temp_arr
    else
      orig_flatten
    end
  end
end

of course although untested i'm sure it will have worse performance than the original flatten in ruby 1.9.2, but better than nothing right? ;)

ruby array to hash method

update 2012-12-21:
you should use flat_map instead of flatten, i'm too lazy to update the code though.. :P

we can convert hash to array easily using .to_a method for example:
hsh = {:a => 1, :b => 2, :c => 3}
arr = hsh.to_a # -> [[:a, 1], [:b, 2], [:c, 3]]

but there's no built in method to reverse that, there's no .to_hash instance method for array.

to solve that i added a simple instance method for array:
class Array
  def to_hash values=nil
    Hash[*(values.is_a?(Array) ? self.zip(values) : self).flatten(1)]
  end
end

hsh = {:a => 1, :b => 2, :c => 3}
arr = hsh.to_a # -> [[:a, 1], [:b, 2], [:c, 3]]
arr.to_hash == hsh # -> true
[:a,:b,:c].to_hash [1,2,3] # -> {:a => 1, :b => 2, :c => 3}

a little precaution, flatten(1) will not work properly in ruby 1.8.7 or older, therefore it won't be possible to create hash with array as key or value. but there's a simple workaround for that by overriding array flatten method to behave like ruby 1.9.2 in my next post :)

useful online tools for web programming

  • Regex for ruby: http://www.rubular.com/
  • strftime: http://strfti.me/
  • css gradient generator: http://www.colorzilla.com/gradient-editor/
  • css validator: http://jigsaw.w3.org/css-validator/
  • css rounder corner generator: http://a.deveria.com/roundgen/
  • 7 great js resources (not really a tool but useful): http://mashable.com/2011/07/03/7-great-javascript-resources

will be updated as i found another tools

Sabtu, 11 Juni 2011

Rails general coding style

Code:

# somethings_controller.rb
def create
  @something = Something.new params[:something]
  @something.save!
  if @something.some_state?
    @something.initialize_for_some_state
  end
  redirect_to success_url
rescue
  render :action => "failed"
end

Hint:

  1. should avoid rescue without specifying the exception, in above code, use
    rescue ActiveRecord::RecordInvalid
  2. better yet, don't use any rescue at all, use save instead of save! with some if
  3. post processing should be done in callback.
    after_create {|something| something.initialize_for_some_state if something.some_state}

Better Code:

# somethings_controller.rb
def create
  @something = Something.new params[:something]
  if @something.save
    redirect_to success_url
  else
    render :action => "failed"
  end
end

Code:

# somethings_controller.rb
def show
  @something = Something.find(:first, :conditions => "user_id = #{current_user.id} AND id = #{params[:id]} AND name = #{params[:name]}")
  respond_to do |format|
    format.html
    format.js do
      render :js => "$('#container').html('#{escape_javascript(render :partial => "something", :object => @something)}');"
    end
    format.json do
      render :json => @something.to_json
    end
  end
end

Hint:

  1. SQL injection warning! never use data from params or from user input directly in sql code. use something like this instead:
    :conditions => ["user_id = #{current_user.id} AND id = ? AND name = ?", params[:id], params[:name]]
  2. using model relation and dynamic finder you can avoid the conditions entirely
    @something = current_user.somethings.find_by_id_and_name params[:id], params[:name]
  3. you should check if nothing found (@something is nil)
  4. this is just my opinion, but in most case i'll avoid using respond_to, instead i'll use 3 different view files: show.html.erb, show.js.erb, show.json.erb. rails will automatically use the correct view.

Better Code:

# somethings_controller.rb
def show
  @something = current_user.somethings.find_by_id_and_name params[:id], params[:name]
  render_invalid "not found" if @something.nil? # read about render_invalid in http://tech.maysora.com/2010/12/rails-renderinvalid-method.html
end


to be continued about model