DateTime and DataMapper - ruby

Problem
I'm essentially trying to save +4 hours from now in DM. I think the DateTime is correct, but the DataMapper doesn't work properly. Any ideas?
Controller
p DateTime.now
t = DateTime.now + params[:hours] / 24
p t
reward = #player.work params[:hours]
action = Action.new(:player => #player, :type => 0, :completed_at => t, :reward => reward)
model
class Action
include DataMapper::Resource
property :id, Serial
property :type, Integer
property :completed_at, DateTime
property :reward, Integer
TYPES = {
0 => "Work",
1 => "Arena",
2 => "Quest"
}
validates_with_method :type, :method => :valid_type?
def valid_type?
if TYPES.key? self.type.to_i
true
else
[false, "Invalid action type."]
end
end
def get_type
case TYPES[self.type]
when "Work"
"you're working"
when "Arena"
"in the arena"
when "Quest"
"in a quest"
end
end
belongs_to :player
end

try:
DateTime.now + (params[:hours] / 24.0)

Related

Passing hash to #new results in array of values instead of correct value

With this setup in minitest,
def test_id
i = Item.new({
:id => 1,
:name => "Pencil",
:description => "You can use it to write things",
:unit_price => BigDecimal.new(10.99,4),
:created_at => Time.now,
:updated_at => Time.now,
:merchant_id => 2
})
assert_equal 1, i.id
end
For some reason, when created, calling the id attribute results in an array of all of the values:
[1,'Pencil','You can use it to write things',#<BigDecimal...>, 2018-07-24 14:43:36 -0600, 2018-07-24 14:43:36 -0600, 2]
instead of the integer 1.
In the item file, it looks like what you would expect
require 'bigdecimal'
require 'time'
class Item
attr_reader :id, :created_at, :merchant_id
attr_accessor :name, :description, :unit_price, :updated_at
def initialize(item_data)
#id = item_data[:id].to_i,
#name = item_data[:name],
#description = item_data[:description],
#unit_price = BigDecimal.new(item_data[:unit_price], 4),
#created_at = item_data[:created_at],
#updated_at = item_data[:updated_at],
#merchant_id = item_data[:merchant_id].to_i
end
end
Not really sure how this is happening.
Throwing a pry in the test method before the assertion and calling i results in
#<Item:0x00007f8cc48eb4f0
#created_at=2018-07-24 15:14:55 -0600,
#description="You can use it to write things",
#id=[1, "Pencil", "You can use it to write things", #<BigDecimal:7f8cc48eb4c8,'0.1099E2',18(27)>, 2018-07-24 15:14:55 -0600, 2018-07-24 15:14:55 -0600, 2],
#merchant_id=2,
#name="Pencil",
#unit_price=#<BigDecimal:7f8cc48eb4c8,'0.1099E2',18(27)>,
#updated_at=2018-07-24 15:14:55 -0600>
in the terminal.
It's your trailing commas in the initializer:
def initialize(item_data)
#id = item_data[:id].to_i, # <=
#name = item_data[:name], # <=
What they do is make ruby see the method like this:
#id = [item_data[id].to_i, #name = item_data[:name], ...]
It seems that the problem are the commas that you are adding at the end of each setting variable. Check with this code:
require 'bigdecimal'
require 'time'
class Item
attr_reader :id, :created_at, :merchant_id
attr_accessor :name, :description, :unit_price, :updated_at
def initialize(item_data)
#id = item_data[:id].to_i
#name = item_data[:name]
#description = item_data[:description]
#unit_price = BigDecimal.new(item_data[:unit_price], 4)
#created_at = item_data[:created_at]
#updated_at = item_data[:updated_at]
#merchant_id = item_data[:merchant_id].to_i
end
end

Advanced count association broken after sequel upgrade

I created an association in order to reduce the number of queries in a construct like this
#user.all do |user|
puts "User ##{user.id}, post count: #{user.posts_count}"
end
My Model:
class User
one_to_many :posts
one_to_one :posts_count, :read_only => true, :key => :id,
:dataset => proc{Post.where(:user_id => :id, :deleted => false).select{count(Sequel.lit("*")).as(count)}},
:eager_loader => (proc do |eo|
eo[:rows].each{|p| p.associations[:posts_count] = nil}
Post.where(:user_id => eo[:id_map].keys, :deleted => false).
select_group(:user_id).
select_append{count(Sequel.lit("*")).as(count)}.
all do |t|
p = eo[:id_map][t.values.delete(:user_id)].first
p.associations[:posts_count] = t
end
end)
def posts_count
super.try(:[], :count) || 0
end
# ...
end
After upgrading to sequel 4.1.0 I get unitialized constant PostsCount, while in sequel 3.44 it worked fine. How can I fix this?
Adding a class attribute solves the issue. Include :class => "Post" like this:
one_to_one :posts_count, :read_only => true, :key => :id, :class => "Post",
#...

Get associated records with Datamapper using Sinatra

I am currently working on a small backend, managing events with associated locations. Unfortunately, it's my first time i work with Ruby/Sinatra/Datamapper. After 3 hours trying to find a solution, i have to write this post.
I have defined two Resources:
class Event
include DataMapper::Resource
property :id, Integer, :key => true
property :name, Text
property :description, Text
has 1, :location
end
class Location
include DataMapper::Resource
property :id, Integer, :key => true
property :name, Text
property :latitude, Float
property :longitude, Float
belongs_to :event
end
This is my route to list all events:
get "/events/" do
#events = Event.all
content_type :json
#events.to_json
end
Is there a easy way the get the location as a parameter in the output of the associated event object?
Thank you very much for your support!
require 'rubygems'
require 'sinatra'
require 'dm-core'
require 'dm-migrations'
require 'dm-sweatshop' # for fixtures
require 'json'
DataMapper::Logger.new($stdout, :debug)
DataMapper.setup(:default, 'sqlite::memory:')
class Event
include DataMapper::Resource
property :id, Serial # will automatically become an auto-increment key
property :name, String # defaults to being max 50 char length
property :description, Text, :lazy => false # defaults to true
belongs_to :location # instead of one-to-one relation, which are rarely useful
end
class Location
include DataMapper::Resource
property :id, Serial
property :name, String
property :latitude, Float # perhaps decimal with less precision would suffice
property :longitude, Float
has n, :events
end
DataMapper.finalize.auto_migrate!
# Define some fixtures to have some data to play around with
def rand_float(min, max); rand * (max - min) + min end
Location.fix {{
:name => /\w+/.gen,
:latitude => rand_float(40.0, 43.0),
:longitude => rand_float(4.8, 5.4)
}}
Event.fix {{
:name => /\w+/.gen,
:description => /[:sentence:]/.gen[5..100],
:location => Location.pick
}}
100.of { Location.gen; Event.gen }
# Search events by properties of its association
get "/events/:location_name" do |location_name|
#events = Event.all(Event.location.name => location_name)
#events.to_json
end
# Return both objects in the same array
get "/events/" do
#events = Event.map {|e| [e, e.location] }
#events.to_json
end
Finally found the answer by myself when i took a deeper look at the to_json options
#events.to_json(:relationships=>{:location=>{}})

Save callbacks not running after append

I'm having a problem trying to append a relationship using the << operator. If I save after the append, my callback does not seem to run.
I have two models:
Fabric
class Fabric
include DataMapper::Resource
property :id, Serial
property :name, String
property :fixed_color, Boolean
property :active, Boolean, :default => true
has 1, :cut
has n, :colors, :through => Resource
after :save, :add_to_fishbowl
def add_to_fishbowl
puts self.colors.size
end
end
Color
class Color
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :cut
has n, :fabrics, :through => Resource
end
I create two colors and a fabric:
yellow = Color.new(:name => "yellow")
red = Color.new(:name => "red")
f = Fabric.create(:name => "tricot", :fixed_color => false)
If I used the append operator, my callback is not run:
f.colors << red
f.save
f.colors << yellow
f.save
puts f.colors.size
=> 0
=> 2
If I add arrays, it is:
f.colors = f.colors + [red]
f.save
f.colors = f.colors + [yellow]
f.save
puts f.colors.size
=> 0
=> 1
=> 2
=> 2
I'm running ruby 1.9.3p392 and data_mapper (1.2.0).
You missing relations, has n, :cuts
class Color
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :cuts
has n, :fabrics, :through => Resource
end

Ruby / Datamapper create or update issue - immutable error

I'm have a user class that can optionally have a billing address. When I post a payment form, assuming the user has indicated they want to save their billing address details, I want to either create a new address record or update the original one.
I have tried many things but the closest I can get to working code is...
class User
include DataMapper::Resource
property :id, Serial
property :provider, String, :length => 100
property :identifier, String, :length => 100
property :username, String, :length => 100
property :remember_billing, Boolean
has 1, :billing_address
end
class BillingAddress
include DataMapper::Resource
property :first, String, :length => 20
property :surname, String, :length => 20
property :address1, String, :length => 50
property :address2, String, :length => 50
property :towncity, String, :length => 40
property :state, String, :length => 2
property :postcode, String, :length => 20
property :country, String, :length => 2
property :deleted_at, ParanoidDateTime
belongs_to :user, :key => true
end
post "/pay" do
#post = params[:post]
#addr = params[:addr]
if #addr == nil
#addr = Hash.new
end
user = User.first(:identifier => session["vya.user"])
user.remember_billing = !!#post["remember"]
if user.remember_billing
user.billing_address = BillingAddress.first_or_create({ :user => user }, #addr)
end
user.save
...
which works fine when there is no record. But if there is already a record, it keeps the original values.
I saw a similar post
DataMapper: Create new record or update existing
but if I alter the code to be
user.billing_address = BillingAddress.first_or_create(:user => user).update(#addr)
I get the error
DataMapper::ImmutableError at /pay
Immutable resource cannot be modified
Any help much appreciated
You're chaining lots of things together, there. How about:
billing = BillingAddress.first_or_new(:user => user, #addr) #don't update, send the hash as second parameter
billing.saved? ? billing.update(#addr) : billing.save
raise "Billing is not saved for some reason: #{billing.errors.inspect}" unless billing && billing.saved?
user.billing_address = billing
user.save

Resources