How to cache an array using Active Model Serializers (0.8)? - ruby-on-rails

On an individual Active Model Serializer you can specify the cache key within your serializer, i.e.
Caching a single object using AMS
class OrgSerializer < ActiveModel::Serializer
cached
delegate :cache_key, to: :object
end
Array caching
I need to deliver an array of these objects:
array_json = ActiveModel::ArraySerializer.new(Org.all.limit(50)).to_json
Rather than do 50 cache hits to construct the array I want to do a single cache hit and expire it when any of the array elements expires.
How to cache the array (and construct the key)?
I therefore want a cache key for the array that is made up of the individual object IDs and their last-updated at date like this:
def cache_key_for_collection collection
keys = collection.collect{|element| "#{element.id}#{element.updated_at}" }
keys.join ""
end
How can you do this with AMS (0.8)?
Does the AMS array serializer allow for this or is it something that needs to just be done manually? Is there a way to declare that you want caching and how the key should be constructed?
If it doesn't do it then what would be the neatest way to manually construct the cache/key?

It works very similarly to standard Serializers. You will create a custom ArraySerializer that looks something like this:
class BaseArraySerializer < ActiveModel::ArraySerializer
cached
def cache_key
keys = object.map{|obj| "#{obj.id}-#{obj.updated_at.to_s(:number)}" }
keys.join "/"
end
end
The one thing to note is that you are still doing object.map instead of objects, but this is purely how AMS 0.8.0 is implemented.
Then to deliver the json the only change you would have to make is:
array_json = ActiveModel::ArraySerializer.new(Org.all.limit(50)).to_json

Related

How to mark as outdated an ActiveRecord object attribute?

I have a database trigger that modifies a field on INSERT. Then when I run object.my_attribute it returns nil instead of lets say 42.
If I do object.reload.my_attribute, this is fine. But I don't want to reload the whole object or part of it unless it is necessary. And I believe code shouldn't be concerned when and how an object was created. It should just be correct.
Is it possible to mark that particular attribute as outdated and any attempt to get its value to result in a query that fetches it from database?
For example:
after_save :forget_my_attribute
def forget_my_attribute
forget_field :my_attribute
end
I think it's better to make some service object where field is modified and call it when create the record. CreateModel.call(args) instead of Model.create(args). It will be more clear than database trigger I think
But you can do something like this
after_create_commit :fetch_my_attribute
def fetch_my_attribute
self[:my_attribute] = self.class.find_by(id: id)[:my_attribute]
end
Or more flexible fetch attribute you need dynamically
def fetch_attribute(atr)
self[atr] = self.class.find_by(id: id)[atr]
end
object.fetch_attribute(:my_attribute)

Extend array without creating new class in Rails 4

I am using an API and receiving a array of hashes. Lets say:
array = client.getObjects(123) //where 123 is some collection of object ID
I want to add some additional attributes to the array to use later in my view, like:
<%= array.getRequestor %> // return a string
What is the easiest way to do this? I was thinking about creating a new class that extends array but I wanted to know can I just add a string "requestor" attribute a lot easier?
Thanks
Extending a core class is not a good idea in general, especially when the additional responsibilities you want to add in are specific to your functional domain.
6 months down the line, somebody (perhaps yourself) will be trying to debug the code and wondering why does Array expose a random custom method.
It would be better to explicitly define your custom view object, perhaps by using a Struct, eg:
# my_view_object.rb
class MyViewObject < Struct.new(:hash)
def getRequestor
# manipulate / return specific hash data
end
end
# controller
#view_obj = MyViewObject.new(client.getObjects(123))
# view
#view_obj.hash # original hash
#view_obj.getRequestor # your custom attribute
Note that the intent of a Struct is to represent a custom data structure, not behaviour. If your custom method needs to do unrelated work, you might want to use a PORO (Plain Old Ruby Object) instead.
I'd say that extending Array sounds like a really bad idea. I would suggest you instead wrap the array in hash of your own. For example
my_hash = {getRequestor: ?, array: array}
and then use it like
<%= my_hash.getRequestor %>
as in your example code.

