Coder Social home page Coder Social logo

ohm's Introduction

Ohm ॐ

Object-hash mapping library for Redis.

Description

Ohm is a library for storing objects in Redis, a persistent key-value database. It has very good performance.

Community

Join the mailing list: http://groups.google.com/group/ohm-ruby

Meet us on IRC: #ohm on freenode.net

Related projects

These are libraries in other languages that were inspired by Ohm.

  • Ohm for Crystal, created by soveran
  • JOhm for Java, created by xetorthio
  • Lohm for Lua, created by slact
  • ohm.lua for Lua, created by amakawa
  • Nohm for Node.js, created by maritz
  • Redisco for Python, created by iamteem
  • redis3m for C++, created by luca3m
  • Ohmoc for Objective-C, created by seppo0010
  • Sohm for Lua, compatible with Twemproxy

Articles and Presentations

Getting started

Install Redis. On most platforms it's as easy as grabbing the sources, running make and then putting the redis-server binary in the PATH.

Once you have it installed, you can execute redis-server and it will run on localhost:6379 by default. Check the redis.conf file that comes with the sources if you want to change some settings.

If you don't have Ohm, try this:

$ [sudo] gem install ohm

Or you can grab the code from http://github.com/soveran/ohm.

Now, in an irb session you can test the Redis adapter directly:

>> require "ohm"
=> true
>> Ohm.redis.call "SET", "Foo", "Bar"
=> "OK"
>> Ohm.redis.call "GET", "Foo"
=> "Bar"

Connecting to a Redis database

Ohm uses a lightweight Redis client called Redic. To connect to a Redis database, you will need to set an instance of Redic, with an URL of the form redis://:<passwd>@<host>:<port>/<db>, through the Ohm.redis= method, e.g.

require "ohm"

Ohm.redis = Redic.new("redis://127.0.0.1:6379")

Ohm.redis.call "SET", "Foo", "Bar"

Ohm.redis.call "GET", "Foo"
# => "Bar"

Ohm defaults to a Redic connection to "redis://127.0.0.1:6379". The example above could be rewritten as:

require "ohm"

Ohm.redis.call "SET", "Foo", "Bar"

Ohm.redis.call "GET", "Foo"
# => "Bar"

All Ohm models inherit the same connection settings from Ohm.redis. For cases where certain models need to connect to different databases, they simple have to override that, i.e.

require "ohm"

Ohm.redis = Redic.new(ENV["REDIS_URL1"])

class User < Ohm::Model
end

User.redis = Redic.new(ENV["REDIS_URL2"])

Models

Ohm's purpose in life is to map objects to a key value datastore. It doesn't need migrations or external schema definitions. Take a look at the example below:

Example

class Event < Ohm::Model
  attribute :name
  reference :venue, :Venue
  set :participants, :Person
  counter :votes

  index :name
end

class Venue < Ohm::Model
  attribute :name
  collection :events, :Event
end

class Person < Ohm::Model
  attribute :name
end

All models have the id attribute built in, you don't need to declare it.

This is how you interact with IDs:

event = Event.create :name => "Ohm Worldwide Conference 2031"
event.id
# => 1

# Find an event by id
event == Event[1]
# => true

# Update an event
event.update :name => "Ohm Worldwide Conference 2032"
# => #<Event:0x007fb4c35e2458 @attributes={:name=>"Ohm Worldwide Conference"}, @_memo={}, @id="1">

# Trying to find a non existent event
Event[2]
# => nil

# Finding all the events
Event.all.to_a
# => [<Event:1 name='Ohm Worldwide Conference 2032'>]

This example shows some basic features, like attribute declarations and querying. Keep reading to find out what you can do with models.

Attribute types

Ohm::Model provides 4 attribute types:

  • Ohm::Model.attribute,
  • Ohm::Model.set
  • Ohm::Model.list
  • Ohm::Model.counter

and 2 meta types:

  • Ohm::Model.reference
  • Ohm::Model.collection.

attribute

