Ruby DataMapper not saving property set in before hook - ruby

class Order
include Datamapper::Resource
property :birthday_day, String
property :birthday_month, String
property :birthday_year, String
property :birthday, Date
before :save do
#birthday = Date.new(#birthday_year.to_i, #birthday_month.to_i, #birthday_day.to_i)
end
end
It's a part of model, but it's enouth.
When save field (from irb or from sinatra application) :birthday not save in DB. But in irb, i see object, where :birthday exist and it Date format.
When change field manual (from irb):
f.birthday = Date.new
f.save
In object and in DB result appear (in obj as Date obj, in DB as "2010-2-3")
Help me please to understand, what wrong with before in model.
Sorry for my not good enlish.

You should use property mutator method. Setting the ivar doesn't trigger dirty tracking. Just do this:
self.birthday = Date.new(birthday_year.to_i, birthday_month.to_i, birthday_day.to_i)
Also it would be better to use Integer as the property type for year, month and day.

DataMapper documentation recommends #attribute_set
Sets the value of the attribute and marks the attribute as dirty if it has been changed so that it may be saved. Do not set from instance variables directly, but use this method.
In your case:
before :save do
set_attribute(:birthday => Date.new(self.birthday_year.to_i, self.birthday_month.to_i, self.birthday_day.to_i))
end
For what it's worth, unless I needed to use all the fields in SELECT criteria, I would save either the integer fields or the date, not both:
class Order
include Datamapper::Resource
property :birthday, Date
end
# change month
o = Order.create(:birthday => Date.new(...))
o.update(:birthday => Date.new(o.birthday.year, new_month, o.birthday.mday))
Or
class Order
include Datamapper::Resource
property :birthday_day, String
property :birthday_month, String
property :birthday_year, String
def birthday
if self.birthday_day && self.birthday_month && self.birthday_year
Date.new(self.birthday_day, ...)
end
end
end

Related

Ruby Datamapper: retrieving record using param in url path returns null - sometimes

I'm creating a Sinatra App using Datamapper.
With the following route, I'm attempting to print the record for an id. So localhost:9292/api/1 should return results for id=1
inside
get '/api/:id' do
I tried a couple things with varied results:
thing = Thing.get(params[:id])
thing.to_json
end
outputs 'null', but:
id_param = params[:id]
id_param
end
prints 1 as expected, and:
hardcoded_thing = Thing.get(1)
hardcoded_thing.to_json
end
correctly prints the hardcoded db record with id=1. So I must be losing it..
Any ideas?
Thanks!
For reference, here's my model:
class Thing
include DataMapper::Resource
include BCrypt
property :id, Serial, :key => true
property :created_at, DateTime
property :updated_at, DateTime
property :name, String, :length => 50
property :cafe_topic, Text
end
Try this:
get '/api/:id' do |id|
thing = Thing.get(id)
thing.to_json
end

Ruby: What is it declare in a class?

I got this sample code from datamapper http://datamapper.org/getting-started.html
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
Can anyone tell me that how "property" generate? Is it a function, variable, class variable or instance variable or a constant?
sometime i also saw this kind of code
class CarModel
attribute :name
attribute :hello
end
but no idea how does this generate
It is a method that is included when you do:
include DataMapper::Resource
You can see its source code here if you're interested in digging in deeper.
It basically adds a property to the list of properties in your Post resource.

DataMapper does not see a property assigned with #

I'm getting started with Ruby and DataMapper and I stumbled upon a problem which I think does not make any sense. Let's say I have the following model :
class Foo
include DataMapper::Resource
property :id, Serial
property :date, Date, required: true
def initialize
#date = Date.today
end
end
I open up IRB to test my models, set up the database connection, and try to save a new foo:
> foo = Foo.new
> foo.date
=> #<Date: 2013-03-28 ((2456380j,0s,0n),+0s,2299161j)>
> foo.save
Then I get the following exception :
DataObjects::IntegrityError: foos.date may not be NULL
And that perfectly makes sense, because I marked the date as required. But the date is there! I assigned it in the constructor of the class! And if I don't initialize it with today's date and I try to save, I only get a validation error. No exception.
What I don't understand (and this is what I want you to answer to) is that if I replace
#date = Date.today
with
self.date = Date.today
it works! foo is saved correctly. Why is that? Is it a bug inside DataMapper?
After bringing up the issue with DataMapper's people, I've been told that this is by design. For dirty tracking to work, you must absolutely use the attribute writer and not set the instance variable directly.

