I18n translation problem - ruby-on-rails

I'm about to translate all time zones to Russian and I've done such things:
model:
# lib/i18n_time_zone.rb
class I18nTimeZone < ActiveSupport::TimeZone
def self.all
super.map { |z| create(z.name, z.utc_offset) }
end
def to_s
translated_name = I18n.t(name, :scope => :timezones, :default => name)
"(GMT#{formatted_offset}) #{translated_name}"
end
end
view:
<%= time_zone_select :user, :time_zone, nil, :model => I18nTimeZone %>
locale file (/config/locales/ru.yml):
ru:
timezones:
"Midway Island": "Мидуэй"
"Samoa": "Самоа"
....
But there are cases when original string includes some dots (".") Like "St. Petersburg"
And I18n.t() tells me that translation is missing.
How can I avoid it?

Just remove the dot for the translation keys.
def to_s
translated_name = I18n.t(key, :scope => :timezones, :default => name)
"(GMT#{formatted_offset}) #{translated_name}"
end
def key
#key ||= name.gsub(/\./, "")
end
ru:
timezones:
"Midway Island": "Мидуэй"
"Samoa": "Самоа"
"St Petersburg" : "some one translate this"

Related

Active Record and file: How do i write Json file with my data?

How do I write a data in table event to json file?
Please see this code:
In model event.rb
class Event < ActiveRecord::Base
attr_accessible :name, :event_description, :start_at, :end_at, :status, :eventable_id
has_event_calendar
belongs_to :eventable, polymorphic: true
after_save :write_json
end
def write_json
Event.all.each do |event|
#eventJson = {
"id" => event.id,
"start" => event.start_at,
"end" => event.end_at,
"title" => event.name,
"body" => event.event_description,
"status" => event.status
}
end
File.open("public/event.json","w") do |f|
f.write(#eventJson.to_json)
end
end
In file Json there's one record, but in table event there are many records. How do I write all records from table event to event.json file after saving the record?
public/event.json
{"id":35,"start":"2013-03-28T00:00:00Z","end":"2013-03-28T00:00:00Z","title":"1345edrewrewr","body":"123124","status":"Confirm"}
The problem is that you assign a value to #eventJson in a loop so the previous values are lost. You should use an array:
def write_json
events_json = []
Event.all.each do |event|
event_json = {
"id" => event.id,
"start" => event.start_at,
"end" => event.end_at,
"title" => event.name,
"body" => event.event_description,
"status" => event.status
}
events_json << event_json
end
File.open("public/event.json","w") do |f|
f.write(events_json.to_json)
end
end
In this case, you might want to use map instead of each -- it's much cleaner.
Given that you said the method is in the model, this is how it would look.
class Event < ActiveRecord::Base
...
def self.write_json
record_json = self.all.map{ |record| { self.name => record.attributes } }.to_json
File.open("#{Rails.root}/#{(self.name.underscore)}.json", "w") do |f|
f.write record_json
end
end
end
You could do it in the way below:
def write_json
File.open('public/event.json', 'w') { |f| f.write(Event.all.to_json) }
end
If you want to save specific fields, you can do it in this way:
def write_json
File.open('public/event.json', 'w') do |f|
f.write(Event.select(:id, :start, :end, :title, :body, :status).to_json)
end
end

Mapping hash maps to class instances

Looking for gem or at least idea how to approach this problem, the ones I have are not exactly elegant :)
Idea is simple I would like to map hashes such as:
{ :name => 'foo',
:age => 15,
:job => {
:job_name => 'bar',
:position => 'something'
...
}
}
To objects of classes (with flat member structure) or Struct such as:
class Person {
#name
#age
#job_name
...
}
Thanks all.
Assuming that you can be certain sub-entry keys won't conflict with containing entry keys, here's some code that should work...
require 'ostruct'
def flatten_hash(hash)
hash = hash.dup
hash.entries.each do |k,v|
next unless v.is_a?(Hash)
v = flatten_hash(v)
hash.delete(k)
hash.merge! v
end
hash
end
def flat_struct_from_hash(hash)
hash = flatten_hash(hash)
OpenStruct.new(hash)
end
Solution that I used it solves problem with same key names but it does not give flat class structure. Somebody might find this handy just keep in mind that values with reserved names such as id, type need to be handled.
require 'ostruct'
def to_open_struct(element)
struct = OpenStruct.new
element.each do |k,v|
value = Hash === v ? to_open_struct(v) : v
eval("object.#{k}=value")
end
return struct
end
An alternate answer where you know the keys before hand
class Job
attr_accessor :job_name, :position
def initialize(params = {})
self.job_name = params.fetch(:job_name, nil)
self.position = params.fetch(:position, nil)
end
end
class Person
attr_accessor :name, :age, :job
def initialize(params = {})
self.name = params.fetch(:name, nil)
self.age = params.fetch(:age, nil)
self.job = Job.new(params.fetch(:job, {}))
end
end
hash = { :name => 'foo', :age => 1, :job => { :job_name => 'bar', :position => 'soetmhing' }}
p = Person.new(hash)
p.name
==> "foo"
p.job
==> #<Job:0x96cacd8 #job_name="bar", #position="soetmhing">
p.job.name
==> "bar"