An attribute is just any value that can be stored as a string. In the example above, we used this field to store the event's name. You can use it to store numbers, but be aware that Redis will return a string when you retrieve the value.

set

A set in Redis is an unordered list, with an external behavior similar to that of Ruby arrays, but optimized for faster membership lookups. It's used internally by Ohm to keep track of the instances of each model and for generating and maintaining indexes.

list

A list is like an array in Ruby. It's perfectly suited for queues and for keeping elements in order.

counter

A counter is like a regular attribute, but the direct manipulation of the value is not allowed. You can retrieve, increase or decrease the value, but you can not assign it. In the example above, we used a counter attribute for tracking votes. As the increment and decrement operations are atomic, you can rest assured a vote won't be counted twice.

reference

It's a special kind of attribute that references another model. Internally, Ohm will keep a pointer to the model (its ID), but you get accessors that give you real instances. You can think of it as the model containing the foreign key to another model.

collection

Provides an accessor to search for all models that reference the current model.

Tracked keys

Besides the provided attribute types, it is possible to instruct Ohm to track arbitrary keys and tie them to the object's lifecycle.

For example:

class Log < Ohm::Model
  track :text
  
  def append(msg)
    redis.call("APPEND", key[:text], msg)
  end
  
  def tail(n = 100)
    redis.call("GETRANGE", key[:text], -(n), -1)
  end
end

log = Log.create
log.append("hello\n")

assert_equal "hello\n", log.tail

log.append("world\n")

assert_equal "world\n", log.tail(6)

When the log object is deleted, the :text key will be deleted too. Note that the key is scoped to that particular instance of Log, so if log.id is 42 then the key will be Log:42:text.

Persistence strategy

The attributes declared with attribute are only persisted after calling save.

Operations on attributes of type list, set and counter are possible only after the object is created (when it has an assigned id). Any operation on these kinds of attributes is performed immediately. This design yields better performance than buffering the operations and waiting for a call to save.

For most use cases, this pattern doesn't represent a problem. If you are saving the object, this will suffice:

if event.save
  event.comments.add(Comment.create(body: "Wonderful event!"))
end

Working with Sets

Given the following model declaration:

class Event < Ohm::Model
  attribute :name
  set :attendees, :Person
end

You can add instances of Person to the set of attendees with the add method:

event.attendees.add(Person.create(name: "Albert"))

# And now...
event.attendees.each do |person|
  # ...do what you want with this person.
end

Sorting

Since attendees is a Ohm::Model::Set, it exposes two sorting methods: Ohm::Model::Collection#sort returns the elements ordered by id, and Ohm::Model::Collection#sort_by receives a parameter with an attribute name, which will determine the sorting order. Both methods receive an options hash which is explained below:

:order

Order direction and strategy. You can pass in any of the following:

  1. ASC
  2. ASC ALPHA (or ALPHA ASC)
  3. DESC
  4. DESC ALPHA (or ALPHA DESC)

It defaults to ASC.

Important Note: Starting with Redis 2.6, ASC and DESC only work with integers or floating point data types. If you need to sort by an alphanumeric field, add the ALPHA keyword.

:limit

The offset and limit from which we should start with. Note that this is 0-indexed. It defaults to 0.

Example:

limit: [0, 10] will get the first 10 entries starting from offset 0.

:by

Key or Hash key with which to sort by. An important distinction with using Ohm::Model::Collection#sort and Ohm::Model::Collection#sort_by is that sort_by automatically converts the passed argument with the assumption that it is a hash key and it's within the current model you are sorting.

Post.all.sort_by(:title)     # SORT Post:all BY Post:*->title
Post.all.sort(by: :title) # SORT Post:all BY title

Tip: Unless you absolutely know what you're doing, use sort when you want to sort your models by their id, and use sort_by otherwise.

:get

A key pattern to return, e.g. Post:*->title. As is the case with the :by option, using Ohm::Model::Collection#sort and Ohm::Model::Collection#sort_by has distinct differences in that sort_by does much of the hand-coding for you.

