dm-tokyo-adapter 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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