Finding relevant attributes in a model - ruby-on-rails

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

Related

Ruby, Map, Object attributes

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

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

Cloning a model in Factory Girl?

I want to use Factory Girl to generate a large collection of models, each of which only differ by one or two attributes.
Is there a way to have a factory accept an instance of a model? Ideally, I'd like
before(:all) do
data1 = create(:instance,
:attribute_1 => 1,
:attribute_2 => 2,
:attribute_3 => "something",
:attribute_4 => "something else",
:attribute_5 => 5
...
)
data2 = create(:instance,
:attribute 2 => 15,
base: data1
)
end
data2 would be initialized as a clone of data1, and I could just specify the new attributes I wanted to overwrite.
I've tried using transient attributes but can't see a way to implement something like:
FactoryGirl.define do
factory :instance do
ignore do
base nil
end
attribute_1 { base.nil? ? argument.attribute_1 : base.attribute_1 + argument.attribute_1 }
attribute_2 { base.nil? ? argument.attribute_2 : base.attribute_2 + argument.attribute_1 }
...
end
end
Am I'm approaching this in entirely the wrong way?
Could you do it more simply by just defining the hash of attributes you want to use and then changing it slightly for the second call?
Something like:
data_attributes = {
:attribute_1 => 1,
:attribute_2 => 2,
:attribute_3 => "something",
:attribute_4 => "something else",
:attribute_5 => 5
...
}
data1 = create(:instance, data_attributes)
data2 = create(:instance, data_attributes.merge(:attribute_2 => 1))
I appreciate that that doesn't exactly answer your question but it might solve your problem.
If you're just trying to generate attributes that differ, have you looked at sequences?
FactoryGirl.define do
sequence :foo { |n| "#{n}" }
factory :data { foo }
end
In your spec:
data1 = FactoryGirl.create(:data)
data1.foo
=> "1"
data2 = FactoryGirl.create(:data) #It makes new, unique attributes for you
data2.foo
=> "2"

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"

Where to put constants in Rails

I have a few constants which are arrays that I don't want to create databse records for but I don't know where to store the constants without getting errors.
For example
CONTAINER_SIZES = [["20 foot"],["40 foot"]]
Where can I store this so all models and controller have access to this?
I will write my way to you.
class User < ActiveRecord::Base
STATES = {
:active => {:id => 100, :name => "active", :label => "Active User"},
:passive => {:id => 110, :name => "passive", :label => "Passive User"},
:deleted => {:id => 120, :name => "deleted", :label => "Deleted User"}
}
# and methods for calling states of user
def self.find_state(value)
if value.class == Fixnum
Post::STATES.collect { |key, state|
return state if state.inspect.index(value.to_s)
}
elsif value.class == Symbol
Post::STATES[value]
end
end
end
so i can call it like
User.find_state(:active)[:id]
or
User.find_state(#user.state_id)[:label]
Also if i want to load all states to a select box and if i don't want some states in it (like deleted state)
def self.states(arg = nil)
states = Post::STATES
states.delete(:deleted)
states.collect { |key, state|
if arg.nil?
state
else
state[arg]
end
}
end
And i can use it now like
select_tag 'state_id', User.states.collect { |s| [s[:label], s[:id]] }
I put them directly in the model class.
class User < ActiveRecord::Base
USER_STATUS_ACTIVE = "ACT"
USER_TYPES = ["MANAGER","DEVELOPER"]
end

Resources