Skip to content

Commit

Permalink
Merge 451094a into 3e2fc3f
Browse files Browse the repository at this point in the history
  • Loading branch information
i2bskn committed Oct 12, 2015
2 parents 3e2fc3f + 451094a commit e449097
Show file tree
Hide file tree
Showing 14 changed files with 334 additions and 139 deletions.
13 changes: 13 additions & 0 deletions CODE_OF_CONDUCT.md
@@ -0,0 +1,13 @@
# Contributor Code of Conduct

As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.

We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.

Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.

This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
7 changes: 7 additions & 0 deletions bin/console
@@ -0,0 +1,7 @@
#!/usr/bin/env ruby

require "bundler/setup"
require "findable"
require "pry"

Pry.start
7 changes: 7 additions & 0 deletions bin/setup
@@ -0,0 +1,7 @@
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

bundle install

# Do any other automated setup that you need to do here
2 changes: 2 additions & 0 deletions findable.gemspec
Expand Up @@ -21,6 +21,8 @@ Gem::Specification.new do |spec|
spec.add_dependency "activesupport"
spec.add_dependency "activemodel"
spec.add_dependency "redis"
spec.add_dependency "redis_eval"
spec.add_dependency "msgpack"

spec.add_development_dependency "bundler"
spec.add_development_dependency "rake"
Expand Down
6 changes: 5 additions & 1 deletion lib/findable.rb
@@ -1,10 +1,14 @@
require "json"
require "pathname"
require "bigdecimal"

require "active_support"
require "active_support/core_ext"
require "active_model"
require "redis"
require "redis_eval"
require "msgpack"

RedisEval.config.script_paths = [File.expand_path("../findable/script", __FILE__)]

require "findable/version"
require "findable/errors"
Expand Down
2 changes: 0 additions & 2 deletions lib/findable/configuration.rb
Expand Up @@ -2,7 +2,6 @@ module Findable
class Configuration
VALID_OPTIONS = [
:redis_options,
:serializer,
:seed_dir,
:seed_file,
].freeze
Expand All @@ -28,7 +27,6 @@ def merge!(params)

def reset
self.redis_options = nil
self.serializer = JSON
self.seed_dir = defined?(Rails) ? Rails.root.join("db", "findable_seeds") : nil
self.seed_file = defined?(Rails) ? Rails.root.join("db", "findable_seeds.rb") : nil
end
Expand Down
137 changes: 42 additions & 95 deletions lib/findable/query.rb
@@ -1,7 +1,6 @@
require "findable/query/connection"
require "findable/query/namespace"
require "findable/query/lock"
require "findable/query/serializer"

module Findable
class Query
Expand All @@ -10,13 +9,12 @@ class Query

attr_reader :model

def initialize(model, serializer = nil)
def initialize(model)
@model = model
@serializer = serializer || Serializer.new
end

def all
@serializer.deserialize(redis.hvals(data_key), model)
deserialize(redis.hvals(data_key), model)
end

def ids
Expand All @@ -28,72 +26,27 @@ def count
end

def find_by_ids(ids)
@serializer.deserialize(redis.hmget(data_key, *Array(ids)), model)
end

def find_by_index(index, value)
if ids = ids_from_index([index, value].join(":"))
find_by_ids(ids)
end
deserialize(redis.hmget(data_key, *Array(ids)), model)
end

def exists?(id)
redis.hexists(data_key, id)
end

def insert(object)
lock do
object.id = auto_incremented_id(object.id)
redis.hset(
data_key,
object.id,
@serializer.serialize(object.attributes)
)
update_index(object)
end
att = script_insert(Array(object.attributes)).first
object.attributes.merge!(att)
object
end

def import(hashes)
lock do
indexes = Hash.new {|h, k| h[k] = [] }
values = hashes.each_with_object([]) do |hash, obj|
hash = hash.with_indifferent_access
hash["id"] = auto_incremented_id(hash["id"])
obj << hash["id"]
obj << @serializer.serialize(hash)

if model.index_defined?
model.indexes.each_with_object([]) do |name, obj|
next if name == :id
indexes[[name, hash[name]].join(":")] << hash["id"]
end
end
end
redis.hmset(data_key, *values)
if indexes.present?
attrs = indexes.map {|k, v| [k, @serializer.serialize(v)] }.flatten
redis.hmset(index_key, *attrs)
end
end
end

