Wednesday, October 16, 2013

How to set up production mode in Rails 4 with Thin and Apache

I just completed a few days of research on the problem: how can Apache serve static assets while Thin runs Rails 4 application on the given port. Apache should create far-future expires headers on assets also. So, to do this, you need some

Apache modules:

a2enmod proxy_balancer
a2enmod proxy_http
a2enmod rewrite
a2enmod headers
a2enmod expires


My Apache virtualhost config:

<virtualhost>
    ServerName my_site.com
    ServerAlias *.my_site.com
    DocumentRoot /home/my_path/my_app/public

    RewriteEngine On

    <proxy balancer:="" thinservers="">
     BalancerMember http://127.0.0.1:5000
     </proxy>
   
    <location assets="">
        Header unset ETag
        FileETag None
        ExpiresActive On
        ExpiresDefault "access plus 1 year"
    </location>

    RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
    RewriteRule ^/(.*)$ balancer://thinservers%{REQUEST_URI} [P,QSA,L]

    # Custom log file locations
    ErrorLog  /home/my_path/my_app/log/error.log
    CustomLog /home/my_path/my_app/log/access.log combined

</virtualhost>


After then, restart your Apache service:

service apache2 restart

That was Apache configuration thanks to this post:
http://bibwild.wordpress.com/2012/11/20/apache-conf-for-rails-asset-pipeline



Setting up Thin


There is my thin configuration file in /etc/thin directory. Set environment to production my_site_name.yml:

chdir: /home/my_path/my_app
environment: production
address: 0.0.0.0
port: 5000
timeout: 30
log: log/thin.log
pid: tmp/pids/thin.pid
max_conns: 1024
max_persistent_conns: 100
require: []
wait: 30
servers: 1
daemonize: true
onebyone: true
user: my_user_name
group: my_user_name
tag: my_site_name-publ


I made a script in application's bin/ directory, my_site_name:

#!/bin/sh
#

# Start the service
start() {
    thin start -C /etc/thin/my_site_name.yml
}

# Stop the service
stop() {
    thin stop -C /etc/thin/my_site_name.yml    
}

# Start the service
restart() {
    thin restart -C /etc/thin/my_site_name.yml
}

### main logic ###
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart)
        restart
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit 0


This is important for start / stop / restart your Rails 4 application easily. Set this file executable. Then just restart Thin like this, in you app's directory:

bin/my_site_name restart


Configuration for production mode

First install yui-compressor gem to minify CSS also.
After then, insert gem 'therubyracer' to production section into gemfile.
 


source 'https://rubygems.org'

gem 'rails', '4.0.0'
gem 'mysql2'
gem 'sass-rails', '~> 4.0.0'

gem 'uglifier', '>= 1.3.0'
gem 'yui-compressor','~> 0.12.0'
gem 'coffee-rails', '~> 4.0.0'
gem 'coffee-script-source', '1.5.0'
gem 'therubyracer', platforms: :ruby

gem 'jquery-rails'
gem 'turbolinks'
gem 'jquery-turbolinks'

gem 'jbuilder', '~> 1.2'

group :doc do
  # bundle exec rake doc:rails generates the API under doc/api.
  gem 'sdoc', require: false
end

gem 'execjs'
gem 'savon'
gem 'faye'
gem 'thin'

group :production do
  gem 'therubyracer'
end



And run 'sudo bundle install'

See config/environments/production file:

