Milliseconds with Rails and Mysql - ruby-on-rails

What is the best way to store Time in milliseconds when using Rails+Mysql?
I am about to use a decimal and composed_of in order to be able to manipulate this value as a Ruby Time. Does anyone have a better idea?!

Several years have passed since this was asked. Here's an updated solution:
https://gist.github.com/MarkMurphy/93adca601b05acffb8b5601df09f66df

I'm not sure I fully understand what you're trying to do, but have you considered simply overriding the reader/writer methods in your model?. If this works for you, it might be preferred over your proposed solution since it's arguably more readable.
MyClass < ActiveRecord::Base
# Override reader method
def my_attribute
super().from_milis
end
# Override writer method
def my_attribute=(value)
super(value.to_milis)
end
end

Posted a solution to store millisecond precision in MySql using composed_of
http://ternarylabs.com/2011/09/26/millisecond-precision-timestamp-in-rails-with-mysql/

1) Store it as a :decimal with ample precision for your purposes.
2) Create a helper method yourself. Something like this:
# app/helpers/application_helper.rb
module ApplicationHelper
def time_with_ms(time)
minutes = (time % 1.minute).floor
seconds = time % 1.minute
"%02d:%05.2f" % [minutes, seconds]
end
end

My approach was to:
open the time class and implement the methods :from_milis and :to_milis :
class Time
def self.from_milis(milis)
self.at(milis.to_f/1000)
end
def to_milis
self.to_f*1000
end
end
migrate the column from timestamp to :decimal,:precision=>17
then, in the AR class in which i am using this column as attribute:
composed_of :ts,
:class_name=>"Time",
:mapping=>%w(ts to_milis),
:constructor=>:from_milis,
:converter=>:from_milis
I just had gochas when using this attribute in arel queries, where I had to explicitly call to_milis in order to get the intended value in the comparision.

Related

Bypass readonly? when saving ActiveRecord