Post.all.sort_by(:title, get: :title)
# SORT Post:all BY Post:*->title GET Post:*->title

Post.all.sort(by: :title, get: :title)
# SORT Post:all BY title GET title

Associations

Ohm lets you declare references and collections to represent associations.

class Post < Ohm::Model
  attribute :title
  attribute :body
  collection :comments, :Comment
end

class Comment < Ohm::Model
  attribute :body
  reference :post, :Post
end

After this, every time you refer to post.comments you will be talking about instances of the model Comment. If you want to get a list of IDs you can use post.comments.ids.

References explained

Doing a Ohm::Model.reference is actually just a shortcut for the following:

# Redefining our model above
class Comment < Ohm::Model
  attribute :body
  attribute :post_id
  index :post_id

  def post=(post)
    self.post_id = post.id
  end

  def post
    Post[post_id]
  end
end

The only difference with the actual implementation is that the model is memoized.

The net effect here is we can conveniently set and retrieve Post objects, and also search comments using the post_id index.

Comment.find(post_id: 1)

Collections explained

The reason a Ohm::Model.reference and a Ohm::Model.collection go hand in hand, is that a collection is just a macro that defines a finder for you, and we know that to find a model by a field requires an Ohm::Model.index to be defined for the field you want to search.

# Redefining our post above
class Post < Ohm::Model
  attribute :title
  attribute :body

  def comments
    Comment.find(post_id: self.id)
  end
end

The only "magic" happening is with the inference of the index that was used in the other model. The following all produce the same effect:

# easiest, with the basic assumption that the index is `:post_id`
collection :comments, :Comment

# we can explicitly declare this as follows too:
collection :comments, :Comment, :post

# finally, we can use the default argument for the third parameter which
# is `to_reference`.
collection :comments, :Comment, to_reference

# exploring `to_reference` reveals a very interesting and simple concept:
Post.to_reference == :post
# => true

Modules

If your models are defined inside a module, you will have to define the references and collections as in the following example:

module SomeNamespace
  class Foo < Ohm::Model
    attribute :name
  end
  
  class Bar < Ohm::Model
    reference :foo, 'SomeNamespace::Foo'
  end
end

Indices

An Ohm::Model.index is a set that's handled automatically by Ohm. For any index declared, Ohm maintains different sets of objects IDs for quick lookups.

In the Event example, the index on the name attribute will allow for searches like Event.find(name: "some value").

Note that the methods Ohm::Model::Set#find and Ohm::Model::Set#except need a corresponding index in order to work.

Finding records

You can find a collection of records with the find method:

# This returns a collection of users with the username "Albert"
User.find(username: "Albert")

Filtering results

# Find all users from Argentina
User.find(country: "Argentina")

# Find all active users from Argentina
User.find(country: "Argentina", status: "active")

# Find all active users from Argentina and Uruguay
User.find(status: "active").combine(country: ["Argentina", "Uruguay"])

# Find all users from Argentina, except those with a suspended account.
User.find(country: "Argentina").except(status: "suspended")

# Find all users both from Argentina and Uruguay
User.find(country: "Argentina").union(country: "Uruguay")

Note that calling these methods results in new sets being created on the fly. This is important so that you can perform further operations before reading the items to the client.

For more information, see SINTERSTORE, SDIFFSTORE and SUNIONSTORE

Uniques

Uniques are similar to indices except that there can only be one record per entry. The canonical example of course would be the email of your user, e.g.

class User < Ohm::Model
  attribute :email
  unique :email
end

u = User.create(email: "[email protected]")
u == User.with(:email, "[email protected]")
# => true

User.create(email: "[email protected]")
# => raises Ohm::UniqueIndexViolation

Ohm Extensions

Ohm is rather small and can be extended in many ways.

A lot of amazing contributions are available at Ohm Contrib make sure to check them if you need to extend Ohm's functionality.

Upgrading

Ohm 2 breaks the compatibility with previous versions. If you're upgrading an existing application, it's nice to have a good test coverage before going in. To know about fixes and changes, please refer to the CHANGELOG file.

