Halaman

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

Rabu, 01 Juni 2011

Counting sub-string occurrence in a string

Once upon a time.. I need a way to count number of occurrence of certain sub-string (not exclusively words) inside a string.

For example, string "Aya Hirano is the best seiyuu in the world! She was mostly known as her role as Haruhi." I wanted to know how many times string "he" occurred in there.
The result should be 4, 2 in the, 1 in She and 1 in her.

By using scan method we can do that easily:
class String
  def substr_count(pattern)
    self.scan(pattern).length
  end
end

"Aya Hirano is the best seiyuu in the world! She was mostly known as her role as Haruhi.".substr_count("he") # -> 4
"Aya Hirano is the best seiyuu in the world! She was mostly known as her role as Haruhi.".substr_count(/(!|\.|\?)/) # -> 2 -> counting sentences? :D

Too simple to become share-worthy article I guess.. :P