I use the readonly? function to mark my Invoice as immutable after they've been sent; for by InvoiceLines, I simply proxy the readonly? function to the Invoice.
A simplified example:
class Invoice < ActiveRecord::Base
has_many :invoice_lines
def readonly?; self.invoice_sent? end
end
def InvoiceLine < ActiveRecord::Base
def readonly?; self.invoice.readonly? end
end
This works great, except that in one specific scenario I want to update an InvoiceLine regardless of the readonly? attribute.
Is there are way to do this?
I tried using save(validate: false), but this has no effect. I looked at persistence.rb in the AR source, and that seems to just do:
def create_or_update
raise ReadOnlyRecord if readonly?
...
end
Is there an obvious way to avoid this?
A (somewhat dirty) workaround that I might do in Python:
original = line.readonly?
line.readonly? = lambda: false
line.save()
line.readonly? = original
But this doesn't work in Ruby, since functions aren't first-class objects ...
You can very easily redefine a method in an instantiated object, but the syntax is definition rather than assignment. E.g. when making changes to a schema that required a tweak to an otherwise read-only object, I have been known to use this form:
line = InvoiceLine.last
def line.readonly?; false; end
Et voila, status overridden! What's actually happening is a definition of the readonly? method in the object's eigenclass, not its class. This is really grubbing around inside the guts of the object, though; outside of a schema change it's a serious code smell.
One crude alternative is forcing Rails to write an updated column directly to the database:
line.update_columns(description: "Compliments cost nothing", amount: 0)
and it's mass-destruction equivalent:
InvoiceLine.where(description: "Free Stuff Tuesday").update_all(amount: 0)
but again, neither should appear in production code outside of migrations and, very occasionally, some carefully written framework code. These two bypass all validation and other logic and risk leaving objects in inconsistent/invalid states. It's better to convey the need and behaviour explicitly in your model code & interactions somehow. You could write this:
class InvoiceLine < ActiveRecord::Base
attr_accessor :force_writeable
def readonly?
invoice.readonly? unless force_writeable
end
end
because then client code can say
line.force_writable = true
line.update(description: "new narrative line")
I still don't really like it because it still allows external code to dictate an internal behaviour, and it leaves the object with a state change that other code might trip over. Here's a slightly safer and more rubyish variant:
class InvoiceLine < ActiveRecord::Base
def force_update(&block)
saved_force_update = #_force_update
#_force_update = true
result = yield
#_force_update = saved_force_update
result
end
def readonly?
invoice.readonly? unless #_force_update
end
end
Client code can then write:
line.force_update do
line.update(description: "new description")
end
Finally, and this is probably the most precision mechanism, you can allow just certain attributes to change. You could do that in a before_save callback and throw an exception, but I quite like using this validation that relies on the ActiveRecord dirty attributes module:
class InvoiceLine < ActiveRecord::Base
validate :readonly_policy
def readonly_policy
if invoice.readonly?
(changed - ["description", "amount"]).each do |attr|
errors.add(attr, "is a read-only attribute")
end
end
end
end
I like this a lot; it puts all the domain knowledge in the model, it uses supported and built-in mechanisms, doesn't require any monkey-patching or metaprogramming, doesn't avoid other validations, and gives you nice error messages that can propagate all the way back to the view.
I ran into a similar problem with a single readonly field and worked around it using update_all.
It needs to be an ActiveRecord::Relation so it would be something like this...
Invoice.where(id: id).update_all("field1 = 'value1', field2 = 'value2'")
Here is an answer, but I don't like it. I would recommend to think twice about the design: If you make this data immutable, and you do need to mutate it, then there may be a design issue. Let aside any headache if the ORM and the datastore "differ".
One way is to use the meta programming facilities. Say you want to change the item_num of invoice_line1 to 123, you can proceed with:
invoice_line1.instance_variable_set(:#item_num, 123)
Note that the above will not work directly with ActiveRecord models' attributes, so it would need be adapted. But well, I would really advice to reconsider the design rather than falling for black magic.
Here's an elegant solution how to disallow modification generally but allow it if it is specifically requested:
In your model, add the following two methods:
def readonly?
return false if #bypass_readonly
return true # Replace true by your criteria if necessary
end
def bypass_readonly
#bypass_readonly=true
yield
#bypass_readonly=false
end
Under normal circumstances, your object is still readonly, so no risk of accidentally writing to a readonly object:
mymodel.save! # This raises a readonly error
However in privileged places where you are sure that you want to ignore the readonlyness, you can use:
mymodel.bypass_readonly do
# Set fields as you like
mymodel.save!
end
Everything inside the bypass_readonly block is now allowed despite readonly. Have fun!
This overrides the #readonly? method for one particular only, not affecting anything else:
line.define_singleton_method(:readonly?) { false }
readonly_attrs = described_class.readonly_attributes.dup
described_class.readonly_attributes.clear
# restore readonly rails constraint
described_class.readonly_attributes.merge(readonly_attrs)
This worked for us with Rails 7.

Serializing a custom class in ActiveRecord with specific syntax

I have a custom Interval class that I would like to use inside a few different ActiveRecord models. Currently I am storing the interval as a string with a specific syntax (w/ a custom validator to enforce formatting) and just creating a new object anytime I need to access Interval methods.
What do I need to add to the ActiveRecord models/ Interval class to be able to use the interval as an object instead of a string, while still storing it in the database using the specific syntax?
Hopefully that makes sense but if not hopefully the following example clears things up.
The ActiveRecord class currently looks something like:
class MyClass < ActiveRecord::Base
validates :interval, allow_blank: true, interval: true # custom validator
...
And to do anything useful I create a new interval:
def some_helper
...
interval_object = Interval.new(#my_class.interval) # #my_class.interval is just a string with specific syntax
if interval_object.useful? # 'useful' method
...
But I would like to do:
def some_helper
...
if #my_class.interval.useful? # 'useful' method
...
Interval syntax that initialize expects:
3:day # represents every 3 days
1:week # represents every week
This seems like it should have a simple solution but I can't seem to find the right phrasing.
You should be able to override the accessor and mutator methods to do what needs to be done:
def interval
Interval.new(super)
end
def interval=(i)
# Or whatever needs to be done to convert `i` back to a string,
# keep in mind that `i` might be a string already.
super(i.to_s)
end
Then you can say things like:
#my_class.interval.useful?
#my_class.interval = some_interval_object
#my_class.interval = some_string_that_looks_right
#my_class.update_attributes(:interval => some_interval_object)
#my_class.update_attributes(:interval => some_string_that_looks_right)
and The Right Things should happen.

Is there a way to override a method of an instance variable

I have an instance variable in an active record class called hash_value. It's a serialized hash.
I want to display the hash as XML. Is it right to call hash_value.to_xml? Many nodes are numbers, and XML tags are not allowed to be only number (or start with a number).
I want to override the to_xml method of hash_value. I don't want to override on all hashes, just the hash that's in this record.
class ProductVersion < ActiveRecord::base
serialize :hash_value, Hash
def hash_value.to_xml
end
end
I tried the answer here redefining a single ruby method on a single instance with a lambda
but it doesn't seem to be applicable. I suspect because when I load the record, it creates a new hash_value object and thus the singleton adjustment on the original is moot.
Any thoughts would be great.
I know I could write a function hash_value_to_xml, but I'd rather avoid doing something like that.
Thanks to the first comment, I came up with a solution. Not a good one, but one that works. I'd love to see if there's a better way, because this one smells a bit.
class MyHash < Hash
def to_xml
1/0 #to see if it's run.
end
end
def hash_value
MyHash.new().merge( attributes['hash_value'] );
end
I would personally go for hash_value_to_xml route. But since you insist, here's an idea that might work (haven't tested that)
class ProductVersion < ActiveRecord::base
serialize :hash_value, Hash
alias_method :old_hash_value, :hash_value
def hash_value
h = old_hash_value
h.define_singleton_method(:to_xml) do
# your custom xml logic here
end
h
end
end
The idea is that you intercept value returned from hash_value and patch it on the fly.

What is the equivalent for write_attribute for associations in Rails?

I'd like to override the setter for an association, but write_attribute() isn't working - probably because that method only works for database columns.
I have tried super(), but that doesn't work either (didn't think it would... but it was worth a guess).
How do I override the setter? Here is what I am trying to do:
def parent=(value)
# this line needs to be changed
write_attribute(:parent, value)
if value.subject.start_with?('Re:')
self.subject = "#{value.subject}"
else
self.subject = "Re: #{value.subject}"
end
self.receivers << value.sender
end
What worked for me is the following:
def parent=(new_parent)
# do stuff before setting the new parent...
association(:parent).writer(new_parent)
end
I found one way to do it, but I am disturbed by it:
alias_method :old_parent=, :parent=
def parent=(value)
self.old_parent = value
if value.subject.start_with?('Re:')
self.subject = "#{value.subject}"
else
self.subject = "Re: #{value.subject}"
end
self.receivers << value.sender
end
One thing I don't necessarily like about Rails is that whenever you want to do something that is out of the norm just a bit - but not unreasonable by any means - the "how" is very different than what your intuition would come up with.
It's not a problem when you know the exceptions, but when you're learning, this sort of irregularity and inconsistency on how to do things makes it harder to learn - not easier.
Java might be initially harder to learn, but it's way more consistent. Your intuition can take you a lot further once you think in Java. This is not true once you think in Rails. Rails is about memorization of methods to call and memorization on how to do things. In java, you can reason it out a lot more... and intellisense fills in the rest.
I'm just disappointed. This is a reoccurring pattern for me - I want do something that is just "a little more complex" than the framework examples... and the "how" is inconsistent and takes 30 minutes or maybe even hours to locate and find the answer for it.
In Rails 4.2.1 doc:
# Association methods are generated in a module that is included into the model class,
# which allows you to easily override with your own methods and call the original
# generated method with +super+. For example:
#
# class Car < ActiveRecord::Base
# belongs_to :owner
# belongs_to :old_owner
# def owner=(new_owner)
# self.old_owner = self.owner
# super
# end
# end
Instead of
def parent=(value)
write_attribute(:parent, value)
end
Couldn't you just do:
def parent=(parent)
parent_id = parent.id
end

