I am using Ruby on Rails 3.0.7 and I would like to know how to initialize\build "custom" data structures responding to the where method as like it works, for example, for common RoR AssociationCollection objects.
For example:
# The following code should work after build the 'test_data' as well...
# but how to build that?
test_data.where(:test_attribute => 'test_value')
I'm not entirely clear on what you're after, but you could create a wrapper around (for example) an array of hashes that used where to do searching.
class Search
def initialize(data)
#data = data
end
def where(filters={})
#data.select do |item|
filters.all?{|key, value| item[key] == value }
end
end
end
data = [
{ :name => 'Sam', :age => 27, :gender => 'M' },
{ :name => 'Sue', :age => 27, :gender => 'F' },
{ :name => 'Bob', :age => 32, :gender => 'M' }
]
search = Search.new(data)
search.where(:age => 27) # returns array containing Sam and Sue hashes
search.where(:gender => 'M') # returns array containing Sam and Bob hashes
search.where(:age => 27, :gender => 'M') # returns array containing just Sam
Related
I need to get all attributes of an object. I know there's a method attributes, but it doesn't return attributes which are nil.
For example:
class User
include Mongoid::Document
field :name
field :email
field :age
end
u = User.new(email: 'foo#bar.com', name: 'foo')
u.save
u.attributes # {'email' => 'foo#bar.com', 'name' => 'foo'}
I need u.attributes to return {'email' => 'foo#bar.com', 'name' => 'foo' 'age' => nil}
There's a method as_json which does what I want, but it's a lot slower. Speed is very important.
I found a quick solution
self.attribute_names.map { |name| [name, self[name]] }.to_h
It does all I want =)
Is there a way to compare two instances of model like
Model.compare_by_name("model1", "model2") which would list the differing column fields
You can use ActiveRecord::Diff if you want a mapping of all the fields that differ and their values.
alice = User.create(:name => 'alice', :email_address => 'alice#example.org')
bob = User.create(:name => 'bob', :email_address => 'bob#example.org')
alice.diff?(bob) # => true
alice.diff(bob) # => {:name => ['alice', 'bob'], :email_address => ['alice#example.org', 'bob#example.org']}
alice.diff({:name => 'eve'}) # => {:name => ['alice', 'eve']}
There is no standard comparator for this. The standard ActiveModel comparator:
Returns true if comparison_object is the same exact object, or comparison_object is of the same type and self has an ID and it is equal to comparison_object.id.
You can write your own by using Hash#diff from activesupport. Something like the following should hopefully get you started:
def Model.compare_by_name(model1, model2)
find_by_name(model1).attributes.diff(find_by_name(model2).attributes)
end
Without using a library or defining a custom method, you can easily get a diff between two models.
For instance,
a = Foo.first
b = Foo.second
a.attributes = b.attributes
a.changes #=> {"id" => [1,2] }
I have a class Sample
Sample.class returns
(id :integer, name :String, date :date)
and A hash has all the given attributes as its keys.
Then how can I initialize a variable of Sample without assigning each attribute independently.
Something like
Sample x = Sample.new
x.(attr) = Hash[attr]
How can I iterate through the attributes, the problem is Hash contains keys which are not part of the class attributes too
class Sample
attr_accessor :id, :name, :date
end
h = {:id => 1, :name => 'foo', :date => 'today', :extra1 => '', :extra2 => ''}
init_hash = h.select{|k,v| Sample.method_defined? "#{k}=" }
# This will work
s = Sample.new
init_hash.each{|k,v| s.send("#{k}=", v)}
# This may work if constructor takes a hash of attributes
s = Sample.new(init_hash)
Take a look at this article on Object initialization. You want an initialize method.
EDIT You might also take a look at this SO post on setting instance variables, which I think is exactly what you're trying to do.
Try this:
class A
attr_accessor :x, :y, :z
end
a = A.new
my_hash = {:x => 1, :y => 2, :z => 3, :nono => 5}
If you do not have the list of attributes that can be assigned from the hash, you can do this:
my_attributes = (a.methods & my_hash.keys)
Use a.instance_variable_set(:#x = 1) syntax to assign values:
my_attributes.each do |attr|
a.instance_variable_set("##{attr.to_s}".to_sym, my_hash[attr])
end
Note(Thanks to Abe): This assumes that either all attributes to be updated have getters and setters, or that any attribute which has getter only, does not have a key in my_hash.
Good luck!
How can I insert multiple records into a database using rails syntax.
INSERT INTO users (email,name) VALUES ('a#ao.in','a'),('b#ao.in','b'),
('c#ao.in','c');
This is how we do it in MySQL. How is this done in Rails?
Check out this blog post: http://www.igvita.com/2007/07/11/efficient-updates-data-import-in-rails/
widgets = [ Widget.new(:title => 'gizmo', :price => 5),
Widget.new(:title => 'super-gizmo', :price => 10)]
Widget.import widgets
Depending on your version of rails, use activerecord-import 0.2.6 (for Rails 3) and ar-extensions 0.9.4 (for Rails 2)
From the author: http://www.continuousthinking.com/tags/arext
While you cannot get the exact SQL that you have there, you can insert multiple records by passing create or new on an array of hashes:
new_records = [
{:column => 'value', :column2 => 'value'},
{:column => 'value', :column2 => 'value'}
]
MyModel.create(new_records)
I use following in my project but it is not proper for sql injection.
if you are not using user input in this query it may work for you
user_string = " ('a#ao.in','a'), ('b#ao.in','b')"
User.connection.insert("INSERT INTO users (email, name) VALUES"+user_string)
Just a use activerecord-import gem for rails 3 or ar-extensions for rails 2
https://github.com/zdennis/activerecord-import/wiki
In Gemfile:
gem "activerecord-import"
In model:
import "activerecord-import"
In controller:
books = []
10.times do |i|
books << Book.new(:name => "book #{i}")
end
Book.import books
This code import 10 records by one query ;)
or
##messages = ActiveSupport::JSON.decode(#content)
#messages = JSON(#content)
#prepare data for insert by one insert
fields = [:field1, :field2]
items = []
#messages.each do |m|
items << [m["field1"], m["field2"]]
end
Message.import fields, items
You can use Fast Seeder to do multiple insert.
In People_controller.rb
# POST people
NAMES = ["Sokly","Nary","Mealea"]
def create
Person.transaction do
NAMES.each do |name|
#name = Person.create(:name => name)
#name.save
end
end
end
Just pass an array of hashs to the create method like this:
User.create([{:email => "foo#com", :name => "foo"}, {:email => "bar#com", :name => "bar"}])
Here's some of my production code (I had to force line breaks):
task = Task.find_or_create_by_username_and_timestamp_and_des \
cription_and_driver_spec_and_driver_spec_origin(username,tim \
estamp,description,driver_spec,driver_spec_origin)
Yes, I'm trying to find or create a unique ActiveRecord::Base object. But in current form it's very ugly. Instead, I'd like to use something like this:
task = Task.SOME_METHOD :username => username, :timestamp => timestamp ...
I know about find_by_something key=>value, but it's not an option here. I need all values to be unique. Is there a method that'll do the same as find_or_create_by, but take a hash as an input? Or something else with similat semantics?
Rails 3.2 first introduced first_or_create to ActiveRecord. Not only does it have the requested functionality, but it also fits in the rest of the ActiveRecord relations:
Task.where(attributes).first_or_create
In Rails 3.0 and 3.1:
Task.where(attributes).first || Task.create(attributes)
In Rails 2.1 - 2.3:
Task.first(:conditions => attributes) || Task.create(attributes)
In the older versions, you could always write a method called find_or_create to encapsulate this if you'd like. Definitely done it myself in the past:
class Task
def self.find_or_create(attributes)
# add one of the implementations above
end
end
I also extend the #wuputah's method to take in an array of hashes, which is very useful when used inside db/seeds.rb
class ActiveRecord::Base
def self.find_or_create(attributes)
if attributes.is_a?(Array)
attributes.each do |attr|
self.find_or_create(attr)
end
else
self.first(:conditions => attributes) || self.create(attributes)
end
end
end
# Example
Country.find_or_create({:name => 'Aland Islands', :iso_code => 'AX'})
# take array of hashes
Country.find_or_create([
{:name => 'Aland Islands', :iso_code => 'AX'},
{:name => 'Albania', :iso_code => 'AL'},
{:name => 'Algeria', :iso_code => 'DZ'}
])