MySiteName::Application.configure do

  # Code is not reloaded between requests.
  config.cache_classes = true

  # Eager load code on boot. This eager loads most of Rails and
  # your application in memory, allowing both thread web servers
  # and those relying on copy on write to perform better.
  # Rake tasks automatically ignore this option for performance.
  config.eager_load = true

  # Full error reports are disabled and caching is turned on.
  config.consider_all_requests_local       = false
  config.action_controller.perform_caching = true

   # Disable Rails's static asset server (Apache or nginx will already do this).
  config.serve_static_assets = false
 
  # Compress JavaScripts and CSS.
  config.assets.js_compressor = :uglifier
  config.assets.css_compressor = :yui

  # Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = false

  # Generate digests for assets URLs.
  config.assets.digest = true

  # Version of your assets, change this if you want to expire all your assets.
  config.assets.version = '1.0'

  # Specifies the header that your server uses for sending files.
  config.action_dispatch.x_sendfile_header = nil  #"X-Sendfile" # for apache

   # Set to :debug to see everything in the log.
  config.log_level = :info

   # Precompile additional assets.
  # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
  config.assets.precompile = ['*.js', '*.js.erb', '*.css', '*.css.erb', '*.js.coffee.erb']
  config.assets.precompile << %w(*.png *.jpg *.jpeg *.gif *.ico)
  config.assets.precompile += [ "images/icons/*" ]


  # Ignore bad email addresses and do not raise email delivery errors.
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
   config.action_mailer.raise_delivery_errors = false

  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  # the I18n.default_locale when a translation can not be found).
  config.i18n.fallbacks = true

  # Send deprecation notices to registered listeners.
  config.active_support.deprecation = :notify

  # Disable automatic flushing of the log to improve performance.
   config.autoflush_log = false

  # Use default logging formatter so that PID and timestamp are not suppressed.
  config.log_formatter = ::Logger::Formatter.new
end


Don't forget to set config.action_dispatch.x_sendfile_header to nil and pay attention to precompile section. Add image subfolders and image extensions also.

 Now you have to compile assets to public/assets directory, but first empty cache and assets files:


rm -rf tmp/*
rm -rf public/assets/*
RAILS_ENV=production bundle exec rake assets:precompile

Don't forget to restart your Rails 4 application, but probably needs to kill thin process because assets files has been changed.

See this post also, it helped me a lot:
http://blog.55minutes.com/2012/02/untangling-the-rails-asset-pipeline-part-1-caches-and-compass/



And the result is a simple live mathematical practice site.

That's all, good luck!

Wednesday, February 29, 2012

Rails 3 ajax Autocomplete with jQuery and jQuery UI theme

So we want a form element with autocomplete. First thing to do is installing jquery-rails and jquery-ui-themes gems and require in Gemfile.

Then require one of the jQuery UI theme into app/assets/stylesheets/application.css
 *= require_self
 *= require_tree .
 *= require_style
 *= require jquery-ui/ui-lightness
*/

 app/assets/javascripts/application.js

//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require_tree .

Our controller responds json object in apps/controllers/accounts_controller.rb
    def search_account
        @accounts=Account.where("LOWER(name) LIKE ?", "%#{params[:name].downcase}%").limit(params[:maxRows]).order(:name)
        
        respond_to do |format|
            format.json {render :json => @accounts}
        end
    end


I've created a helper for input element app/helpers/application_helper.rb
(Take care of heredoc strings.)

    def autocomplete_input(attributes={})
        #*need* object: this form's object : "person"
        #*need* instance: searcheable object : 'account' 
        # instance_key: primary key of this instance : 'account_id'
        # value: original or default value for instance : 'Yahoo Corp.'
        # value_key: original or default value for instance_key: 252
        #*need* ajax_url: remote url for ajax call without format: url_for(:controller=> :accounts, :action=> :search, :id=>'all')
        # ajax_query_additional_params (Array): ["maxRows: 7", "anything :20"]
        #*need* ajax_query_searchable_param Object's string (String): "name" (@person.account.name)
        #*need* ajax_query_searchable_param_key Object's foreign key (String): "id" (@person.account.id)
        # min_length minimal chars to start searching: 2    
        
        if attributes.nil? then return false end
        if attributes.empty? then return false end
        object=attributes[:object]
        instance=attributes[:instance]
        if object.nil? or instance.nil? then return false end    
        object=object.to_s.downcase
        instance=instance.to_s.downcase
        instance_key=attributes[:instance_key] || instance + "_id"
        value=attributes[:value] || ""
        value_key=attributes[:value_key] || ""
        value_key_html=""
        unless value_key.to_s.empty? then
            value_key_html=" value=\""+value_key.to_s+"\"" 
        end
        ajax_url=attributes[:ajax_url]
        ajax_query_additional_params=attributes[:ajax_query_additional_params] || ""
        ajax_query_searchable_param=attributes[:ajax_query_searchable_param] || "name"
        ajax_query_searchable_param_key=attributes[:ajax_query_searchable_param_key] || "id"
        min_length=attributes[:min_length] || 2
        
        ajax_query_additional_params_formatted=""
        unless ajax_query_additional_params.nil? then
            case ajax_query_additional_params
                when Array then
                    i=0
                    ajax_query_additional_params.each do |aqap|
                        if i==0 then
                            ajax_query_additional_params_formatted=ajax_query_additional_params_formatted + aqap.to_s
                        else
                            ajax_query_additional_params_formatted=ajax_query_additional_params_formatted + ",\n" + aqap.to_s
                        end
                        i=i.next
                    end
                when String then
                    ajax_query_additional_params_formatted=ajax_query_additional_params
                when Hash then
                    i=0
                    ajax_query_additional_params.each do |aqap_key,aqap_value|
                        if i==0 then
                            ajax_query_additional_params_formatted=ajax_query_additional_params_formatted + aqap_key.to_s + ": " + aqap_value.to_s
                        else
                            ajax_query_additional_params_formatted=ajax_query_additional_params_formatted + ",\n" + aqap_key.to_s + ": " + aqap_value.to_s
                        end
                        i=i.next
                    end
            end
        end
        jquery_request_data_params="data: {\n"
        unless ajax_query_additional_params_formatted.empty? then
            jquery_request_data_params=jquery_request_data_params + ajax_query_additional_params_formatted + ",\n"
        end
        jquery_request_data_params=jquery_request_data_params + "#{ajax_query_searchable_param}: request.term\n},"
        search_field_id="search_#{object}_#{instance}"
        value_div_id="#{object}_#{instance}_log"
        hidden_field_id="#{object}_#{instance_key}"
        hidden_field_name="#{object}[#{instance_key}]"
        function_log_name="log_#{object}_#{instance}"
        
        html_text = <<HTML1