ohm's People

Contributors

abecciu avatar alfius avatar clivecrous avatar cyx avatar damonmorgan avatar djanowski avatar ehq avatar eloyesp avatar frodsan avatar gramos avatar joker-777 avatar kevinjhanna avatar lastcanal avatar luca3m avatar maiha avatar marr avatar matflores avatar mikz avatar mwpastore avatar phuongnd08 avatar porta avatar pote avatar rspeicher avatar sadfuzzy avatar seppo0010 avatar soveran avatar sush avatar yosefbennywidyo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ohm's Issues

Error messages

Hi! We want to add your adapter to our project: http://padrinorb.com

I've only a big trouble.

Is possible make errors compatible with active model? Basically we need different errors keys like:

:inclusion
:exclusion
:invalid
:confirmation
:accepted
:empty
:blank
:too_long
:too_short
:wrong_length
:taken
:not_a_number
:greater_than
:greater_than_or_equal_to
:equal_to
:less_than
:less_than_or_equal_to
:odd
:even
:record_invalid
:content_type

So we can use easly standard I18n error messages.

Is possible do that?

Thanks so much!
/DAddYE

Undefined method '[]' for nil:NilClass (NoMethodError)

require 'rubygems'
require 'ohm'

class Event < Ohm::Model
end

event = Event.create

When run, it throws following error:

/var/lib/gems/1.8/gems/ohm-0.0.31/lib/ohm/redis.rb:81:in initialize': undefined method[]' for nil:NilClass (NoMethodError)
from /var/lib/gems/1.8/gems/ohm-0.0.31/lib/ohm.rb:12:in new' from /var/lib/gems/1.8/gems/ohm-0.0.31/lib/ohm.rb:12:inredis'
from /var/lib/gems/1.8/gems/ohm-0.0.31/lib/ohm.rb:680:in db' from /var/lib/gems/1.8/gems/ohm-0.0.31/lib/ohm.rb:676:ininitialize_id'
from /var/lib/gems/1.8/gems/ohm-0.0.31/lib/ohm.rb:528:in create' from /var/lib/gems/1.8/gems/ohm-0.0.31/lib/ohm.rb:497:increate'
from /home/amit/NetBeansProjects/sinohm/lib/application.rb:8

Setting an ID on a new record makes #new? return false

class Post < Ohm::Model
  attribute :name
end

o = Post.new name: "Hello"
o.new? # true, as expected

p = Post.new id: 200, name: "Hello" # nonexistent id
p.new? # false -- even if the record doesn't exist in the data store

I see that new? is defined as "check if the object has an ID" in the documentation, but IMO the name of the method implies "check if it doesn't exist in the data store."

Helper to find many ocurrences

Hello,

Discussing this with soveran on IRC, Ohm provided in the past a way to search using sunionstore, which allowed things like this:

Model.search(:indexed_key => [1, 3, 5])

Model#find and Model#except do not provide this multiple values for the indexed key. You can consider this an equivalent to IN (...) in SQL-land.

He agreed that search could have a come back ;-)

I've tried to hack this myself but my expertise on Redis and Ohm is not as great as yours.

Thank you.

Joining an association doesn't set the association id

foo       = Foos[1]
foo.bars << Bar.create

The created Bar doesn't have its foo_id set. Worse, it's added to the Redis Bar:foo_id:MQ== set, so a Bar.find(:foo_id => 1) returns the Bar, even though its foo_id is nil.

Need hook for creation of `@_attributes` container

In Ohm::Model#initialize please provide a hook by calling, e.g. @_attributes ||= Hash.new { |hash, key| hash[key] = get(key) } Note use of conditional assignment. Same in the 1.0 branch...please update to call @_attributes ||= {}. This is needed for proper support of typecasting using your style cast[] hook. Doing it via a factory method, i.e. make_attributes would be better yet.

Thanks!
.tv/

No DB update when inside a Thread

