I'm trying to retrieve data in a nested form from the following two tables (in SQLite)
DB = Sequel.sqlite('database.sqlite')
DB.create_table? :artists do
primary_key :id
String :name
end
DB.create_table? :albums do
primary_key :id
String :title
foreign_key :artist_id,
:artists,
:key => :id
end
artists = DB[:artists]
albums = DB[:albums]
id1 = artists.insert(:name => 'Mike')
id2 = artists.insert(:name => 'John')
albums.insert(:title => 'Only You', :artist_id => id1 )
albums.insert(:title => 'Only Us', :artist_id => id1 )
albums.insert(:title => 'Only Me', :artist_id => id2 )
The output I'm trying to get -
[
{
:id => 1,
:name => 'Mike'
:albums => [
{
:id => 1,
:title => 'Only You'
},
{
:id => 2,
:title => 'Only Us'
}
]
},
{
:id => 2,
:name => 'John'
:albums => [
{
:id => 3,
:title => 'Only Me'
}
]
}
]
I've tried 'eager' loading -
class Artist < Sequel::Model(:artists)
one_to_many :db[:albums], :key => :artist_id
end
class Album < Sequel::Model(:albums)
many_to_one :artist, :key => :artist_id
end
Artist.eager(:albums).all{ |a| p a }
But that didn't work.
Can anyone point me in the right direction?
Artist.eager(:albums).all does eagerly load the albums, but {|a| p a} is not going to show the albums (as Sequel::Model#inspect only shows values for the current model, not any associated objects). Use {|a| p [a, a.albums]} to see that the albums are already loaded.
If you want to produce the hash you described:
Artist.eager(:albums).all.map do |a|
a.values.merge(:albums=>a.albums.map{|al| al.values})
end
You can add a method to Artist to output it the way you want it
class Artist < Sequel::Model(:artists)
one_to_many :albums, :key => :artist_id
def my_hash
to_hash.merge!(
{
:albums => albums.map{|a|
a.to_hash.reject!{ |k,v|
k==:artist_id
}
}
}
)
end
end
class Album < Sequel::Model(:albums)
many_to_one :artist, :key => :artist_id
end
records = Artist.all.map{ |a| a.my_hash }
p records
Instead of using reject! it would be cleaner to add a my_hash method the Album to return a hash without the :artist_id, but you get the idea. This outputs:
[
{
:albums=>[
{
:title=>"Only You",
:id=>1
},
{
:title=>"Only Us",
:id=>2
}
],
:name=>"Mike",
:id=>1
},
{
:albums=>[
{
:title=>"Only Me",
:id=>3
}
],
:name=>"John",
:id=>2
}
]
Related
I have been trying for ages. I am trying to round an array with hashes.
[
{
:email => "user_01#yorlook.com",
:amount => 129.22500000000002
},
{
:email => "user_02#yorlook.com",
:amount => 112.67500000000001
}
]
I need a method to make it look like this:
[
{
:email => "user_01#yorlook.com",
:amount => 129.23
},
{
:email => "user_02#yorlook.com",
:amount => 112.68
}
]
Thanks for your help in advance!
Assuming arr always contains Hash with key amount which is a Float (as in example).
arr.each { |hash|
hash[:amount] = hash[:amount].round(2)
}
#=> [{:email=>"user_01#yorlook.com", :amount=>129.23}, {:email=>"user_02#yorlook.com", :amount=>112.68}]
get '/watch/:id' do |id|
erb :mystream
#result = Twitchtvst.all( :fields => [:Twitchtv ],
:conditions => { :user_id => "#{id}" }
)
puts #result
end
result in terminal;
#< Twitchtvst:0x007fb48b4d5a98 >
How do I get that into a string (TwitchTV answer in database)
How do I go about creating a new class and adding a few methods to it without resorting to "eval"?
Here's what I'm trying to do; I'd like to transform this structure:
obj = [
{
:scope => 'account',
:fields => [
{ :title => 'title', :length => 64, :required => true },
{ :title => 'email', :length => 256, :required => true, :type => 'email' }
],
:before_save => Proc.new{
#do something here
},
},
{
:scope => 'product',
:fields => [
{ :title => 'title', :length => 64, :required => true },
{ :title => 'description', :length => 256, :required => true },
{ :title => 'cost', :required => true, :type => 'decimal' }
]
},
]
into this:
class Account
include DataMapper::Resource
property :id, Serial
property :title, String, :length => 64, :required => true
property :email, String, :length => 256, :required => true
def before_save
#do something here
end
end
...
Thanks!
As Andrew already said, there are different ways to create class dynamically. This could be one of them:
Say you start with one DM model (haven't used DM, taking the first one from the docs):
class Post
include DataMapper::Resource
property :id, Serial # An auto-increment integer key
property :title, String # A varchar type string, for short strings
property :body, Text # A text block, for longer string data.
property :created_at, DateTime # A DateTime, for any date you might like.
end
and you want to create it dynamically, from a metadata given in a hash of the form
{:ClassName => {:field1 => :Type1, :field2 => :Type2 ...}}
You could do:
require 'data_mapper'
models = {:Post => {
:id => :Serial,
:title => :String,
:body => :Text
}}
models.each do |name, fields|
klass = Class.new do
include DataMapper::Resource
fields.each do |field_name, field_type|
property(field_name, const_get(field_type))
end
end
Object.const_set(name, klass)
end
Key methods:
Class.new
Module#const_set
If you want to look at a real-world example, please consult the code in this library: https://github.com/apohllo/rod/blob/v0.7.x/lib/rod/model.rb#L410
I'm playing around with nested hashes and I'm trying to figure out how to fetch multiple keys when my hash is a nested one:
imahash = { :id => { :name => "Alma", :email => "alma#mail.com" },
:stats => { :gender => "Female" },
:location => { :city => "Freeport", :state => "Maine" }
}
I know how to retrieve the nested value, and typing in the hash name will dump all the keys and values. But what I want to do is to fetch specific keys, such as :name and :gender only. Or :name and :city only.
Is this possible? Because from what I've found, it seems that you can only retrieve hash values for one key at a time or for all the keys at once.
My desired output would be something like:
=> { :id => { :name => "Alma" }, :location => { :city => "Freeport" } }
I presume you want to grab the values out in a tuple? You can make an array that contains whatever collection of values you want.
Try the following for name and city:
[imahash[:id][:name], imahash[:location][:city]]
=> ["Alma", "Freeport"]
Not exactly sure what you're asking here, but it seems like you're wanting to create a new hash from the bigger one.
To fetch specific keys like :name and :gender only
name_and_gender_hash = {
:name => imahash[:id][:name],
:gender => imahash[:stats][:gender]
}
would result in
{:name => "Alma", :gender => "female"}
Normally I have:
cookies[:location] = { :value => { :city => 'foo', :country => 'bar' } }
However, sometimes :country is not set. When I do:
cookies[:location][:country].present?
to check if country has been set, it returns an error:
[:country] is not a symbol
How would I check to see if a country is set in cookie[:location] if I cannot do it this way?
You cookies hash is nested differently than you are expecting: :value is nested under :location, :city and :country are nested under :value. Here is some irb output to get you started:
[~]$ irb
>> cookies={}
=> {}
>> cookies[:location] = { :value => { :city => 'foo', :country => 'bar' } }
=> {:value=>{:city=>"foo", :country=>"bar"}}
>> cookies[:location][:value][:country]
=> "bar"
>> cookies.to_s
=> "{:location=>{:value=>{:city=>\"foo\", :country=>\"bar\"}}}"
If you nest the hash like below, you will get your expected behavior:
>> cookies[:location] = { :city => 'foo', :country => 'bar' }
=> {:city=>"foo", :country=>"bar"}
>> cookies[:location][:country]
=> "bar"