def delete(object)
if model.index_defined?
model.indexes.each do |name|
next if name == :id
if value = object.public_send("#{name}_was") || object.public_send(name)
redis.hdel(index_key, value)
end
end
end

redis.hdel(data_key, object.id)
def delete(objects)
delete_ids = Array(objects).map(&:id)
script_delete(delete_ids)
end

def delete_all
redis.multi do
[data_key, info_key, index_key].each {|key| redis.del(key) }
eval_keys.each {|key| redis.del(key) }
end
end

Expand All @@ -102,50 +55,44 @@ def lock
Lock.new(lock_key, thread_key).lock { yield }
end

def update_index(object)
if model.index_defined?
indexes = model.indexes.each_with_object([]) {|name, obj|
next if name == :id || object.public_send("#{name}_changed?")

if old_value = object.public_send("#{name}_was")
old_index_key = [name, old_value].join(":")

if (old_ids = ids_from_index(old_index_key)).present?
new_ids = old_ids.reject {|id| id == object.id }
if new_ids.empty?
redis.hdel(index_key, old_index_key)
else
obj << old_index_key
obj << @serializer.serialize(new_ids)
end
end
end

obj << [name, object.public_send(name)].join(":")
obj << object.id
}
redis.hmset(index_key, *indexes)
end
# Lua Script API

# Insert and update data of Findable.
#
# @param hashes [Array<Hash>] Attributes
# @return [Array<Hash>]
def script_insert(hashes)
eval_arguments = hashes.map(&:to_msgpack)
RedisEval.insert.execute(eval_keys, eval_arguments).map {|packed|
MessagePack.unpack(packed)
}
end
alias_method :import, :script_insert

# Delete data of Findable with index.
#
# @param ids [Array<Integer>] Delete target ids.
# @return [Integer] Deleted count.
def script_delete(ids)
RedisEval.delete.execute(eval_keys, Array(ids))
end

private
def auto_incremented_id(id)
if id.present?
current = redis.hget(info_key, AUTO_INCREMENT_KEY).to_i
id = id.to_i
if id > current
redis.hset(info_key, AUTO_INCREMENT_KEY, id)
end
id
else
redis.hincrby(info_key, AUTO_INCREMENT_KEY, 1)
end
def eval_keys
[
data_key,
info_key,
model.secondary_indexes.map {|idx| index_key(idx) },
].flatten
end

def ids_from_index(index_name)
if ids = redis.hget(index_key, index_name)
@serializer.deserialize(ids)
end
def deserialize(raw_data, klass = nil)
objects = Array(raw_data).compact.map {|data|
object = MessagePack.unpack(data)
object = object.with_indifferent_access if object.is_a?(Hash)
klass ? klass.new(object) : object
}
raw_data.is_a?(String) ? objects.first : objects
end
end
end
21 changes: 13 additions & 8 deletions lib/findable/query/namespace.rb
@@ -1,16 +1,21 @@
module Findable
class Query
module Namespace
PREFIX = "findable".freeze
KEY_NAMES = %i(info data lock index).freeze
AUTO_INCREMENT_KEY = :auto_increment
PREFIX = "findable"
META_NAMES = %i(info lock thread)
DELIMITER = ":"

KEY_NAMES.each do |name|
META_NAMES.each do |name|
define_method([name, "key"].join("_")) { namespaces[name] }
end

def thread_key
[PREFIX, basename].join("_")
def data_key
@_data_key ||= [PREFIX, basename].join(DELIMITER)
end

def index_key(column)
@_index_base ||= [data_key, "index"].join(DELIMITER)
[@_index_base, column].join(DELIMITER)
end

private
Expand All @@ -20,8 +25,8 @@ def basename

# @return [Hash] namespaces
def namespaces
@_namespaces ||= KEY_NAMES.each_with_object({}) {|key, obj|
obj[key] = [PREFIX, basename, key].join(":")
@_namespaces ||= META_NAMES.each_with_object({}) {|key, obj|
obj[key] = [data_key, key].join(DELIMITER)
}
end
end
Expand Down
26 changes: 0 additions & 26 deletions lib/findable/query/serializer.rb

This file was deleted.

4 changes: 4 additions & 0 deletions lib/findable/schema.rb
Expand Up @@ -21,6 +21,10 @@ def indexes
@_indexes ||= [:id]
end

def secondary_indexes
indexes.select {|name| name != :id }
end

def index_defined?
indexes.size > 1
end
Expand Down

0 comments on commit e449097

Please sign in to comment.