Refactoring a hash to Object

I have a method that return a Hash and then I write the entries of hash in xml file. Iwant to convert this Hash to an object to store the entry and then write it to xml file...
My current code is like this
def entry(city)
{
:loc => ActionController::Integration::Session.new.url_for(:controller => 'cities', :action => 'show', :city_name => city.name, :host => #country_host.value),
:changefreq => 0.8,
:priority => 'monthly',
:lastmod => city.updated_at
}
end
The write_entry method is inside my writer class that writes this entry to xml file
def write_entry(entry)
url = Nokogiri::XML::Node.new( "url" , #xml_document )
%w{loc changefreq priority lastmod}.each do |node|
url << Nokogiri::XML::Node.new( node, #xml_document ).tap do |n|
n.content = entry[ node.to_sym ]
end
end
url.to_xml
end
Thanks
I might be way off here, but it seems like what you're trying to do is something like this:
First, figure out what makes sense as a class name for your new object. I'm going with Entry, because that's the name of your method:
class Entry
end
Then take all the "properties" of your hash and make them reader methods on the object:
class Entry
attr_reader :loc, :action, :changefreq, :priority, :lastmod
end
Next you need to decide how this object will be initialized. It seems like you will need both the city and #country_host for this:
class Entry
attr_reader :loc, :action, :changefreq, :priority, :last mod
def initialize(city, country_host_value)
#loc = ActionController::Integration::Session.new.url_for(:controller => 'cities', :action => 'show', :city_name => city.name, :host => country_host_value)
#changefreq = 0.8 # might actually want to just make this a constant
#priority = 'monthly' # another constant here???
#lastmod = city.updated_at
end
end
Finally add your XML builder method to the class:
class Entry
attr_reader :loc, :action, :changefreq, :priority, :last mod
def initialize(city, country_host_value)
#loc = ActionController::Integration::Session.new.url_for(:controller => 'cities', :action => 'show', :city_name => city.name, :host => country_host_value)
#changefreq = 0.8 # might actually want to just make this a constant
#priority = 'monthly' # another constant here???
#lastmod = city.updated_at
end
def write_entry_to_xml(xml_document)
url = Nokogiri::XML::Node.new( "url" , xml_document )
%w{loc changefreq priority lastmod}.each do |node|
url << Nokogiri::XML::Node.new( node, xml_document ).tap do |n|
n.content = send(node)
end
end
url.to_xml
end
end
Now that your hash has been refactored, you can update your other class(es) to use the new object:
class WhateverClassThisIs
def entry(city)
Entry.new(city, #country_host.value)
end
end
It's not clear how the XML writer method is being called, but you would need to update that as well to use the new write_entry_to_xml method, passing in the xml document as an argument.

In Rails 3.1, how can I create an HTML table generator that uses block style formatting

I'm developing an application that displays tabular data in many different areas and I find myself constantly using the same HTML table structure over and over. For example a particular table looks like this:
%table.zebra-striped#user-table{ :cellspacing => "0" }
%colgroup
%col{:id => "email"}
%col{:id => "username"}
%col{:id => "sign-in-count"}
%col{:id => "last-sign-in-at"}
%thead
%tr
%th{:id => "email-head", :scope => "col"} E-mail
%th{:id => "username-head", :scope => "col"} Username
%th{:id => "sign-in-count-head", :scope => "col"} Sign Ins
%th{:id => "last-sign-in-at-head", :scope => "col"} Last Sign In
%tbody
- #users.each do |user|
%tr{ :class => zebra }
%td
=h user.email
%td
=h user.username
%td
=h user.sign_in_count
%td
=h user.last_sign_in_at
Ideally, I would like to create some kind of helper method where I could do something like:
= custom_table_for #users do
= column :email
= column :username do |user|
= link_to user.username, user_path(user)
= column "Sign Ins", :sign_in_count
= column :last_sign_in_at
This way I can change the formatting of the data in the columns and the column header names if I'm not happy with default values, but have the table generated for me.
I suppose I could create a normal helper, but I'd have to use arrays and I have no idea how I could include custom data formatting per column.
active_admin has something similar to this which you can see here: http://activeadmin.info/docs/3-index-pages/index-as-table.html
Any leads or ideas would be greatly appreciated.
I just came up with this:
A few points:
The line #columns = [] is a reset so you can call it more than once.
The yield in the custom_table_for calls the block that you pass it.
The block in the column method is stored and called in custom_table_for if it is set.
I included a sample class to show the usage too.
please note I did this outside of a rails app and you almost certainly want to use http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag instead of the p "<table>" this is merely for sample purposes when you run it in the console.
module TableHelper
def custom_table_for(items)
#columns = []
yield
p "<table>"
#columns.each do |c|
p "<th>#{c[:value]}</th>"
end
items.each do |e|
p "<tr>"
#columns.each do |c|
e[c[:name]] = c[:block].call(e[c[:name]]) if c[:block]
p "<td>#{e[c[:name]]}</td>"
end
p "</tr>"
end
p "</table>"
end
def column(name, value = nil, &block)
value = name unless value
#columns << {:name => name, :value => value, :block => block}
end
end
class ExampleTable
include TableHelper
def test
#users = [{:email => "Email 1", :username => "Test User"}, {:email => "Email 2", :username => "Test User 2"}]
custom_table_for #users do
column :email, "Email"
column :username do |user|
user.upcase
end
end
end
end
et = ExampleTable.new
et.test
UPDATE
I migrated this to rails to use content_tags
module TableHelper
def custom_table_for(items)
#columns = []
yield
content_tag :table do
thead + tbody(items)
end
end
def thead
content_tag :thead do
content_tag :tr do
#columns.each do |c|
concat(content_tag(:th, c[:value]))
end
end
end
end
def tbody(items)
content_tag :tbody do
items.each { |e|
concat(content_tag(:tr){
#columns.each { |c|
e[c[:name]] = c[:block].call(e[c[:name]]) if c[:block]
concat(content_tag(:td, e[c[:name]]))
}
})
}
end
end
def column(name, value = nil, &block)
value = name unless value
#columns << {:name => name, :value => value, :block => block}
end
end
To compliment #gazler's response, here's a way to make a table of a single resource-- column one for attribute names, column two for their values:
module TableHelper
#resource = nil
def simple_table_for(resource)
#resource = resource
content_tag :table do
content_tag :tbody do
yield
end
end
end
def row(key, label = nil, &block)
if key.is_a? String
label = key
end
content_tag(:tr) {
concat content_tag :td, label || key.capitalize
concat content_tag(:td ){
if block_given?
yield
else
#resource.send(key)
end
}
}
end
end

How can I inizialize that ActiveRecord Tableless Model?

I am using Ruby on Rails 3 and I would like to inizialize an ActiveRecord Tableless Model.
In my model I have:
class Account < ActiveRecord::Base
# The following ActiveRecord Tableless Model statement is from http://codetunes.com/2008/07/20/tableless-models-in-rails/
def self.columns()
#columns ||= [];
end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
attr_reader :id,
:firstname,
:lastname,
def initialize(attributes = {})
#id = attributes[:id]
#firstname = attributes[:firstname]
#lastname = attributes[:lastname]
end
end
If in a controller, for example in the application_controller.rb file, I do:
#new_account = Account.new({:id => "1", :firstname => "Test name", :lastname => "Test lastname"})
a debug\inspect output of the #new_account variable is
"#<Account >"
Why? How I should inizialize properly that ActiveRecord Tableless Model and make it to work?
According to that blog post it would have to look like this:
class Account < ActiveRecord::Base
class_inheritable_accessor :columns
def self.columns()
#columns ||= [];
end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
column :id, :integer
column :firstname, :string
column :lastname, :string
end
And then:
#new_account = Account.new({:id => "1", :firstname => "Test name", :lastname => "Test lastname"})
Did you already try it like that?
I my view, you don't need to extend ActiveRecord::Base class.
You can write your own model class something like this
# models/letter.rb
class Letter
attr_reader :char
def self.all
('A'..'Z').map { |c| new(c) }
end
def self.find(param)
all.detect { |l| l.to_param == param } || raise(ActiveRecord::RecordNotFound)
end
def initialize(char)
#char = char
end
def to_param
#char.downcase
end
def products
Product.find(:all, :conditions => ["name LIKE ?", #char + '%'], :order => "name")
end
end
# letters_controller.rb
def index
#letters = Letter.all
end
def show
#letter = Letter.find(params[:id])
end
I hope it will help you.
Reference: http://railscasts.com/episodes/121-non-active-record-model

Resources