Halaman

Rabu, 22 Desember 2010

[shared] change rails form error field wrapper

by default rails wrap all fields that contain error data using div with "fieldWithErrors" class

but sometimes we want to change that wrapper for example to use span instead of div.
that can easily be done by overriding field_error_proc class attribute in ActionView::Base

in environment.rb
ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| "#{html_tag}".html_safe }

file position (for rails 2.3.10):
[ruby_dir]\lib\ruby\gems\1.8\gems\actionpack-2.3.10\lib\action_view\helpers\active_record_helper.rb

[shared] rails render_invalid method

purpose


provide DRY solution for rendering (or redirecting) from controller on invalid request, such as failed validation on create/update

requirements


- jquery js framework
- notifications helper from my earlier post about custom ajax helper:
http://tech.maysora.com/2010/11/rails-custom-ajax-helper-for-jquery.html

codes


application_controller.rb
# render invalid request to show error message depend on request type
# example:
#   render_invalid "Update failed."
#     ajax: show error notification with message "Update failed."
#     html: simply render text "Update failed."
#   render_invalid "Save failed.", :render => {:action => "new"}
#     ajax: show error notification with message "Save failed."
#     html: render action new with "Save failed." flash message
#   render_invalid "Save failed.", :render => {:action => "new"}, :flash => false
#     same as above but without flash for html
#   render_invalid "You can't do that.", :redirect_to => {:action => "index"}, :flash => false, :status => :unauthorized
#     same as above but redirecting to index instead of rendering new for html
#     and using unauthorized(401) status instead of not_acceptable(406)
#   render_invalid "Update failed.", :object => @user, :fields_wrapping => true
#     ajax: show error notification with addition of object's error messages and wrap error fields with div class fieldWithErrors
#     html: same as ajax minus fields wrapping
def render_invalid message="Error.", options={:flash => true}
  options[:status] ||= :not_acceptable
  message += "
#{options[:object].errors.full_messages.join(' ')}
" if options[:object].present? respond_to do |format| format.js do render(:update, :status => options[:status]) do |page| page << notification_error(message) if options[:object].present? && options[:fields_wrapping] page.rjs_clean_error_fields request page.rjs_apply_error_fields options[:object], request end end end format.html do if options[:render].present? flash.now[:error] = message if options[:flash] render options[:render].try(:merge, {:status => options[:status]}) || {:text => message, :status => options[:status]} elsif options[:redirect_to].present? flash[:error] = message if options[:flash] redirect_to options[:redirect_to] else render :text => message, :status => options[:status] end end end end

application_helper.rb (only if you use ajax error fields wrapping)
def rjs_apply_error_fields object, request
  klass_name = object.class.name.underscore
  wrapper = "