While doing some tests i noticed that any update to a pre-existing database entry, while inside a Thread, does not perform any actions on the DB itself.

I already with thread safety on and off.

Any ideas?

Unititated Constant Ohm::Typecast

encoding: utf-8

require 'sinatra'
require 'haml'
require 'sass'
require 'redis'
require 'json'
require './utils.rb'
require 'ohm'
require 'ohm/contrib'

Redis.current = Redis.new(:host => '127.0.0.1', :port => 6379)

use Rack::MethodOverride

configure do
set :haml, :format => :html5
end

get '/stylesheets/application.css' do
scss :application, :style => :expanded
end

class Person < Ohm::Model
include Ohm::Typecast
attribute :first_name, String
attribute :last_name, String
attribute :mi, String
attribute :address, Hash
end

----Webrick response on start--------------------------------------
bastion.rb:25:in <class:Person>': uninitialized constant Ohm::Typecast (NameError) from bastion.rb:24:in

'

encoding: utf-8

Returning values from transactions

How does the current implementation provide for e.g. retrieving values atomically from the transaction context? Say, reading and/or updating multiple counters. I would fix this by returning the Store from commit after running it through the after filters. Probably it should be passed to the before filters too for symmetry and then cloned for each retry of the read/write loop. Either that and/or provide each transaction method block with the invoking object's self as context. Thoughts ?

Cannot delete from set by just id

You can only remove a member from a Set by passing the member itself. There should be a way to remove a set member by just its id.

IDs are incremented although insertion fails

Hi all,

I'm iterating over a set of items and wanna insert them into Redis backend:

      begin
        item = Item.create :part_number => part_number,
            :name => name,                
            :description => description
        puts item.id
      rescue Ohm::UniqueIndexViolation => uiv
        next # Skip if same item (part_number) already exists in the database
      end

UniqueIndexViolation triggers on items whith same :part_number (unique :part_number in my model). What now happens is: although the exception is triggered and the item is not inserted into Redis, the id is incremented. That's strange because I need monotonous increasing IDs without any gaps to get random access by Item[rand(1..Item.all.size)]. That would fail in my case cause the range contains IDs which are nil. Otherwise a set with 1200 data records with e.g. 1000 unique records contains 1000 items after insertion but also the IDs are counted from 1 to 1200. Any hints?

Stack level too deep issue!!

I use Rails 3.2.3 and unicorn for production mode, the operation system is CentOS 6.2. (Ohm 1.0.2, Ohm-contrib 1.0.1)

Recently i met a strange problem:

When I started the unicorn, everything went fine at the beginning. But after a few hours, this error occured:

Completed 500 Internal Server Error in 87ms

SystemStackError (stack level too deep):
activesupport (3.2.3) lib/active_support/callbacks.rb:409

Here my problem comes from:

I have A B C D 4 models.
After A created, in its after_create callback, I create B C D once for each. B C D all have before_save method, but when D was creating, it crushed after before_save executed. Then this error appeared.

I am a rookie of rails & redis. Anyone help me ? Thx :)

Using Ohm::DataTypes still results in unitialized constant error

Adding 'include Ohm::DataTypes now gives a different error:

class Period < Ohm::Model
include Ohm::DataTypes

attribute :period_name
attribute :period_start, Date
attribute :period_end, Date
attribute :period_earn, Decimal
end

