dm-tokyo-adapter 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .idea/
5
+ vendor/
6
+ Gemfile.lock
7
+ test/tc/*
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 "Shane Hanna"
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,86 @@
1
+ = DataMapper Tokyo Cabinet/Tyrant Table Store Adapter
2
+
3
+ * http://github.com/shanna/dm-tokyo-adapter/tree/master
4
+
5
+ == Description
6
+
7
+ A DataMapper Tokyo Cabinet/Tyrant table store adapter.
8
+
9
+ === Table Store
10
+
11
+ http://tokyocabinet.sourceforge.net/spex-en.html#features_tctdb
12
+
13
+ The Tokyo Cabinet table storage engine doesn't require a predefined schema and as such properties in your resource are
14
+ only used for by the adapter for typecasting. There is no need to migrate your resource when you create, update or
15
+ delete properties.
16
+
17
+ == Dependencies
18
+
19
+ Ruby::
20
+ * dm-core ~> 0.10.0
21
+ * rufus-tokyo ~> 0.1.12
22
+
23
+ == Install
24
+
25
+ * Via gem:
26
+
27
+ gem install shanna-dm-tokyo-adapter -s http://gems.github.com
28
+
29
+ * Via git:
30
+
31
+ git clone git://github.com/shanna/dm-tokyo-adapter.git
32
+ rake install
33
+
34
+ == Synopsis
35
+
36
+ # Tokyo Cabinet DB files will be located in #{path}/#{database}/#{resource}.tdb
37
+ DataMapper.setup(:default,
38
+ :adapter => 'tokyo_cabinet',
39
+ :database => 'tc',
40
+ :path => File.dirname(__FILE__)
41
+ )
42
+
43
+ # Tokyo Tyrant connection.
44
+ DataMapper.setup(:default,
45
+ :adapter => 'tokyo_tyrant',
46
+ :host => 'localhost',
47
+ :port => '1978'
48
+ )
49
+
50
+ # Define your DataMapper resource and start saving:
51
+ class User
52
+ include DataMapper::Resource
53
+ property :id, Serial
54
+ property :name, String
55
+ property :age, Integer
56
+ end
57
+
58
+ # No need to (auto_)migrate!
59
+ User.create(:name => 'Fred', :age => '25')
60
+
61
+ # Conditions:
62
+ users = User.all(:age.gte => 10, :limit => 20, :order => [:age.asc])
63
+
64
+ == TODO
65
+
66
+ * Documentation. It's undocumented at the moment.
67
+ * Give access to the <tt>Rufus::Tokyo::Table</tt> object through the adapter. Handy if you want to add indexes and
68
+ other things that can't be done through the DataMapper API.
69
+ * Better tests. I haven't really tested all the DM primitives and query operators yet.
70
+ * Better typecasting. DataTime and Time should typecast to Integer so that they can be searched using the numeric
71
+ operators.
72
+
73
+ Ideally in the future I'd like to contribute to these broader goals:
74
+
75
+ * All the TokyoCabinet stores equally supported in DM.
76
+ * DataMapper define a public/semipublic API for key => value and search stores through Moneta (memcachedb, memcacheq,
77
+ couchdb, mtokyo cabinet bdb, etc.)
78
+ * DataMapper per adapter query operators. You can't always shoehorn everything into an SQL mindset.
79
+
80
+ == Contributing
81
+
82
+ Go nuts. Just send me a pull request (github or otherwise) when you are happy with your code.
83
+
84
+ == Copyright
85
+
86
+ Copyright (c) 2009 "Shane Hanna". See LICENSE for details.
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = 'dm-tokyo-adapter'
8
+ gem.summary = %Q{Tokyo Cabinet/Tyrant Table DataMapper Adapter.}
9
+ gem.email = 'shane.hanna@gmail.com'
10
+ gem.homepage = 'http://github.com/shanna/dm-tokyo-adapter'
11
+ gem.authors = ['Shane Hanna']
12
+ gem.files.reject!{|f| f =~ /\.tdb$/}
13
+ gem.add_dependency 'dm-core', '~> 0.10.0'
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ rescue LoadError
17
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
18
+ end
19
+
20
+ require 'rake/rdoctask'
21
+ Rake::RDocTask.new do |rdoc|
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = 'dm-tokyo-cabinet-adapter'
24
+ rdoc.options << '--line-numbers' << '--inline-source'
25
+ rdoc.rdoc_files.include('README*')
26
+ rdoc.rdoc_files.include('lib/**/*.rb')
27
+ end
28
+
29
+ require 'rake/testtask'
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs << 'lib' << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+
36
+ task :default => :test
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 4
4
+ :patch: 0
@@ -0,0 +1,62 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{dm-tokyo-adapter}
5
+ s.version = "0.4.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Joshua Partogi", "Shane Hanna"]
9
+ s.date = %q{2010-10-14 2009-08-09}
10
+ s.email = %q{joshua.partogi@gmail.com shane.hanna@gmail.com}
11
+ s.extra_rdoc_files = [
12
+ "LICENSE",
13
+ "README.rdoc"
14
+ ]
15
+ s.files = [
16
+ ".gitignore",
17
+ "LICENSE",
18
+ "README.rdoc",
19
+ "Rakefile",
20
+ "VERSION.yml",
21
+ "dm-tokyo-adapter.gemspec",
22
+ "lib/dm-tokyo-adapter.rb",
23
+ "lib/dm-tokyo-adapter/cabinet.rb",
24
+ "lib/dm-tokyo-adapter/query.rb",
25
+ "lib/dm-tokyo-adapter/tyrant.rb",
26
+ "lib/dm-tokyo-adapter/version.rb",
27
+ "test/helper.rb",
28
+ "test/test_cabinet.rb",
29
+ "test/test_query.rb",
30
+ "test/test_tyrant.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/scrum8/dm-tokyo-adapter}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.4}
36
+ s.summary = %q{Tokyo Cabinet/Tyrant Table DataMapper Adapter.}
37
+ s.test_files = [
38
+ "test/helper.rb",
39
+ "test/test_cabinet.rb",
40
+ "test/test_query.rb",
41
+ "test/test_tyrant.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<dm-core>, [">= 1.0.2"])
50
+ s.add_dependency(%q<rufus-tokyo>, ">= 1.0.7")
51
+ s.add_dependency(%q<addressable>, ">= 2.2.2")
52
+ else
53
+ s.add_dependency(%q<dm-core>, [">= 1.0.2"])
54
+ s.add_dependency(%q<rufus-tokyo>, ">= 1.0.7")
55
+ s.add_dependency(%q<addressable>, ">= 2.2.2")
56
+ end
57
+ else
58
+ s.add_dependency(%q<dm-core>, [">= 1.0.2"])
59
+ s.add_dependency(%q<rufus-tokyo>, ">= 1.0.7")
60
+ s.add_dependency(%q<addressable>, ">= 2.2.2")
61
+ end
62
+ end
@@ -0,0 +1,12 @@
1
+ require 'extlib'
2
+ require 'fileutils'
3
+
4
+ require 'dm-core'
5
+
6
+ require 'rufus/tokyo'
7
+ require 'rufus/tokyo/tyrant'
8
+
9
+ require 'dm-tokyo-adapter/query'
10
+ require 'dm-tokyo-adapter/cabinet'
11
+ require 'dm-tokyo-adapter/tyrant'
12
+ require 'dm-tokyo-adapter/version'
@@ -0,0 +1,90 @@
1
+ module DataMapper
2
+ module Adapters
3
+ module Tokyo
4
+
5
+ # A DataMapper Tokyo Cabinet table store adapter.
6
+ #
7
+ # http://tokyocabinet.sourceforge.net/spex-en.html#features_tctdb
8
+ #
9
+ # The Tokyo Cabinet table storage engine doesn't require a predefined schema and as such properties in your
10
+ # resource are only used by the adapter for typecasting. There is no need to migrate your resource when you
11
+ # create, update or delete properties.
12
+ #
13
+ # == See
14
+ #
15
+ # DataMapper::Adapters::Tokyo::Query:: Table Query.
16
+ class CabinetAdapter < AbstractAdapter
17
+ def create(resources)
18
+ resources.map do |resource|
19
+ model = resource.model
20
+
21
+ with_connection(resource.model) do |connection|
22
+ initialize_serial(resource, connection.generate_unique_id)
23
+ connection[key(resource)] = attributes(resource, :field)
24
+ end
25
+ end.size
26
+ end
27
+
28
+ def read(query)
29
+ with_connection(query.model) do |connection|
30
+ Tokyo::Query.new(connection, query).read
31
+ end
32
+ end
33
+
34
+ def update(attributes, collection)
35
+ with_connection(collection.query.model) do |connection|
36
+ collection.each do |record|
37
+ connection[key(record)] = attributes(record, :field)
38
+ end.size
39
+ end
40
+ end
41
+
42
+ def delete(collection)
43
+ with_connection(collection.query.model) do |connection|
44
+ collection.each do |record|
45
+ connection.delete(key(record))
46
+ end.size
47
+ end
48
+ end
49
+
50
+ protected
51
+ def create_connection(model)
52
+ @tdb_path ||= FileUtils.mkdir_p(
53
+ File.join(*[@options[:path], (@options[:host] || @options[:database])].compact)
54
+ ).first
55
+ tdb = File.join(@tdb_path, "#{model.base_model.storage_name(name)}.tdb")
56
+ Rufus::Tokyo::Table.new(tdb)
57
+ end
58
+
59
+ def close_connection(connection)
60
+ connection.close
61
+ end
62
+
63
+ private
64
+ def key(resource)
65
+ key = resource.key
66
+ (key.size > 1 ? key.join(':') : key.first).to_s
67
+ end
68
+
69
+ def attributes(resource, key_on = :name)
70
+ resource.attributes(key_on).map{|k, v| [k, v.to_s]}.to_hash
71
+ end
72
+
73
+ def with_connection(model)
74
+ begin
75
+ connection = create_connection(model)
76
+ return yield(connection)
77
+ rescue => error
78
+ DataMapper.logger.error(error.to_s)
79
+ raise error
80
+ ensure
81
+ close_connection(connection) if connection
82
+ end
83
+ end
84
+ end # CabinetAdapter
85
+ end # Tokyo
86
+
87
+ TokyoCabinetAdapter = Tokyo::CabinetAdapter
88
+ const_added(:TokyoCabinetAdapter)
89
+ end # Adapters
90
+ end # DataMapper
@@ -0,0 +1,141 @@
1
+ module DataMapper
2
+ module Adapters
3
+ module Tokyo
4
+
5
+ # Query a Tokyo Cabinet table store with a DataMapper query.
6
+ #
7
+ # == Notes
8
+ #
9
+ # Query conditions not supported natively by TC's table query will fall back to DM's in-memory query
10
+ # filtering. This may impact performance.
11
+ class Query
12
+ include Extlib::Assertions
13
+ include DataMapper::Query::Conditions
14
+
15
+ def initialize(connection, query)
16
+ assert_kind_of 'connection', connection, Rufus::Tokyo::Table
17
+ assert_kind_of 'query', query, DataMapper::Query
18
+ @connection, @query, @native = connection, query, []
19
+ end
20
+
21
+ #--
22
+ # TODO: connection[] if I have everything I need to fetch by the primary key.
23
+ def read
24
+ records = @connection.query do |statements|
25
+ if @query.conditions.kind_of?(OrOperation)
26
+ fail_native("Operation '#{@query.conditions.slug}'.")
27
+ else
28
+ @query.conditions.each do |condition|
29
+ condition_statement(statements, condition)
30
+ end
31
+ end
32
+
33
+ if native? && !@query.order.empty?
34
+ sort_statement(statements, @query.order)
35
+ end
36
+
37
+ statements.limit(@query.limit) if native? && @query.limit
38
+ statements.no_pk
39
+ end
40
+
41
+ records.each do |record|
42
+ @query.fields.each do |property|
43
+ field = property.field
44
+ record[field] = property.typecast(record[field])
45
+ end
46
+ end
47
+
48
+ return records if native?
49
+ # TODO: Move log entry out to adapter sublcass and use #name?
50
+ DataMapper.logger.warn(
51
+ "TokyoAdapter: No native TableQuery support for conditions: #{@native.join(' ')}"
52
+ )
53
+ @query.filter_records(records)
54
+ end
55
+
56
+ def native?
57
+ @native.empty?
58
+ end
59
+
60
+ private
61
+ def condition_statement(statements, conditions, affirmative = true)
62
+ case conditions
63
+ when AbstractOperation then operation_statement(statements, conditions, affirmative)
64
+ when AbstractComparison then comparison_statement(statements, conditions, affirmative)
65
+ end
66
+ end
67
+
68
+ def operation_statement(statements, operation, affirmative = true)
69
+ case operation
70
+ when NotOperation then condition_statement(statements, operation.first, !affirmative)
71
+ when AndOperation then operation.each{|op| condition_statement(statements, op, affirmative)}
72
+ else fail_native("Operation '#{operation.slug}'.")
73
+ end
74
+ end
75
+
76
+ def comparison_statement(statements, comparison, affirmative = true)
77
+ subject = comparison.subject
78
+ primitive = subject.primitive unless subject.kind_of?(DataMapper::Associations::Relationship)
79
+ value = comparison.value.kind_of?(DataMapper::Resource) ?
80
+ comparison.value[comparison.value.class.serial.name] :
81
+ comparison.value
82
+
83
+ if subject.is_a?(DataMapper::Associations::ManyToOne::Relationship)
84
+ statements.add(subject.child_key.first.name, :numeq, quote_value(value), affirmative)
85
+ return
86
+ end
87
+
88
+ if subject.is_a?(DataMapper::Associations::OneToMany::Relationship)
89
+ value = comparison.value[subject.child_key.first.name]
90
+ statements.add(subject.target_key.first.name, :numeq, quote_value(value), affirmative)
91
+ return
92
+ end
93
+
94
+ if value.kind_of?(Range) && value.exclude_end?
95
+ operation = BooleanOperation.new(:and,
96
+ Comparison.new(:gte, comparison.property, value.first),
97
+ Comparison.new(:lt, comparison.property, value.last)
98
+ )
99
+ operation_statement(statements, operation, affirmative)
100
+ return
101
+ end
102
+
103
+ operator = case comparison
104
+ when EqualToComparison then primitive == Integer ? :numeq : :eq
105
+ when InclusionComparison then primitive == Integer ? :numoreq : nil
106
+ when RegexpComparison then :regex
107
+ when LikeComparison then :regex
108
+ when GreaterThanComparison then :gt
109
+ when LessThanComparison then :lt
110
+ when GreaterThanOrEqualToComparison then :gte
111
+ when LessThanOrEqualToComparison then :lte
112
+ else fail_native("Comparison #{comparison.slug}'.") && return
113
+ end
114
+
115
+ statements.add(comparison.subject.field, operator, quote_value(value), affirmative)
116
+ end
117
+
118
+ def quote_value(value)
119
+ "#{value}"
120
+ end
121
+
122
+ def sort_statement(statements, conditions)
123
+ fail_native("Multiple (#{conditions.size}) order conditions.") if conditions.size > 1
124
+
125
+ sort_order = conditions.first
126
+ primitive = sort_order.target.primitive
127
+ direction = case sort_order.operator
128
+ when :asc then primitive == Integer ? :numasc : :asc
129
+ when :desc then primitive == Integer ? :numdesc : :desc
130
+ end
131
+
132
+ statements.order_by(sort_order.target.field, direction)
133
+ end
134
+
135
+ def fail_native(why)
136
+ @native << why
137
+ end
138
+ end # Query
139
+ end # Tokyo
140
+ end # Adapters
141
+ end # DataMapper
@@ -0,0 +1,33 @@
1
+ module DataMapper
2
+ module Adapters
3
+ module Tokyo
4
+
5
+ # A DataMapper Tokyo Tyrant table store adapter.
6
+ #
7
+ # http://tokyocabinet.sourceforge.net/tyrantdoc/
8
+ # http://tokyocabinet.sourceforge.net/spex-en.html#features_tctdb
9
+ #
10
+ # The Tokyo Cabinet table storage engine doesn't require a predefined schema and as such properties in your
11
+ # resource are only used by the adapter for typecasting. There is no need to migrate your resource when you
12
+ # create, update or delete properties.
13
+ #
14
+ # == See
15
+ #
16
+ # DataMapper::Adapters::Tokyo::Query:: Table Query.
17
+ class TyrantAdapter < Tokyo::CabinetAdapter
18
+ protected
19
+
20
+ #--
21
+ # TODO: Default port to 1978?
22
+ def create_connection(model)
23
+ credentials = [@options[:socket] || @options.values_at(:host, :port)]
24
+ Rufus::Tokyo::TyrantTable.new(*credentials.flatten)
25
+ end
26
+ end # TyrantAdapter
27
+ end # Tokyo
28
+
29
+ TokyoTyrantAdapter = Tokyo::TyrantAdapter
30
+ const_added(:TokyoTyrantAdapter)
31
+ end # Adapters
32
+ end
33
+
@@ -0,0 +1,7 @@
1
+ module DataMapper
2
+ module Adapters
3
+ module Tokyo
4
+ VERSION = "0.4.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require "bundler/setup"
4
+ require 'shoulda'
5
+
6
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+
8
+ require 'dm-migrations'
9
+ require 'dm-tokyo-adapter'
10
+
11
+ class Test::Unit::TestCase
12
+ end
13
+
14
+ DataMapper::Logger.new(STDOUT, :debug) if $VERBOSE
15
+ DataMapper.setup(:default, {
16
+ :adapter => 'tokyo_cabinet',
17
+ :database => 'tc',
18
+ :path => File.dirname(__FILE__)
19
+ })
20
+
21
+ DataMapper.setup(:sqlite, 'sqlite::memory:')
22
+
23
+ class Test::Unit::TestCase
24
+ include Extlib::Hook
25
+
26
+ # after :teardown do
27
+ def teardown
28
+ descendants = DataMapper::Model.descendants.to_a
29
+ while model = descendants.shift
30
+ descendants.concat(model.descendants.to_a - [ model ])
31
+
32
+ parts = model.name.split('::')
33
+ constant_name = parts.pop.to_sym
34
+ base = parts.empty? ? Object : Object.full_const_get(parts.join('::'))
35
+
36
+ if base.const_defined?(constant_name)
37
+ base.send(:remove_const, constant_name)
38
+ end
39
+
40
+ DataMapper::Model.descendants.delete(model)
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,143 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class AdapterTest < Test::Unit::TestCase
4
+ context DataMapper::Adapters::TokyoCabinetAdapter do
5
+ context 'Serial key resource' do
6
+ setup do
7
+ class ::User
8
+ include DataMapper::Resource
9
+ property :id, Serial
10
+ property :name, String
11
+ property :age, Integer
12
+ end
13
+
14
+ @user = User.create(:name => 'Joe', :age => 22)
15
+ end
16
+
17
+ teardown do
18
+ User.all.destroy
19
+ end
20
+
21
+ should 'assign id to attributes' do
22
+ item = User.create
23
+ assert_kind_of User, item
24
+ assert_not_nil item.id
25
+ end
26
+
27
+ should 'get an item' do
28
+ assert_equal @user, User.get(@user.id)
29
+ end
30
+
31
+ should 'get items' do
32
+ assert_equal 1, User.all.size
33
+ end
34
+
35
+ should 'destroy item' do
36
+ assert @user.destroy
37
+ assert_equal 0, User.all.size
38
+ end
39
+
40
+ should 'update item' do
41
+ @user.name = 'Woot'
42
+ assert @user.save
43
+ assert_equal 'Woot', User.get(@user.id).name
44
+ end
45
+ end
46
+
47
+ context 'Compound key resource' do
48
+ setup do
49
+ class ::User
50
+ include DataMapper::Resource
51
+ property :name, String, :key => true
52
+ property :age, Integer, :key => true
53
+ end
54
+
55
+ @user = User.create(:name => 'Joe', :age => 22)
56
+ end
57
+
58
+ teardown do
59
+ User.all.destroy
60
+ end
61
+
62
+ should 'get an item' do
63
+ assert_equal @user, User.get(*@user.key)
64
+ end
65
+ end
66
+
67
+ context 'Relationship same db' do
68
+ setup do
69
+ class ::User
70
+ include DataMapper::Resource
71
+
72
+ has n, :comments
73
+
74
+ property :id, Serial
75
+ property :name, String
76
+ end
77
+ class ::Comment
78
+ include DataMapper::Resource
79
+
80
+ belongs_to :user
81
+
82
+ property :id, Serial
83
+ property :content, String
84
+ end
85
+ end
86
+
87
+ should 'map object' do
88
+ @user= User.create(:name => 'Joe')
89
+ @comment= Comment.create(:content => 'Foo')
90
+
91
+ @user.comments << @comment
92
+ @user.save
93
+
94
+ assert_equal 1, @user.comments.length
95
+ end
96
+
97
+ teardown do
98
+ User.all.destroy
99
+ Comment.all.destroy
100
+ end
101
+ end
102
+
103
+ context 'Relationship different db' do
104
+ setup do
105
+ class ::User
106
+ include DataMapper::Resource
107
+
108
+ has n, :comments
109
+
110
+ property :id, Serial
111
+ property :name, String
112
+ end
113
+ class ::Comment
114
+ include DataMapper::Resource
115
+
116
+ def self.default_repository_name
117
+ :sqlite
118
+ end
119
+ belongs_to :user
120
+
121
+ property :id, Serial
122
+ property :content, String
123
+ end
124
+ DataMapper.auto_migrate!(:sqlite)
125
+ end
126
+
127
+ should 'map object' do
128
+ @user= User.create(:name => 'Joe')
129
+ @comment= Comment.create(:content => 'Foo')
130
+
131
+ @user.comments << @comment
132
+ @user.save
133
+
134
+ assert_equal 1, @user.comments.length
135
+ end
136
+
137
+ teardown do
138
+ User.all.destroy
139
+ Comment.all.destroy
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,58 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class QueryTest < Test::Unit::TestCase
4
+ context 'Resource' do
5
+ setup do
6
+ class ::User
7
+ include DataMapper::Resource
8
+ property :id, Serial
9
+ property :name, String
10
+ property :age, Integer
11
+ end
12
+
13
+ @joe = User.create(:name => 'Joe', :age => 11)
14
+ @jack = User.create(:name => 'Jack', :age => 22)
15
+ @john = User.create(:name => 'John', :age => 33)
16
+ end
17
+
18
+ teardown do
19
+ User.all.destroy
20
+ end
21
+
22
+ should 'get items' do
23
+ assert_equal 3, User.all.size
24
+ end
25
+
26
+ should 'get items with sring conditions' do
27
+ User.create(:name => 'John', :age => 44)
28
+ assert_equal 2, User.all(:name => 'John').size
29
+ end
30
+
31
+ should 'get items with integer equality conditions' do
32
+ User.create(:name => 'Fred', :age => 33)
33
+ assert_equal 2, User.all(:age => 33).size
34
+ end
35
+
36
+ should 'get items with integer range conditions' do
37
+ User.create(:name => 'Fred', :age => 33)
38
+ assert_equal 3, User.all(:age.gte => 22, :age.lte => 34).size
39
+ end
40
+
41
+ should 'order items by string' do
42
+ users = [@jack, @joe, @john]
43
+ assert_equal users, User.all(:order => [:name.asc])
44
+ assert_equal users.reverse, User.all(:order => [:name.desc])
45
+ end
46
+
47
+ should 'order items by integer' do
48
+ users = [@joe, @jack, @john]
49
+ assert_equal users, User.all(:order => [:age.asc])
50
+ assert_equal users.reverse, User.all(:order => [:age.desc])
51
+ end
52
+
53
+ should 'limit items' do
54
+ assert_equal 2, User.all(:limit => 2).size
55
+ end
56
+ end
57
+ end # QueryTest
58
+
@@ -0,0 +1,7 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class AdapterTest < Test::Unit::TestCase
4
+ context DataMapper::Adapters::TokyoTyrantAdapter do
5
+ should 'behave like DM::A::TokyoCabinetAdapter'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-tokyo-adapter
3
+ version: !ruby/object:Gem::Version
4
+ hash: 13
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 4
9
+ - 1
10
+ version: 0.4.1
11
+ platform: ruby
12
+ authors:
13
+ - Joshua Partogi
14
+ - Shane Hanna
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-10-14 00:00:00 +11:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: dm-core
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 19
31
+ segments:
32
+ - 1
33
+ - 0
34
+ - 2
35
+ version: 1.0.2
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ - !ruby/object:Gem::Dependency
39
+ name: rufus-tokyo
40
+ prerelease: false
41
+ requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ hash: 25
47
+ segments:
48
+ - 1
49
+ - 0
50
+ - 7
51
+ version: 1.0.7
52
+ type: :runtime
53
+ version_requirements: *id002
54
+ - !ruby/object:Gem::Dependency
55
+ name: addressable
56
+ prerelease: false
57
+ requirement: &id003 !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 2
65
+ - 2
66
+ - 2
67
+ version: 2.2.2
68
+ type: :runtime
69
+ version_requirements: *id003
70
+ description:
71
+ email: joshua.partogi@gmail.com shane.hanna@gmail.com
72
+ executables: []
73
+
74
+ extensions: []
75
+
76
+ extra_rdoc_files:
77
+ - LICENSE
78
+ - README.rdoc
79
+ files:
80
+ - .gitignore
81
+ - LICENSE
82
+ - README.rdoc
83
+ - Rakefile
84
+ - VERSION.yml
85
+ - dm-tokyo-adapter.gemspec
86
+ - lib/dm-tokyo-adapter.rb
87
+ - lib/dm-tokyo-adapter/cabinet.rb
88
+ - lib/dm-tokyo-adapter/query.rb
89
+ - lib/dm-tokyo-adapter/tyrant.rb
90
+ - lib/dm-tokyo-adapter/version.rb
91
+ - test/helper.rb
92
+ - test/test_cabinet.rb
93
+ - test/test_query.rb
94
+ - test/test_tyrant.rb
95
+ has_rdoc: true
96
+ homepage: http://github.com/scrum8/dm-tokyo-adapter
97
+ licenses: []
98
+
99
+ post_install_message:
100
+ rdoc_options:
101
+ - --charset=UTF-8
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: 3
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ requirements: []
123
+
124
+ rubyforge_project:
125
+ rubygems_version: 1.3.7
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: Tokyo Cabinet/Tyrant Table DataMapper Adapter.
129
+ test_files:
130
+ - test/helper.rb
131
+ - test/test_cabinet.rb
132
+ - test/test_query.rb
133
+ - test/test_tyrant.rb