I'm looking for a ruby gem (or rails plugin) which abstracts the details of memcached in the same way that ActiveRecord abstracts the details of SQL. I am NOT looking for something to help cache ActiveRecord models in memcached. I'm sure there are approximately 4215 gems that will help with that problem.
Ideally what I'd like is to be able to do something like:
class Apple < MemcachedModel
# whatever else here
end
and then be able to do stuff like:
my_apple = Apple.find('some memcached key')
which would look up the JSON representation of this class in memcached and deserialize it. I'd also maybe be able to do things like:
my_apple.color = "red"
# persist changes back to memcached
my_apple.save
# load any changes from memcached into local model
my_apple.update
It seems like someone must have scratched this itch by now and created something along these lines, but whenever I google for such a gem I just keep turning up thing which help cache AR models using memcached.
You can take a look at my moneta gem, which is an ORM'ish thing for all kinds of key-value-stores. You can see it at: http://github.com/wycats/moneta/tree/master
The basic idea behind moneta is that all KVSs should behave exactly like a subset of normal Ruby hashes. We support:
#[]
#[]=
#delete
#fetch
#key?
#store
#update_key
#clear
The store and update_key methods take an additional options hash which you can use thusly:
cache = Moneta::Memcache.new(:server => "localhost:11211", :namespace => "me")
cache.store("name", "wycats", :expires_in => 2)
cache.update_key("name", :expires_in => 10)
We support a large number of KVSs:
BerkeleyDB
CouchDB
DataMapper (which means any store supported by DM)
Files
LMC
Memcache
In-process memory
MongoDB
Redis
Tokyo Cabinet
Tokyo Tyrant
S3
SDBM
Files using XAttrs
Every store supports expiry, either natively (like in memcached) or using a standard module that emulates memcache-style expiry. The API is always identical and there is a shared spec that all adapters are run against to ensure compliance.
It is also quite easy to add your own adapter, which is why so many exist.
I don't know about any Ruby ActiveRecord-like adapter for Memcached. A similar library would probably be hard to create because Memcached doesn't act as a relational database.
The result is that the library wouldn't be able to implement about the 80% of the features supported by ActiveRecord, so what's the benefit of such an implementation?
You already have everything you need in Rails to work with memcache with a "CRUD" pattern.
Rails.cache.read('key')
Rails.cache.write('key', 'value')
Rails.cache.delete('key')
Rails.cache.increment('key', 5)
Rails.cache.fetch('key') { 'value' }
If you feel more comfortable, you can create a wrapper and proxy these methods with corresponding new/create/update/save/destroy methods. However, you would never be able to go beyond a basic CRUD system just because Memcached is not intended to be a relational database.
It's fairly easy to implement.
require 'ostruct'
require 'active_support/cache'
class StoredStruct < OpenStruct
attr_writer :store
def self.store
#store || superclass.store
end
def self.expand_key(key)
'StoredStruct_' + (superclass == OpenStruct ? '' : "#{self}_") + key.to_s
end
def self.get_unique_id
key = expand_key('unique_id')
store.write(key, 0, :unless_exist => true)
store.increment(key)
end
def self.save(instance)
id = instance.id || get_unique_id
store.write(expand_key(id), instance)
id
end
def self.find(id)
store.read(expand_key(id))
end
attr_reader :id
def attributes
#table
end
def attributes=(hash)
#table = hash
end
def new_record?
self.id.nil?
end
def save
#id = self.class.save(self)
true
end
def reload
instance = self.class.find(self.id)
self.attributes = instance.attributes unless self == instance
self
end
end
Use it like this:
# connect to memcached
StoredStruct.store = ActiveSupport::Cache::MemCacheStore.new("localhost:11211")
class Apple < StoredStruct
end
fruit = Apple.new
fruit.color = "red"
fruit.taste = "delicious"
fruit.id
#=> nil
fruit.save
#=> true
fruit.id
#=> 1
# to load any changes:
fruit.reload
Apple.find(1)
#=> fruit
As Simone Carletti wrote, Memcached isn't a relational database; it can't even list all its keys. As such, any ActiveRecord-like model storing data in Memcached will not contain all the functionality of ActiveRecord. Nonetheless, I think there is some value in having a consistent API for all your models, so if it makes sense to have one of your models store its data in Memcached, you can use this module I created for that purpose:
http://blog.slashpoundbang.com/post/1455548868/memcachemodel-make-any-ruby-object-that-persists-in
You may be looking for Nick Kallen's cache-money.
Related
I want to load data from my YAML file and use the it in my ruby application. The contents available in YAML will be used in different places. Therefore, I want to read it initially and use the data whenever required.
I would like to know what is the best practice for this?
What I tried is
config.yaml
db:
username: admin
password: admin
config.rb
class Config
class << self
attr_accessor :uname, :pwd
def load
config_data = YAML.load_file("c:\config.yml")
#uname = config_data['db']['username']
#pwd = config_data['db']['password']
end
end
end
my_app.rb
Config.load
puts Config.uname
puts Config.pwd
Please let me know whether this is a right way to load and use YAML data. If not please share the best practice.
It hardly depends on your intentions, but generally speaking, yes, it is mostly correct approach. What do you want to do more, is to cache once read data:
class Config
class << self
attr_accessor :uname, :pwd
def load force_reload = false
return #config_data if #config_data && !force_reload
#config_data = YAML.load_file("c:\config.yml")
#uname = #config_data['db']['username']
#pwd = #config_data['db']['password']
end
end
end
Here we return immediately, if the #config was already loaded, allowing fast subsequent calls to Config.config.
Whether one wants to explicitly reload the config, it should pass the parameter to load:
Config.load true # true means reload YAML explicitly
I have two different classes that both represent objects that need to be persisted to my database and now I want to share the database client object between the two classes. I want to avoid instantiating the client object more than once.
Currently I do this by using a global variable
$client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")
class Person
def save
$client.query("INSERT INTO persons")
end
end
class Car
def save
$client.query("INSERT INTO cars")
end
end
This works, but I am wondering if there are more correct ways to do this and why they are more correct?
You can inherit from a parent class. This allows you to share common functionality across objects and follows DRY (do not repeat yourself) programming principles. It will also allow you to protect your DB connection with locks, resuces, queues, pools, and whatever else you may want to do without having to worry about it in your children classes
class Record
#table_name = nil
##client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")
def save
##client.query("INSERT INTO #{#table_name}") if #table_name
end
end
class Person < Record
#table_name = "persons"
end
class Car < Record
#table_name = "cars"
end
While we are on the subject, you should look at using ActiveRecord for handling your database models and connections. It already does pretty much anything you'll need and will be more compatible with other gems already out there. It can be used without rails.
As an alternative on using inheritance, why not consider a simple Singleton pattern? This could make your models cleaner, by separating the responsibility outside your classes. And eliminating the need for inheritance.
The example below illustrates this. Only one, single instance of the DataManager class can exist. So, you'll only instantiate it once - but can use it everywhere:
require 'singleton'
class DataManager
include Singleton
attr_accessor :last_run_query
def initialize()
if #client.nil?
p "Initialize the Mysql client here - note that this'll only be called once..."
end
end
def query(args)
# do your magic here
#last_run_query = args
end
end
Next, calling it using the .instance accessor is a breeze - and will always point to one single instance, like so:
# Fetch, or create a new singleton instance
first = DataManager.instance
first.query('drop table mother')
p first.last_run_query
# Again, fetch or create a new instance
# this'll actually just fetch the first instance from above
second = DataManager.instance
p second.last_run_query
# last line prints: "drop table mother"
For the record, the Singleton pattern can have some downsides and using it frequently results in a never-ending debate on whether you should use it or not. But in my opinion it's a decent alternative to your specific question.
I started off with https://gist.github.com/scttnlsn/1295485 as a basis to make a restful sinatra app. I'm having difficulty, though, managing HaBTM relationships for paths such as
delete '/:objecttype/:objid/:habtm_type/:habtm_id'
I already have the objecttype thanks to the map (as per that gist), and pulling the right object from the db with the id is straightfoward. However, getting the other side of the habtm and calling the appropriate method on objecttype to delete the relationship involves turning a handful of strings into the appropriate objects and methods.
I came up with a solution, but it uses eval. I'm aware that using eval is evil and doing so will rot my very soul. Is there a better way to handle this, or should I put in some safeguards to protect the code and call it a day?
Here's a working, self contained, sinatra-free example to show how I'm doing the eval:
require 'mongoid'
require 'pp'
def go
seed
frank = Person.find_by(name:"Frank")
apt = Appointment.find_by(name:"Arbor day")
pp frank
really_a_sinatra_route(frank.id, "appointments", apt.id)
frank.reload
pp frank
end
def really_a_sinatra_route(id, rel_type,rel_id)
# I use "model" in the actual app, but hardwired a person here to
# make a simpler example
person = Person.find_by(id: id)
person.deassociate(rel_type,rel_id)
end
class Base
def deassociate(relationship,did)
objname = associations[relationship].class_name
# Here's the real question... this scares me as dangerous. Is there
# a safer way to do this?
obj = eval "#{objname}.find(did)"
eval "#{relationship}.delete(obj)"
end
end
class Person < Base
include Mongoid::Document
has_and_belongs_to_many :appointments
end
class Appointment < Base
include Mongoid::Document
has_and_belongs_to_many :persons
end
def seed
Mongoid.configure do |config|
config.connect_to("test_habtmexample")
end
Mongoid.purge!
frank=Person.create(name:"Frank")
joe=Person.create(name:"Joe")
ccon = Appointment.create(name:"Comicon")
aday = Appointment.create(name:"Arbor day")
frank.appointments << ccon
frank.appointments << aday
ccon.persons << joe
joe.reload
end
go
A nice gentleman on freenode helped me out. Those two evals can be replaced with:
obj= self.send(relationship.to_sym).find(did)
self.send(relationship.to_sym).delete(obj)
I have a class that uses the AWS S3 gem and have several methods in my class that utilise the gem. My issue is, that rather than configuring it in several locations, I'd like to make it a property of my object.
In PHP, we'd do this;
<?php
class myClass {
private $obj;
public function __construct() {
$this->obj = new Object();
}
}
?>
And then I could use $this->obj->method() anywhere in myClass.
I am having a hard time getting similar to work in ruby.
My scenario is similar to this;
require 'aws/s3'
class ProfileVideo < ActiveRecord::Base
def self.cleanup
# <snip> YAML load my config etc etc
AWS::S3::Base.establish_connection!(
:access_key_id => #aws_config['aws_key'],
:secret_access_key => #aws_config['aws_secret']
)
end
def self.another_method
# I want to use AWS::S3 here without needing to establish connection again
end
end
I have also noticed in my class that initialize fails to execute - a simple 'puts "here"' does nothing. Considering this is a rake task and I can 'puts "here"' in the other methods. I'm not sure if maybe rake does not init like running ProfileVideo.new would?
Anyway, thanks in advance.
I'm not familiar with the S3 gem in particular but here are a couple ways you could go about this.
If you simply want to make establishing the connection easier, you can create a method in your model like so:
def open_s3
return if #s3_opened
AWS::S3::Base.establish_connection!(
:access_key_id => #aws_config['aws_key'],
:secret_access_key => #aws_config['aws_secret']
)
#s3_opened = true
end
then you can call open_s3 at the top of any methods that require it and it will only open once.
Another route you could take is to place the connection code in a before hook set to fire before any other hooks (IIRC, the order in which you define them sets the order in which they fire) and then make your calls.
In either case, I would recommend against putting your AWS key and secret into your code. Instead, those should go into a config file is ignored by your version control system and generated on-deploy for remote systems.
I'm quite new to OOP and I'm concerned that this class that I've written is really poorly designed. It seems to disobey several principles of OOP:
It doesn't contain its own data, but relies on a yaml file for
values.
Its methods need to be called in a particular order
It has a lot of instance variables and methods
It does work, however. It's robust, but I'll need to modify the source code to add new getter methods every time I add page elements
It's a model of an html document used in an automated test suite. I keep thinking that some of the methods could be put in subclasses, but I'm concerned that I'd have too many classes then.
What do you think?
class BrandFlightsPage < FlightSearchPage
attr_reader :route, :date, :itinerary_type, :no_of_pax,
:no_results_error_container, :submit_button_element
def initialize(browser, page, brand)
super(browser, page)
#Get reference to config file
config_file = File.join(File.dirname(__FILE__), '..', 'config', 'site_config.yml')
#Store hash of config values in local variable
config = YAML.load_file config_file
#brand = brand #brand is specified by the customer in the features file
#Define instance variables from the hash keys
config.each do |k,v|
instance_variable_set("##{k}",v)
end
end
def visit
#browser.goto(#start_url)
end
def set_origin(origin)
self.text_field(#route[:attribute] => #route[:origin]).set origin
end
def set_destination(destination)
self.text_field(#route[:attribute] => #route[:destination]).set destination
end
def set_departure_date(outbound)
self.text_field(#route[:attribute] => #date[:outgoing_date]).set outbound
end
def set_journey_type(type)
if type == "return"
self.radio(#route[:attribute] => #itinerary_type[:single]).set
else
self.radio(#route[:attribute] => #itinerary_type[:return]).set
end
end
def set_return_date(inbound)
self.text_field(#route[:attribute] => #date[:incoming_date]).set inbound
end
def set_number_of_adults(adults)
self.select_list(#route[:attribute] => #no_of_pax[:adults]).select adults
end
def set_no_of_children(children)
self.select_list(#route[:attribute] => #no_of_pax[:children]).select children
end
def set_no_of_seniors(seniors)
self.select_list(#route[:attribute] => #no_of_adults[:seniors]).select seniors
end
def no_flights_found_message
#browser.div(#no_results_error_container[:attribute] => #no_results_error_container[:error_element]).text
raise UserErrorNotDisplayed, "Expected user error message not displayed" unless divFlightResultErrTitle.exists?
end
def submit_search
self.link(#submit_button_element[:attribute] => #submit_button_element[:button_element]).click
end
end
If this class is designed as a Facade, then it's not (too) bad design. It provides a coherent unified way to perform related operations that rely on a variety of un-related behavior holders.
It appears to be poor separation of concerns, in that this class essentially coupling all the various implementation details, which might turn out to be somewhat tricky to maintain.
Finally, the fact methods need to be called in a specific order may hint at the fact you're trying to model a state machine - in which case it probably should be broken down to several classes (one per "state"). I don't think there's a "too many methods" or "too many classes" point you'd reach, the fact is you need the features provided by each class to be coherent and making sense. Where to draw the line is up to you and your specific implementation's domain requirements.