yields:
bastion.rb:78:in `class:Period': uninitialized constant Period::Decimal (NameError)

Gem versions for local development

Hello, what is the versions of the gem that you are using?

I have an issue when wanting to run the tests:

$ rake
...............

Cutest::AssertionFailed: [#<B:0x007fb9cb05b978 @attributes={}, @_memo={}, @errors={}, @id="1">, #<B:0x007fb9cb055c30 @attributes={}, @_memo={}, @errors={}, @id="2">] != []

- assert_equal [b1, b2], B.all.sort.to_a test/connection.rb +84

Thanks

New version?

There appears to have been lots of work done in the last year, is there a reason why a new version hasn't been pushed out?

assert_unique treats nil as valid

Using the following model:

class Service < Ohm::Model
  include Ohm::Typecast
  include Ohm::Timestamping
  include Ohm::Callbacks
  include Ohm::ExtraValidations

  attribute :name
  attribute :status
  reference :host, Host

  index :name
  index :status

  def validate
    assert_present :name
    assert_present :status
    assert_unique [:name, :host_id]
    assert_member :status, ["up", "down", "pending"]
  end
end

results in:

>> s = Service.create(:name => 'someservice', :status => 'up')
>> s.valid?
=> true

It would be expected that name + nil isn't valid and should push to s.errors. In the interim, I can make :host_id a required field.

Chaining searches fails on third find...

First off, great library, its been so solid up till this point I had spent an hour looking for the bug in my code since I assumed it couldn't be in ohm. Well there appears to be a bug when doing a series of finds on the same set of data. The first and second searches work fine, the third one always returns an empty set. It doesn't seem to matter what order you do it in, or what the index is on. I have mad a test case on gist that shows the issue.

http://gist.github.com/339731

Accessing lists and sets before save

I'm working on a messaging app and utilizing callbacks to publish all new messages to a set of channels after they're create. I want to use an after_create callback to trigger the notification-

class RealTime::Message < Ohm::Model
include Ohm::Callbacks

set :channels, RealTime::Channel
after :create, :publish

...
end

Ideally I would instantiate a new Message, set the channels, call save and have my after_create notify all the channels. But since I can't set channels, this pattern falls. Here is what I did to work around it:

def _channels
    if new?
        @new_channels ||= []
    else
        channels 
    end
end

Now I always refer to my _channels variabel which falls back to an in-memory instance variable until the values are persisted to redis. I have another after_create that adds @new_channels to the set channels and persists them. This way my callback pattern works out fine.

Any reason that this type of functionality is missing from Ohm by default? I'm assuming there's some scenario where this is a bad idea otherwise you'd have already added it?

Support the Redis::Namespace

We are using Ohm with rails that acts in different envs e.g. development and test.
In this case we need to support the namespace for all Ohm models.
Like:

Ohm.connect(:host  :port ..., :namespace => "my_env")

list/set support

I noticed in master branch, the list/set support is not really there... List isn't implemented and for sets you can only do a limited number of actions (add but not delete). Are you guys planning on adding these back in?

Thanks,
Chris

What migration strategy should be used with Ohm?

The standard problem. I have "production" data store that is currently syncronized with "development". Than I change the model. How should I update all records on "production" data store to make them syncronized with new model?

How about NOT mandatory indices?

Hi,

first of all, thank you for Ohm! :) It rocks.
Maybe you can help me understanding something:

I have two models, one is saved to an MySQL storage, handeled by active record, and one is saved to an Redis store handeled by Ohm. Model A (User) has a to many relation to Model B (Venue) and Model B has a to one relation with Model A.

User (ActiveRecord/MySQL)
def venues
Venue.find :user_id => self.id
end

Venue (Ohm/Redis)
index :user
reference :user, lambda { |id| User.find(id) }

That seem to work, but I got a problem if a venue doesnt have a relation to a user because it is not owned by one... In that case i get the error "Couldn't find User without an ID".

Do you know something about that?

Best regards and thank you in progress,

hijolan

list and set operations applied even if object is invalid

I noticed that list and set operations are effectively committed immediately.

Consider the case where an object has a list that is updated with a "push", then an attribute is updated some invalid value, and the user tries to save the object. The validation fails yet the list is still left updated.

0.23 relies on String#force_encoding breaking ruby 1.8 compatability

line 243 of redis.rb:

reply = @sock.read(bulklen).force_encoding("UTF-8")

same problem affected Mechanize and they just skipped it for ruby 1.8, simple hacky workaround:

reply = @sock.read(bulklen)
reply = reply.force_encoding("UTF-8") if reply.respond_to?(:force_encoding)

Ohm.connect changes its argument

I almost think this is my illusion.

puts DB_SETTINGS.to_yaml

# Connect to redis database.
Ohm.connect(DB_SETTINGS)
Ohm.redis.info

puts DB_SETTINGS.to_yaml

The output is


---
:host: localhost
:port: 6379
:db: 15

---
:host: 127.0.0.1
:port: 6379
:db: 0
:password:

I am using Ohm latest version (0.1.3).

Missing version file on master

When I take the latest from master and try to run bundle update I get:

There was a LoadError while evaluating ohm.gemspec:
no such file to load -- ./lib/ohm/version from
/Users/chris/.rvm/gems/ruby-1.9.2-p290/bundler/gems/ohm-e301c56e604f/ohm.gemspec:1:in `<main>'