<table><tbody><tr>
    <td><input id="#{search_field_id}" class="ui-autocomplete-input"/></td>
    <td><div id="#{value_div_id}" class="ui-widget-content">#{value}</div></td>
</tr></tbody></table>
<input type="hidden" id="#{hidden_field_id}" name="#{hidden_field_name}" #{value_key_html} >

HTML1
        
         js_text = <<JS1
        
$(function() {

    function #{function_log_name}( label, id ) {
        $( "##{value_div_id}" ).html(label);
        $( "##{hidden_field_id}").val(id);
    }

    $( "##{search_field_id}" ).autocomplete({
        source: function( request, response ) {
            $.ajax({
                url: "#{ajax_url}.json",
                dataType: "json",
                #{jquery_request_data_params}
                success: function( data ) {
                    response( $.map( data, function( item ) {
                        return {
                            label: item.#{ajax_query_searchable_param},
                            value: item.#{ajax_query_searchable_param},
                            id: item.#{ajax_query_searchable_param_key}
                        }
                    }));
                }
            });
        },
        minLength: #{min_length},
        select: function( event, ui ) {
            if (ui.item) {
                #{function_log_name}( ui.item.value, ui.item.id );
            } else {
                #{function_log_name}( this.value, this.value );
            }
        },
        open: function() {
            $( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
        },
        close: function() {
            $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
        }
    });
});
        
JS1
    concat(raw(javascript_tag(js_text)))
    concat(raw(html_text))
    end

Finally insert autocomplete element in _form.htm.erb

<div class="ui-widget">
<label for="search_account">Account:</label><br />
<% autocomplete_input(:object    => "person",
            :instance            => "account",
            :instance_key        => "account_id",
            :value               => (@person.account.name unless @person.account.nil?),
            :value_key           => @person.account_id,
            :ajax_url            => url_for(:controller => 'accounts',
                                            :action     => 'search_account',
                                            :id         => 'all'),
            :ajax_query_additional_params    => {:maxRows => 7},
            :ajax_query_searchable_param     => "name",
            :ajax_query_searchable_param_key => "id",
            :min_length          => 2
)%>
</div>
</div>

Works with Rails 3.2 and jQuery JavaScript Library v1.7.1. Good luck! Questions?

Tuesday, August 9, 2011

How to create scriptaculous ajax inplace editor fields in Rails3 with validation

First, we should do our html view in app/views/ips/index.html:

<div id="ips_table_div">
<%= render "ips_table" %>
</div>

Then, we create the table and the javascript code in our partial view (app/views/ips/_ips_table.html.erb).

<table cellpadding="10"><tbody>

