I have an object that looks like the below:
class Report
attr_accessor :weekly_stats, :report_times
def initialize
#weekly_stats = Hash.new {|h, k| h[k]={}}
#report_times = Hash.new {|h, k| h[k]={}}
values = []
end
end
I want to loop through the weekly_stats and report_times and upcase each key and assign it its value.
Right now I have this:
report.weekly_stats.map do |attribute_name, value|
report.values <<
{
:name => attribute_name.upcase,
:content => value ||= "Not Currently Available"
}
end
report.report_times.map do |attribute_name, value|
report.values <<
{
:name => attribute_name.upcase,
:content => format_date(value)
}
end
report.values
Is there a way I could map both the weekly stats and report times in one loop?
Thanks
(#report_times.keys + #weekly_stats.keys).map do |attribute_name|
{
:name => attribute_name.upcase,
:content => #report_times[attribute_name] ? format_date(#report_times[attribute_name]) : #weekly_stats[attribute_name] || "Not Currently Available"
}
end
If you are guaranteed nil or empty string in weekly_stats, and a date object in report_times, then you could use this information to work through a merged hash:
merged = report.report_times.merge( report.weekly_stats )
report.values = merged.map do |attribute_name, value|
{
:name => attribute_name.upcase,
:content => value.is_a?(Date) ? format_date(value) : ( value || "Not Currently Available")
}
end
Related
I am really new to Ruby and Rails and need to know how to check if a string contains a dash before applying titlelize.
#city = City.first :conditions => { :title => params[:city].titleize }
What I need to do is:
#city = City.first :conditions => { :title => params[:city] }
and then write something that will apply titleize ONLY if the #city variable doesn't contain a dash.
I like this solution added by zachrose a couple of weeks ago: https://gist.github.com/varyonic/ccda540c417a6bd49aec
def nice_title(phrase)
return phrase if phrase =~ /^-+$/
phrase.split('-').map { |part|
if part.chars.count == part.bytes.count
part.titleize
else
part.split(' ').map { |word| word.mb_chars.titleize }.join(' ')
end
}.join('-')
end
if params[:city] =~ /-/
#city = City.first :conditions => { :title => params[:city] }
else
#city = City.first :conditions => { :title => params[:city].titleize }
end
I do not know why you are using this, but I believe it will not work for all cases. There should be a better approach.
I am using Ruby on Rails 3.2.2. I have the following scenario:
# hash_params.class
# => Hash
# hash_params.inspect
# => { :key1 => :value1, :key2 => value2, ... => ... }
#
def self.method_1(hash_params)
hash_params.each do { |hash_param| self.method_2(hash_param) }
end
# param.class
# => Array
# param.inspect
# => [:key1, value1] # or '[:key2, value2]' or '[..., ...]', depending on cases.
#
def self.method_2(param)
logger.debug "Key => #{param[0])"
logger.debug "Value => #{param[1])"
end
Given outputs commented out in the above code, when I run the method_1 then in the logger file I have the following:
Key => :key1
Value => :value1
Key => :key2
Value => :value2
Key => ...
Value => ...
I would like to treat the param variable in method_2 as-like a key / value pair (not as an Array), for example by making something like the following
def self.method_2(param)
param do |key, value| # Note: This code line doesn't work. It is just a sample code to clarify the question.
logger.debug "Key => #{key.inspect)"
logger.debug "Value => #{value.inspect)"
end
end
? Is it possible? If so, how? What do you advice about?
Use Hash[]:
param = [:key1, 'value1']
h = Hash[*param]
puts h[:key1]
Output:
value1
How about
def self.method_1(hash_params)
hash_params.each do { |key, value| self.method_2(key, value) }
end
def self.method_2(key, value)
logger.debug "Key => #{key)"
logger.debug "Value => #{value)"
end
Otherwise you can still pass a hash in param like
def self.method_1(hash_params)
hash_params.keys.each do { |key| self.method_2(hash_params.slice(key)) }
end
edit: if you want a hash as parameter you could just do
def self.method_1(hash_params)
hash_params.each do { |key, value| self.method_2({key => value}) }
end
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"
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
I have a model with 30 attributes. but those attributes can be grouped in 2 groups.
For example I have:
string:title
string:text
...
and
string:title_old
string:text_old
...
I want to be able: When I check title attribute at the same time to check the title_old attribute. Can I perform that with a loop if I make an array of the 15 first strings or I should write hard coded if statements
Final goal:
[
{
:name => :title,
:y => 1 (constant),
:color=> red, (if title_old == "something" color = red else color = green)
},
{
:name=> :text,
:y => 1 (constant)
:color => red (if text_old == "something" color = red else color = green)
},
.... (all other 13 attributes)
]
your model:
class MyModel < AR::Base
def attributize
attrs = self.attributes.except(:created_at, :updated_at).reject{ |attr, val| attr =~ /.*_old/ && !val }
attrs.inject([]) do |arr, (attr, val)|
arr << { :name => attr, :y => 1, :color => (self.send("#{attr}_old") == "something" ? "red" : "green") }
end
end
end
usage:
my_object = MyModel.last
my_object.attributize
Very simple example:
class MyModel
def identify_color
if send("#{name}_old".to_sym) == "something"
'red'
else
'green'
end
end
end
MyModel.all.collect do |instance|
attrs = instance.attributes
attrs.merge!('color' => identify_color)
attrs
end
Add some rescue at will, but it can be done in different ways.
Try this:
[
:title,
..,
..
:description
].map do |attr|
{
:name => attr,
:y => 1 (constant),
:color=> (read_attribute("#{attr}_old") == "something") ? "red" : "green"
}
end
PS: Naming an attribute text is a bad idea.
use state_machine, that way your logic will be in one place with a clear dsl. https://github.com/pluginaweek/state_machine