because ohm.gemspec is referencing a non-existent file require "./lib/ohm/version"

is this normal?

irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'ohm'
=> true
irb(main):003:0> Ohm.connect
=> nil
irb(main):004:0> class Atom < Ohm::Model
irb(main):005:1> attribute :num
irb(main):006:1> attribute :rgb
irb(main):007:1> end
=> #Proc:0xb7465ee4@/var/lib/gems/1.8/gems/ohm-1.0.1/lib/ohm.rb:1034

system intel atom & ubuntu 12.04

Collection needs [] methods

I have been using this monkey patch so I can iterate over an index or just grab the first entry. Is there something dangerous about this code?

Great job BTW this just made using redis trivial

module Ohm
  module Attributes
    class Collection
      def [](id)
        model ? model[raw[id]] : raw[id]
      end
    end
  end
end

Updates inside thread - v1.0.0

Hi,

I came across an issue while making the leap from 0.1.5 to 1.0.0.

Basically and before going into details, here is a short description of the issue:

If I create a new entity on my root thread, and inside a thread I load it and update its data, I get lots of instability afterwards.

Here is a more detailed explication:

Model.create(:hash => "123", :data => "1")

Thread.new do
    a = Model.find(:hash => "123").first
    a.update(:data => "2")
end

b = Model.find(:hash => "123").first

From now on, every time I call b.data I sometimes get the data back and sometimes I get nil back. Here is an example from my code:

irb(main):060:0> a = $root.model.export.find(:hash => "a8efcfc706a9c35f525fff8f2c98b721").first
=> #<Root::Model::Export:0x17d66064 @_memo={}, @id="44", @attributes={:export_format=>"csv",  :hash=>"a8efcfc706a9c35f525fff8f2c98b721", :stop_export=>"0", :status=>"{\"message\":\"queued\",\"result_count\":-1,\"current_id\":-1}", :end_time=>"1335888859.28", :start_time=>"1335888859.28", :created_at=>"1335888859", :updated_at=>"1335888883", :data=>"ceasdddd"}>
irb(main):061:0> a = $root.model.export.find(:hash => "a8efcfc706a9c35f525fff8f2c98b721").first
=> #<Root::Model::Export:0x37de2e49 @_memo={}, @id="44", @attributes={:export_format=>"csv", :hash=>"a8efcfc706a9c35f525fff8f2c98b721", :stop_export=>"0", :status=>"{\"message\":\"queued\",\"result_count\":-1,\"current_id\":-1}", :end_time=>"1335888859.28", :start_time=>"1335888859.28", :created_at=>"1335888859", :updated_at=>"1335888883", :data=>"ceasdddd"}>
irb(main):062:0> a = $root.model.export.find(:hash => "a8efcfc706a9c35f525fff8f2c98b721").first
=> nil
irb(main):063:0> a = $root.model.export.find(:hash => "a8efcfc706a9c35f525fff8f2c98b721").first
=> nil
irb(main):064:0> a = $root.model.export.find(:hash => "a8efcfc706a9c35f525fff8f2c98b721").first
=> #<Root::Model::Export:0x1057aa43 @_memo={}, @id="44", @attributes={:export_format=>"csv", :hash=>"a8efcfc706a9c35f525fff8f2c98b721", :stop_export=>"0", :status=>"{\"message\":\"queued\",\"result_count\":-1,\"current_id\":-1}", :end_time=>"1335888859.28", :start_time=>"1335888859.28", :created_at=>"1335888859", :updated_at=>"1335888883", :data=>"ceasdddd"}>