In ActiveRecord, how to specify a class attribute that is calculated just once for the duration of the page?

Let's say I have an ActiveRecord called Apples, and I want a class method that calculates the total price of every apple in my database, as so:
def Apples.total_price
Apples.sum(:price)
end
This is a method I use in one of my views to make a pie chart. So, something like:
Apples.brand("red delicious").sum(:price)/Apples.total_price = % of pie chart
Apples.brand("fuji").sum(:price)/Apples.total_price = another % of pie chart
Apples.total_price is called repeatedly but the value isn't going to change at this point. A) Does Rails make the query repeatedly, or does it know to cache the value? And if it does call it repeatedly, what's the best way to define a method in Apple so that this total_price is just calculated once for the runtime of this view?
The way you've defined it, I believe it is done repeatedly. The logs will show for sure.
Through a technique known as memoization you process the it only if needed. Rails supplies easy memoization for instance methods, but you're on your own for class methods.
Here's how you roll your own memoization for a class method.
class Apple < ActiveRecord::Base
cattr_reader :total_price
def Apple.total_price
##total_price ||= Apples.sum(:price)
end
end
first way
def total_price
#total_price ||= calc_total_price
end
There is also memoize method. so you can do it this way also
def total_price
#you long running code goes here
end
memoize :total_price
Here you can find some details on it:
http://ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization

Resources