Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
226 lines (186 sloc) 6.76 KB
require 'rubygems'
require 'dm-core'
require 'tokyocabinet'
include TokyoCabinet
module DataMapper
module Adapters
class TokyoCabinetAdapter < AbstractAdapter
def create(resources)
resource = resources[0]
attributes = resource.attributes
item_id = access_data(resource.model) do |item|
#Getting the latest id
#TODO:Find out how to get last id using FDB, rather than BDB
cur = BDBCUR::new(item)
cur.last
attributes[:id] = cur.key.to_i + 1
item.put(attributes[:id], Marshal.dump(attributes))
attributes[:id]
end
resource.instance_variable_set(:@id, item_id)
add_index(attributes, resource, item_id)
# Seems required to return 1 to update @new_record instance variable at DataMapper::Resource.
# Not quite sure how it works.
1
end
def read_many(query)
results = parse_query(query)
if results
results = results.sort_by do |result|
result[query.order.first.property.name] if result
end
results = results.reverse if query.order.first.direction == :desc
end
if results # to handle results == nil
Collection.new(query) do |collection|
results.each do |result|
data = map_into_query_field(query, result)
if data # to handle results == [nil]
collection.load(data)
end
end
end
else
[]
end
end
def read_one(query)
results = parse_query(query)
data = (results.class == Array ? results.first : results)
if data
data = map_into_query_field(query, data)
query.model.load(data, query)
end
end
def update(attributes, query)
item_id = get_id(query)
old_attributes = get_items_from_id(query, item_id)
delete_index(old_attributes, query, item_id)
# Converting {#<Property:User:name>=>"peter", #<Property:User:age>=>22} to {:age=>22, :name=>"peter"}
new_attributes = attributes.inject({}){|total,current| total[current[0].name] = current[1]; total}
add_index(new_attributes, query, item_id)
access_data(query.model) do |item|
raw_data = item.get(item_id)
if raw_data
record = Marshal.load(raw_data)
attributes.each do |key, value|
record[key.name.to_sym] = value
end
item.put(item_id, Marshal.dump(record))
end
end
# Seems required to return 1 to update @new_record instance variable at DataMapper::Resource.
# Not quite sure how it works.
1
end
def delete(query)
item_id = get_id(query)
attributes = get_items_from_id(query, item_id)
delete_index(attributes, query, item_id)
access_data(query.model) do |item|
item.out(item_id)
end
# Seems required to return 1 to update @new_record instance variable at DataMapper::Resource.
# Not quite sure how it works.
1
end
private
# Access Index file if property is given. If not, access data file
def access_data(model, property = nil, &block)
item = BDB::new
attribute = property.to_s.capitalize if property
item.open(data_path + "#{model}#{attribute}.bdb", BDB::OWRITER | BDB::OCREAT)
result = yield(item)
item.close
result
end
def data_path
data_path = DataMapper.repository.adapter.uri[:data_path].to_s + "/"
end
def get_id(query)
unless query.conditions.empty?
query.conditions.first.last
end
end
def get_items_from_id(query, values)
result = []
values_in_array = (values.class == Array ? values : [values])
access_data(query.model) do |item|
result = values_in_array.map do |value|
raw_data = item.get(value)
if raw_data
Marshal.load(raw_data)
end
end
end
values.class == Array ? result : result.first
end
def parse_query(query)
results = []
unless query.conditions.empty?
operator, property, value = query.conditions.first
if property.name == :id # Model.get
results = get_items_from_id(query, value)
else # Model.first w argument
case operator
when :eql
then
item_ids = access_data(query.model, property.name) do |item|
value = value.first if value.class == Array
item.getlist(value)
end
when :not # TODO: Think about better way to extract, as this is going through data one by one
then NotImplementedError{"The below code is not working as order is not always correct"}
else
raise NotImplementedError("#{operator} is not implmented yet")
end
results = get_items_from_id(query, item_ids)
end
else # Model.all w/o argument
access_data(query.model) do |item|
#Getting the first id
#TODO:Find out how to get first id using FDB, rather than BDB
raw_data = BDBCUR::new(item)
if raw_data.first
while key = raw_data.key
results << Marshal.load(raw_data.val)
raw_data.next
end
end
end
end
results
end
def map_into_query_field(query, data)
if data
query.fields.map do |property|
data[property.field.to_sym]
end
end
end
def delete_index(attributes, query, item_id)
# Don't need id attribute and attribut with no data.
attributes.reject{|k,v| k == :id || v == nil}.each do | k, v|
access_data(query.model, k) do |item|
items = item.getlist(v)
items = items - [item_id]
item.out(v)
if items.size > 0
item.putlist(v, items)
end
end
end
end
def add_index(attributes, query, item_id)
attributes.each do |key, value|
# Creating index for each attributes except id
unless key == :id
access_data(query.model, key) do |item|
item.putlist(value, [item_id])
end
end
end
end
end # class AbstractAdapter
end # module Adapters
end # module DataMapper