" object.errors.each do |attr, msg| attr = attr.split(".") page << "var $fields = $(), $container = $('\##{klass_name}_#{object.id || "new"}');" page << "if(!$container.length) $container = $('form[action=\\'#{request.request_uri}\\']');" page << "if(!$container.length) $container = $(document);" if attr.length > 1 object.send(attr.first).each_with_index do |x,i| page << "$fields = $fields.add('[id^=#{klass_name}_#{attr.first}_attributes][id$=#{attr.last}]:eq(#{i}),label[for^=#{klass_name}_#{attr.first}_attributes][for$=#{attr.last}]:eq(#{i})', $container)" if x.errors.on(attr.last) end else page << "$fields = $('label[for=#{klass_name}_#{attr}],\##{klass_name}_#{attr}', $container);" end page << "if(!$fields.parent('.fieldWithErrors').length)" page << "$fields.wrap('#{wrapper}')" end end def rjs_clean_error_fields request page << "$('.fieldWithErrors.rjs_#{request.request_method}_#{request.request_uri.gsub(/_=\d+($|&)/, "").parameterize} *').unwrap();" end

usage

users_controller.rb (using non ajax form)
#...

def create
  @user = User.new(params[:user])
  unless @user.save
    render_invalid "Failed to register.", :object => @user, :render => {:action => "new"}
  else
    flash[:info] = "Registration success, please check your email to activate."
    redirect_to login_path
  end
end

#...
users_controller.rb (using ajax form)
#...

def update
  @user = current_user
  unless @user.update_attributes(params[:user])
    render_invalid "Profile update failed.", :object => @user, :fields_wrapping => true
  end
end

#...
and update.js.rjs (for success behavior)
page << notification_info "Profile updated."
page.rjs_clean_error_fields request # used for cleaning error fields wrapping
#... other necessary action ...

additional tips

depending on your site style, it might be a good idea to change the div wrapper in rjs_apply_error_fields helper to span. you can also change rails standard error field wrapper for non-ajax form, see this post

[shared] floating point error in js

console.log(0.1+0.2); //-> 0.30000000000000004
console.log(0.1*0.2); //-> 0.020000000000000004

solution:
1. use x.toFixed(n) if you only need 5 or less decimal digit
2. use function from http://www.codingforums.com/showpost.php?p=483962&postcount=9
3. best solution, use js BigDecimal library https://github.com/jhs/bigdecimal.js or http://stz-ida.de/index.php?option=com_content&view=article&id=18&Itemid=32

summary:
use number 1 if decimal precision isn't really important (most likely), or use number 3 with some performance cost

Minggu, 05 Desember 2010

foobar2k headphone surround setup

this guide will add 5.1 speaker simulation using headphone for foobar2k
the result isn't really perfect but it's noticeably increase surround effect and reduce sound inside head feels..

needed files:
- foobar2k http://www.foobar2000.org/download
- foo_input_dts http://www.foobar2000.org/components/view/foo_input_dts
- foo_channel_mixer http://skipyrich.com/wiki/Foobar2000:Channel_Mixer
- nvidia purevideo decoder http://www.nvidia.com/object/dvd_decoder_1.02-223-trial.html

step:
- install nvidia purevideo decoder, use the trial CC and activation code in the site
- install foobar
- copy foo_input_dts and foo_channel_mixer dll to [foobar installation folder]/components/
- run foobar, go to file, preferences, playback, DSP manager
- (optional) add resampler (PPHS) to active DSP, then configure it to 48000Hz Ultra mode
- add channel mixer
- configure channel mixer, general: output channels 6, check L C R RL RR, uncheck LFE, Stereo image width 1.00
- upmix: mode surround, mode surround, center 1.00, volume 2.00, all others 0.00
- add Dolby headphone, configure: dolbyhph.dll to C:\windows\system32\DolbyHph.dll
- (optional) add others DSP to the top of the list if you want, make sure channel mixer then dolby headphone is the last two on the list

other useful components:
- Skip silence
- foo_ui_columns (nice ui)
- foo_uie_lyrics (add lyric, need foo_ui_columns)
- foo_shutdown (auto shutdown/standby/hibernate)
- amip for foobar and amip configurator (add winamp like jump and now playing announce to messengers and irc)

original source: http://www.head-fi.org/forum/thread/447089/5-1-headphone-experience-foobar-configuration-for-all-stereo-music-files

Minggu, 21 November 2010

[shared] rails custom ajax helper for jquery

requirements:
- jrails plugin (https://github.com/aaronchi/jrails)
- working jquery.gritter or other js notification (http://boedesign.com/blog/2009/07/11/growl-for-jquery-gritter/)
- my jquery.extended_helper (http://code.google.com/p/jquery-extendedhelper)

installation:
- add these code in application_helper.rb
# Adding additional options for ajax function
# You can use thisElement to manipulate element that using the function (link/button/form)
def custom_remote_function_options options
  options[:before] ||= ""
  options[:loading] ||= ""
  options[:complete] ||= ""
  options[:success] ||= ""
  options[:before] += "; var thisElement=this"
  options[:failure] ||= "; eval(request.responseText)"

  unless options[:loading_text].blank?
    options[:loading] += "; thisElement.notif = #{notification_loading(options[:loading_text])}"
    options[:complete] += "; #{notification_remove "thisElement.notif"}"
  end
  options
end

# Override rails remote_function using additional options
def custom_remote_function options
  remote_function(custom_remote_function_options(options)).html_safe
end

# Override rails link_to_remote function using additional options
# Disabling the link when ajax loading in progress to avoid double request
def custom_link_to_remote name, options={}, html_options=nil
  options = custom_remote_function_options options
  options[:loading] += "; $(thisElement).setLink(false)"
  options[:complete] += "; $(thisElement).setLink(true)"
  link_to_remote name, options, html_options
end

# Override rails button_to_remote function using additional options
# Disabling the button when ajax loading in progress to avoid double request
def custom_button_to_remote name, options={}, html_options=nil
  options = custom_remote_function_options options
  options[:loading] += "; $(thisElement).setInput(false)"
  options[:complete] += "; $(thisElement).setInput(true)"
  button_to_remote name, options, html_options
end

# Adding additional options for ajax form
# Add :unchange_default => true to prevent successful request changing default form fields value
# Add :disable_link => true to disable all link inside form when loading in progress
def custom_remote_form_options options
  options = custom_remote_function_options options
  options[:loading] += "; $(':input', thisElement).setInput(false);"
  options[:complete] += "; $(':input', thisElement).setInput(true);"
  unless options[:unchange_default]
    options[:success] +=  "; $(':input', thisElement).setDefault();"
  end
  if options[:disable_link]
    options[:loading] += "; $('a', thisElement).setLink(false);"
    options[:complete] += "; $('a', thisElement).setLink(true);"
  end
  options
end

# Override rails remote_form_for using additional options
def custom_remote_form_for record_or_name_or_array, *args, &proc
  args.push custom_remote_form_options(args.extract_options!)
  remote_form_for record_or_name_or_array, *args, &proc
end
alias_method :custom_form_remote_for, :custom_remote_form_for

# Override rails form_remote_tag using additional options
def custom_form_remote_tag options={}, &block
  form_remote_tag custom_remote_form_options(options), &block
end
alias_method :custom_remote_form_tag, :custom_form_remote_tag

def custom_button_to_remote name, value, options = {}
  button_to_remote name, value, custom_remote_form_options(options)
end

# Override rails submit_to_remote using additional options
def custom_submit_to_remote(name, value, options = {})
  submit_to_remote(name, value, custom_remote_form_options(options))
end

def notification_info text, no_escape=false
  text.gsub!(/\n/, '
')
  text = escape_javascript(text) unless no_escape
  "notificationInfo('#{text}')"
end

def notification_loading text, no_escape=false
  text.gsub!(/\n/, '
')
  text = escape_javascript(text) unless no_escape
  "notificationLoading('#{text}')"
end

def notification_error text, no_escape=false
  text.gsub!(/\n/, '
')
  text = escape_javascript(text) unless no_escape
  "notificationError('#{text}')"
end

def notification_remove notification
  "notificationRemove(#{notification})"
end

- add notification code in application.js (sample using jquery.gritter)
function notificationInfo(text){
  return $.gritter.add({text: text.replace(/\n/g, '
')});
}

function notificationError(text){
  return $.gritter.add({text: '' + text.replace(/\n/g, '
') + '',time: 6000});
}

function notificationLoading(text){
  return $.gritter.add({text: text.replace(/\n/g, '
'), image: '/images/gritter-loading.gif', sticky:true})
}

function notificationRemove(notification){
  $.gritter.remove(notification);
}