<% unless @ips.nil? %>

		<tr>

			<td id="ip_new_ip_td_div">
				<%= form_tag url_for(:controller => :ips,
							:action	=> :create_ip),
						:remote => true,
						:id 	=> "new_ip" %>
					
						<%= text_field_tag "ip" %>
						<%= submit_tag t(:add_button, :scope => [:application]), :id => "new_ip_submit" %>
				</form>
			</td>

			<td>
			</td>

		</tr>

	<% @ips.each do |ip| %>
		<tr>

			<td id="ip_ip_td_div_<%=ip.id %>">
				<div id="ip_ip_text_div_<%=ip.id %>"><%= ip.ip %></div>
			</td>

			<td>
				<%= link_to t(:delete_link, :scope => [:application]),
 								url_for(:controller => :ips,
									:action	=> :delete_ip,
									:id 	=> ip.id),
								:remote => true,
								:confirm => t(:ip_delete_confirm, :scope => [:application])%>
			</td>

		</tr>
	<% end %>

<% end %>

</tbody></table>

<script type="text/javascript">
var ip_validate_background_color_error = "#fff999";
var ip_validate_border_color_error = "#f80000";
var ip_validate_background_color = "#ffffff";
var ip_validate_border_color = "#999999";

var ipModifyFormOptions= $H({
		cancelText:'<%=t :cancel_link, :scope => [:application] %>',
		okText: 'ok',
		savingText: '<%=t :saving_text, :scope => [:application] %>',
		loadingText: '<%=t :loading_text, :scope => [:application] %>',
		clickToEditText: '<%=t :click_to_edit_text, :scope => [:application] %>',
		
});

<% unless @ips.nil? %>

Form.findFirstElement('new_ip').style.borderColor = ip_validate_border_color;
Form.findFirstElement('new_ip').style.backgroundColor = ip_validate_background_color;
Form.findFirstElement('new_ip').setAttribute("onkeypress","return input_filter_exact(event, 'ip')");
$('new_ip_submit').disabled=true;
new Form.Observer('new_ip', 0.3, function(form, value) {
		if(validateIpAddress(Form.findFirstElement('new_ip').value, false) == true) {
				Form.findFirstElement('new_ip').style.borderColor = ip_validate_border_color;
				Form.findFirstElement('new_ip').style.backgroundColor = ip_validate_background_color;
				$('new_ip_submit').disabled=false;
		}	else {
				Form.findFirstElement('new_ip').style.borderColor = ip_validate_border_color_error;
				Form.findFirstElement('new_ip').style.backgroundColor = ip_validate_background_color_error;
				$('new_ip_submit').disabled=true;
		}
});


	<% @ips.each do |ip| %>

new Ajax.InPlaceEditor('ip_ip_text_div_<%=ip.id %>', '<%= url_for :controller => :ips, :action => :modify_ip, :id => ip.id	%>', ipModifyFormOptions.merge({
	formId: 'ip_ip_form_<%=ip.id %>',
	onEnterEditMode: function(form, value) {
			setTimeout(function() {
					Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.borderColor = ip_validate_border_color;
					Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.backgroundColor = ip_validate_background_color;
					Form.findFirstElement('ip_ip_form_<%=ip.id %>').setAttribute("onkeypress","return input_filter_exact(event, 'ip')");
					new Form.Observer('ip_ip_form_<%=ip.id %>', 0.3, function(form, value) {
						if(validateIpAddress(Form.findFirstElement('ip_ip_form_<%=ip.id %>').value, false) == true) {
							Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.borderColor = ip_validate_border_color;
							Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.backgroundColor = ip_validate_background_color;
							$('ip_ip_form_<%=ip.id %>').elements[1].disabled=false;
						}	else {
							Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.borderColor = ip_validate_border_color_error;
							Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.backgroundColor = ip_validate_background_color_error;
							$('ip_ip_form_<%=ip.id %>').elements[1].disabled=true;
						}
				});
			}, 1000);
	}
}).toObject());

	<% end %>

<% end %>
</script>

Then we should make the controller (app/controllers/ips_controller.rb):

respond_to :js, :only => [:delete_ip, :create_ip]
respond_to :html, :except => [:delete_ip, :create_ip]
layout :application, :except => [:delete_ip, :create:ip, :modify_ip]
layout false, :only => [:delete_ip, :create:ip, :modify_ip]

def index
  @ips=Ip.all
  respond_with(@ips)
end