Freeze a property in DataMapper Model

Consider I have this following model definition, I want a particular property which should be constant from the moment it has been created
class A
property :a1, String, :freeze => true
end
Is there something like this? or may be using callbacks ?
Try the following:
class YourModel
property :a1, String
def a1=(other)
if a1
raise "A1 is allready bound to a value"
end
attribute_set(:a1, other.dup.freeze)
end
end
The initializer internally delegates to normal attribute writers, so when you initialize the attribute via YourModel.new(:a1 => "Your value") you cannot change it with your_instance.a1 = "your value".. But when you create a fresh instance. instance = YourModel.new you can assign once instance.a1 = "Your Value".
If you don't need to assign the constant, then
property :a1, String, :writer => :private
before :create do
attribute_set :a1, 'some value available at creation time'
end
may suffice

How to get a (Ruby) DataMapper custom type to work?

I have a SchoolDay class that represents a school day: it can tell you the date, the semester, the term, the week, and the day. It can generate a string like "Sem1 13A Fri". To store these objects in the database, I want them serialized as a string.
Here is my DataMapper custom type code. I've sort of scraped ideas from the code in dm-types because (disappointingly) there is no real documentation for creating custom types. Sorry it's long.
module DataMapper
class Property
class SchoolDay < DataMapper::Property::String
#load_as ::SchoolRecord::DomainObjects::SchoolDay
# Commented out: the 'load_as' method is not found
def load(value)
# Take a string from the database and load it. We need a calendar!
val = case value
when ::String then calendar.schoolday(value)
when ::SR::DO::SchoolDay then value
else
# fail
end
end
def dump(value)
# Store a SchoolDay value into the database as a string.
case value
when SR::DO::SchoolDay
sd = value
"Sem#{sd.semester} #{sd.weekstr} #{sd.day}"
when ::String
value
else
# fail
end
end
def typecast(value)
# I don't know what this is supposed to do -- that is, when and why it
# is called -- but I am aping the behaviour of the Regexp custom type,
# which, like this one, stores as a String and loads as something else.
load(value)
end
# private methods calendar() and error_message() omitted
end
end
end
This code works for reading from the (SQLite) database, but not for creating new rows. The error message is:
Schoolday must be of type String
The code that defines the DataMapper resource and tries to create the record is:
class LessonDescription
include DataMapper::Resource
property :id, Serial
property :schoolday, SchoolDay # "Sem1 3A Fri"
property :class_label, String # "10"
property :period, Integer # (0..6), 0 being before school
property :description, Text # "Completed yesterday's worksheet. hw:(4-07)"
end
# ...
ld = LessonDescription.create(
schoolday: #schoolday,
class_label: #class_label,
period: #period,
description: description
)
Here is the code for the Regexp datamapper type in the dm-types library. It's so simple!
module DataMapper
class Property
class Regexp < String
load_as ::Regexp # NOTE THIS LINE
def load(value)
::Regexp.new(value) unless value.nil?
end
def dump(value)
value.source unless value.nil?
end
def typecast(value)
load(value)
end
end
end
end
For some reason, I cannot use the load_as line in my code.
To summarise: I am trying to create a custom type that translates between a SchoolDay (domain object) and a String (database representation). The translation is easy, and I've copied the code structure primarily from the DataMapper Regexp type. But when I try to save a SchoolDay, it complains that I'm not giving it a string. Frustratingly, I can't use the "load_as" method that the built-in and custom types all use, even though I have the latest gem. I can't find the "load_as" method defined anywhere in the source code for DataMapper, either. But it's called!
Sorry for the ridiculous length. Any help would be greatly appreciated, as would a pointer to a guide for creating these things that I have somehow missed.
It seems that the current code of dm-types at github hasn't made it to any official release -- that's why load_as doesn't work in your example. But try to add this method:
module DataMapper
class Property
class SchoolDay < DataMapper::Property::String
def custom?
true
end
end
end
end
That's working here.

Resources