Active record where query for value inside of an array

Question: Is it possible to build a class method scope that can query objects based on values inside an array in a table? If yes, how can I do this?
In my example, I have a “wells” table that has an array field called “well_tags”. I want to build a query that returns all objects that have a specified value (such as “ceramic”) in the wells_tags array. The basic query would be something like this:
#well = Well.all
#query = #well.where(“well_tags contains ceramic”)
And then the class method scope would look something like this, with the “well_tag_search” param passed in from the controller:
class Well < ActiveRecord::Base
def self.well_tag_filter(well_tag_search)
if well_tag_search.present?
where(“well_tags contains ceramic")
else
Well.all
end
end
I found another post that asks a similar question (see link below), but I cannot get the answer to work for me...the result is always 'nil' when I know there should be at least 1 object. I am a beginner using sqlite (for now) as my database and rails 4.0.
Active Record Query where value in array field
Thanks!
UPDATE: some progress
I figured out how to create an array of all the objects I want using the ‘select’ method. But I still need to return the results as an Active Record object so I create a class method scope.
#well = Well.select
{ |well| if well.well_tags.present?
then well.well_tags.include? ‘ceramic' end }
#well.class #=> array
Not sure where Show is coming from.
Can you try doing Well.all instead of Show.all?

Ruby on Rails Class method scoped objects references

I was wondering If I could modify objects using a class method.
For example, users = User.scoped # This will select all the objects
And, suppose, I want to assign a variable for each of the object there is. Let's say, I want them to share a single value. So, when I try to access, for example, users.first.my_variable it would produce the value, I want.
My naive implementation:
def self.set_my_variable(variable_value)
scoped.tap do |obj|
obj.my_variable = variable_value
end
end
So, ideally, when I wan't this variable to be set, I should call the class method like this: users.set_my_variable("hello, stackoverflow")
But, when I try accessing the variable through arbitrary object of the set, like this:
users.first.my_variable
I get nil. Comparing .object_id's in both: obj.object_id and users.first.object_id shows that they are different. Why so ? I thought that they share the same reference (pointing to the same objects)
Any way of fixing it (preferrably without passing a collection to this class method) ?
you need to save object to database:
obj.my_variable = variable_value
obj.save

Something like $ or ## variable, but unique for each user session. Rails

I have array of objects. I can't store it in DB for performance reason. I tried store array in Global ($var) and Class (##var) variables (in controller), but it was the same array for all users. It should be unique for each user session and in each session it should be able to be modified quickly.
I understand that the session[] - is not the best solution. What is the best way?
I'm doing something like this $lines_rules << Processing::rule_creator(...) in every time, when run action in controller.
$lines_rules - it is my array of objects.
Why DB is not right for store $lines_rules? In $lines_rules I store objects with lambda function. When user press button I need call every lambda function with user input and keep result. Then I load new objects into $lines_rules. Thus every request change $lines_rules. I think work with memory is the better way for perfomance.
UPDATE
I use $global_hash [ session[:session_id] ] and this technique for resource saving:
def dead_sessions_killer
ActiveRecord::SessionStore::Session.where(["updated_at < ?", 30.minutes.ago]).each do |session|
$global_hash.delete_if {|key, value| key == session.session_id }
session.delete
end
end
Use a global, but a global hash, keyed by the user's session id.
Store whatever you want in there, although with proper caching, hard to say if this is a great idea.
You could declare the array as a class variable (only 1 # sign), and provide an accessor method.
That is:
class Foo
#some_array
def some_array
#some_array
end
end
From other classes, this lets you call foo_instance.some_array, which keeps the array specific to the user, but allows public access from any class.
You could store the user session in memory with memcache. That would be convenient and fast.
http://awesomerails.wordpress.com/2011/08/23/rails-3-memcached-session-store/

Resources