def delete_ip
  Ip.destroy(params[:id])
  # it will returns a javascript code by default, so have to do the
  # app/views/ips/delete_ip.js.erb, what will loads the table completely
 @ips=Ip.all
  respond_with(@ips)
end

def create_ip
  ip=Ip.new(params[:ip]).save
  # it will returns a javascript code, so have to do the
  # app/views/ips/create_ip.js.erb, what will loads the table completely
  @ips=Ip.all
  respond_with(@ips)
end

def modify_ip
  ip=Ip.find(params[:id])
  ip.update_attribute(:ip, params[:value])
  # don't forget some validations here also
  # it will returns a html code, so have to do the
  # app/views/ips/modify_ip.html.erb, what will contains the new text only, or:
  respond_to do |format|
    format.html { render :text => ip.ip }
  end
end

Then we can create returned javascript codes app/views/ips/create_ip.js.erb and delete_ip.js.erb:

  $("ips_table_div").innerHTML='';
  $("ips_table_div").update("<%=escape_javascript(render(:template => "/ips/_ips_table.html.erb", :locals => {:ips => @ips})) %>"); 

Finally we must define javascipt validation functions in public/apllication.js:

function validateIpAddress(value, can_be_empty) {
    if (arguments.length == 1) {
        can_be_empty = false;
    }
    if (can_be_empty == true) {
        if (value == null) {
            return false;
        }
        if (value == "") {
            return true;
        }
    }
    if (value.match(/\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/))
    {
        if (value.length>0 && value.length<256)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false; 
    }
}

function input_filter_exact(e, type) {
    var enabled_chars;    
    switch (type)
        {
            case "ip": enabled_chars="0123456789.";
                                    break;
            default: enabled_chars="-/.,_?[]()!abcdefghijklmnopqrstvuwxyzABCDEFGHIJKLMNOPQRSTVUWXYZ0123456789éáőúűóüöíÉÁŐÚÓÜÖÍ ";
        }
    var key;
  var keychar;
    if (window.event)
        key = window.event.keyCode;
  else if (e)
        key = e.which;
    else
        return true;
    keychar = String.fromCharCode(key);
    if ((key==null) || (key==0) || (key==8) || (key==9) || (key==13) || (key==27) )
        return true;
    else if (((enabled_chars).indexOf(keychar) > -1))
        return true;
    else
    return false;
} 


That's all. Works with Rails 3.0.9, Prototype 1.7 and script.aculo.us v1.8.3

Wednesday, August 3, 2011

How to sort mysql table by ip address string column

There are lots of result for ordering mysql table by IP address string column on the net. These are almost hellish tricky. So I use this:

ips=Ip.where(:reserved => false).order("INET_ATON(address) ASC")

You can save numeric value of ip address also with INET_ATON, then you can get the string value by "INET_NTOA()" mysql function.

Here is the link about mysql miscellaneous functions:
http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html

Good luck!

Wednesday, May 18, 2011

Scriptaculous Slider in Rails3 form

So there is an excellent slider. http://madrobby.github.com/scriptaculous/slider/ It is perfect to set up a select form element where values are predefined numbers or integers. First you have to download http://script.aculo.us/downloads the slider.js file and move it to the /public/javascripts directory. Then insert this row in your layout, /app/views/layouts/application.html.erb after the defaults.

<%= javascript_include_tag "slider.js" %>

Then we define CSS style for the slider as the same as at the script.aculo.us homepage. (/app/public/stylesheets/style.css)

div.slider {
 width:256px;
 margin:10px 0;
 background-color:#ccc;
 height:10px;
 position: relative;
}

div.slider div.handle {
 width:10px;
 height:15px;
 background-color:#f00;
 cursor:move;
 position: absolute;
}

Then we create a helper for it. (/app/helpers/application_helper.rb)