I have also tried doing this on the same Thread and I don't get the same behaviour.

Any ideas of what might be causing this? I'm running on JRuby with :thread_safe = true.

helper method in rails failed

Model

class Post < Ohm::Model
attribute :title
attribute :content
attribute :tags
include Ohm::ActiveModelExtension
include Ohm::Timestamping
...
End

In controller

GET /posts/new

GET /posts/new.xml

def new
@post = Post.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @post }
end
end

In view, just use the generated one.

<%= form_for(@post) do |f| %>
....

<%= f.text_field :title %> or
<%#= text_field(:post, :title, :size => 20) %>

Error message
undefined method `title' for #Ohm::ActiveModelInterface:0xb6ad8c0

I did not found any other has such error in Google or here.
I can not found out what's the problem caused the title not found.

See about nested transactions

For the transaction mechanism to be more useful, there needs to be support for nesting, e.g. a transactional update of object A invokes a transactional update of object B. Further, the read phase of the transaction on A may require fetching B but this is not known until the A.t.read proc executes, generating the B.t.read proc. So, do you have a plan for how to compose and manage transactions in this situation, while keeping the code for A and B (and C etc.) orthogonal, given that Redis MULTI/EXEC does not nest?

Performance in retrieving large amount of values

Hi there,

One of the functionality of our software is to cache chemical metadata. We have at the moment something like 60 million chemicals, however they're not stored on redis. Redis in this case is just being used as a caching layer for faster retrieval and indexing.
We're using ohm-expire to control the ttl of the chemicals.

The way we're using to retrieve all of the chemicals (in a batch) is the following:

query = [1,2,3,4,5,6,7,8,9,10] #for example but it could be 1000 elements
i = 0
    query.each do |id|
    found_chemical_array = (i == 0 ? self.find(:chemical_id => id) : found_chemical_array.union(:chemical_id => id))
    i += 1
end

Am I doing this right? - Just asking because it takes around 7 seconds to retrieve 2000 elements.

Thank you!

Storing Binary files

I'd like to store small images (10-500K) as values in my datastore. Is this possible with Ohm?

Upgrade script

I know it is written in the README that there will be an upgrade script and that it is not yet available. I am basically opening this ticket to allow people to follow progress on that front.

Ohm::Model.const_missing doesn't work with namespaces

Ohm currently wraps undefined constants to defer evaluation until the constant has been defined, and accomplishes this using ohm::Model.const_missing. However, the wrapping in const_missing uses Object.const_get to evaluate the constant when unwrapping it later on. This works fine when your classes live in the top level namespace, however doesn't work as expected when your classes are inside of some other namespace (for example see ohm-namespace-orig.rb in this gist )

The problem occurs because the behaviour of Object.const_get is not consistent with what happens when the interpreter looks up evaluated constants. Consider:

module A
   class B
    puts B.inspect
    puts const_get(:B).inspect
  end
end

const_get(:B) fails, as :B is not defined in A::B's namespace or the top level. However, simply evaluating 'B' works just fine, because the interpreter accounts for the fact that your inside this nested namespace and will look up B in A and get you A::B (which is what we all expect).

To solve this problem in Ohm, we can simply change the const_get in Ohm::Model.const_missing to an eval (see ohm-namespace.rb in the gist ). I'll do some more tests to make sure this gets the job done in similar scenarios.

Phil

warning: already initialized constant RedisTimer

If Ohm is used with the Redis gem, or gem that requires it, "warning: already initialized constant RedisTimer" is given:

>> require 'ohm'
=> true
>> require 'redis'
/usr/local/lib/ruby/gems/1.8/gems/redis-0.1/lib/redis.rb:10: warning: already initialized constant RedisTimer
=> true

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.