def slider(attributes={})
  unless attributes.nil? then
  values=attributes[:values]
  default_value=attributes[:default_value].to_s
  object=attributes[:object].to_s
  instance=attributes[:instance].to_s
  unit=attributes[:unit] || ""
  text_div_id=object + "_" + instance + "_slider_text_div"
  slider_id=object + "_" + instance + "_slider"
        
  concat(raw(
    "<div id=\"#{slider_id}\" class=\"slider\">
       <div class=\"handle\"></div>
     </div>"
  ))

  concat(raw(hidden_field(object, instance, :value => default_value.to_i)))
  concat(raw("<div id=\"#{text_div_id}\">" + default_value + " " + unit + "</div>"))
        
  js_text = <<JS3
  (function() {
    var zoom_slider = $('#{slider_id}');
    var text_div = $('#{text_div_id}');
    var hidden_item = $('#{object + "_" + instance}');
    var values=#{values.inspect};
    
    var values_length = values.length;
    var min=40;
    var max=200;

    var unit = Math.ceil((max - min) / values_length);
    var range = $R(min, max - unit);

    var pix_values = new Array();
    for(i=1;i<=values_length;i++){
      pix_values[i-1]=min + (unit * (i - 1));
    }
        
    var default_value=pix_values[#{values.index(default_value.to_i)}];

    new Control.Slider(zoom_slider.down('.handle'), zoom_slider, {
      range: range,
      sliderValue: default_value,
            increment: unit,
            values: pix_values,
      onSlide: function(value) {
                text_div.innerHTML=values[pix_values.indexOf(value)] + " #{unit}";
                hidden_item.value=values[pix_values.indexOf(value)];
      },
      onChange: function(value) { 
                text_div.innerHTML=values[pix_values.indexOf(value)] + " #{unit}";
                hidden_item.value=values[pix_values.indexOf(value)];
      }
    });
  })();
        
JS3
      concat(raw(javascript_tag(js_text)))
  end
end

Then just define your form element in your view / form:

<table><tbody><tr><td>
<% slider :object        => "config",
          :instance      => "mem_size",
          :unit          => "MB",
          :values        => [512, 1024, 1536, 2048, 2560, 3072, 4096].sort,
          :default_value => 1024 %>
</td></tr></tbody></table>

That"s all! Questions?

Monday, May 9, 2011

Rails3 UTF-8 support for strings

There is a general problem in rails 3 with utf8 string support. For example upcase, downcase, titleize or capitalize. Basically these methods produce ASCII strings because of lots of memory usage of unicode strings. mb_chars string method do the trick. Create a new rb file in /lib:
[String].each do |klass|
  klass.class_eval <<-RUBY, __FILE__, __LINE__
 
 def utf8_downcase
  mb_chars.downcase.to_s
 end
 
 def utf8_upcase
  mb_chars.upcase.to_s
 end
 
 def utf8_capitalize
  mb_chars.capitalize.to_s
 end

 def utf8_titleize
  mb_chars.titleize.to_s
 end

RUBY
end

Works with Rails 3.0.7 and Ruby 1.8 surely. Don't forget to put in config/application.rb this row after require 'rails/all':

Dir.glob("./lib/*.{rb}").each { |file| require file }
Now you can call:

irb(main):005:0> "é ÉÚŐÚ ééé ŐŐÚSDFSF".utf8_downcase
=> "é éúőú ééé őőúsdfsf"

That's all. Questions?

Thursday, March 3, 2011

How to submit an ajax form in javascript with Rails3 and Prototype

So there is a Rails 3 problem, when you want to submit a form in javascript (without a submit button), it is working on normal way, not ajax. But we can do creating a hidden submit button and then initializing a click event on it what is exactly doing ajax submission. The submit(); is not working anymore because it doesn't call the rails.js functions. It just works when you simple want to create a not ajax post without any confirmation. Let's see an ajax example in view's index.html.erb file:

<%= form_tag "some_url",
 :remote  => true,
 :method  => :post,
 :name  => "some_procedure",
 :id   => "some_procedure" %>
<%= submit_tag 'procedure_submit_button', :id =>"procedure_submit_button" , :style => "display: none" %>

You can add here your html code for form and create some element what is doing the submission inside a table, for a td:

<td onclick="javascript: procedure_init();" style="cursor: pointer;">

put here an image for example

</td>

</form>

<%= javascript_tag <<-RUBY
function procedure_init()
{
 var submit_button="procedure_submit_button";
 var form_name="some_procedure";
 // here you can build the form, or modify form parameters
 $(submit_button).click();
}
RUBY
%>

If you have problems with sessions or current_user quits within ajax rendering, put this code to public/javascripts/application.js. /thanks to this answer/

document.observe("dom:loaded", function() {
 Ajax.Responders.register({
  onCreate: function(request) {
   var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
   if (csrf_meta_tag) {
    var header = 'X-CSRF-Token',
      token = csrf_meta_tag.readAttribute('content');

    if (!request.options.requestHeaders) {
     request.options.requestHeaders = {};
    }
    request.options.requestHeaders[header] = token;
   }
  }
 });
});

That's all. Questions?

Monday, November 22, 2010

Each loop for a class or model instance variables

Sometimes we need to check all of instance variables in a class or model one by one. In this exaple we will check if a varaible is an Array or not:

your_model.instance_variables.each do |i|
 if your_model.instance_variable_get(i).instance_of?(Array) then
  #your code to do anything with your_model.instance_variable_get(i) what is a value
 end
end



If you have an active record model, you can do:

@account = Account.first

Account.column_names.each do |i|
  @account.instance_eval(i)
 # row returns @account.name for example inside the loop, next @account.address and so on
end





That's all!

How to get requested controller name, action name and id in Rails3 view

To get requested controller name, action name and id in view we will call a simple helper function. For example we have the '/users/show/23' request, by routing rule: 'match ':controller(/:action(/:id(.:format)))''


#to get controller name:
<%= controller.controller_name %>
#=> 'users'

#to get action name, it is the method:
<%= controller.action_name %>
#=> 'show'


#to get id information:
<%= ActionController::Routing::Routes.recognize_path(request.url)[:id] %>
#=> '23'


That's all!

Monday, November 8, 2010

I18n yml files in Rails3

Your locale files are located in config/locales directory by default. Let's make two locale files: en.yml and for example: hu.yml. In that files we can describe our string translations what we will use in our page view with helpers. Take care of yml rules, you can use spaces only instead of tabs, and spaces determine level of string. You can use double quotes to define a string, but if you want to use quotes inside the string, use html entity: " for it. You can use html in strings fo course, but don't forget raw helper in view before. Finally, you can give variables to yml simply, between %{var} (in rails2: {{var}} ) signs and full string needs to be between double quotes. I used to create yml blocks by controllers.


Labels are very simple also. Just take under helpers: and controller_name: (helpers: and contact: in the example below). Submit definitions are the same like labels.


en:
  contact:
    prices:
      show_price: "%{price_dollar}$"
      quote: "Coordinator: & quot;Crucifixion?& quot? & lt;br /& gt; Mr. Cheeky: & quot;Er, no, freedom actually.& quot;"

  attributes:
    created_at: "Created at"
    updated_at: "Updated at"

  helpers:
    submit:
      contact:
        create: "Order"
        update: "Modify"
    label:
      contact:
        name: "Your name"
        company_name: "Company name"
        phone: "Phone number"
        email: "E-mail"
        comment: "Comment"

  date:
    formats:
      default: "%Y-%m-%d"
      short: "%b %d"
      long: "%B %d, %Y"
    day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
    abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
    month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
    abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
    order: [ :year, :month, :day ]
 
  time:
    formats:
      default: "%a, %d %b %Y %H:%M:%S %z"
      short: "%d %b %H:%M"
      long: "%B %d, %Y %H:%M"
    am: "am"
    pm: "pm"


And the reference in our view file (apps/views/contact/index.html.erb):


<%= form_for @contact, :as   => :contact,
   :url  => {:action => 'new'},
   :html => {} do |f| %>

<%=f.label :name %>

<%= f.submit %>

<%=t :show_price, :scope => [:contact, :prices], :price_dollar => 50 %>

<%=raw t(:quote, :scope => [:contact, :prices], :locale=>'en') %>
<%=l Date.today, :format => :long %>

You can define exact locale in view like :locale=>'hu'. Localize dates with l helper function. You can use I18n.locale in controller for getting current locale. You can get all locales in an array with: I18n.available_locales . Don't forget to check application.rb in config directory, where you can setting up default locale:


config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
     config.i18n.default_locale = :hu

Then make a method in app/controllers/application_controller.rb for users to change their language and create a before_filter:


class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_locale

  def set_locale
  locale = params[:locale] || session[:locale] || I18n.default_locale.to_s
  locale = I18n.available_locales.include?(locale.to_sym) ? locale : 18n.default_locale.to_s
  session[:locale] = I18n.locale = locale
  end
end

Then create your link to set locale in your layout: link_to('English', url_for(:locale => 'en')). That's all! Questions?