dm-ferret-adapter 0.9.11 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ === 0.10.0 / 2009-10-15
2
+
3
+ * Updated to work with dm-core 0.10.0
4
+
1
5
  === 0.9.11 / 2009-03-29
2
6
 
3
7
  * No changes this version
@@ -1,18 +1,19 @@
1
1
  .gitignore
2
- History.txt
2
+ History.rdoc
3
3
  LICENSE
4
4
  Manifest.txt
5
- README.txt
5
+ README.rdoc
6
6
  Rakefile
7
7
  TODO
8
8
  bin/ferret
9
9
  lib/ferret_adapter.rb
10
+ lib/ferret_adapter/adapter.rb
10
11
  lib/ferret_adapter/local_index.rb
11
12
  lib/ferret_adapter/remote_index.rb
12
13
  lib/ferret_adapter/repository_ext.rb
13
14
  lib/ferret_adapter/version.rb
14
15
  spec/adapter_spec.rb
15
- spec/helper.rb
16
16
  spec/spec.opts
17
+ spec/spec_helper.rb
17
18
  tasks/install.rb
18
19
  tasks/spec.rb
File without changes
data/Rakefile CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'pathname'
2
- require 'rubygems'
3
2
 
4
3
  ROOT = Pathname(__FILE__).dirname.expand_path
5
4
  JRUBY = RUBY_PLATFORM =~ /java/
@@ -14,7 +13,7 @@ GEM_NAME = 'dm-ferret-adapter'
14
13
  GEM_VERSION = DataMapper::FerretAdapter::VERSION
15
14
  GEM_DEPENDENCIES = [['dm-core', GEM_VERSION], ['ferret', '~>0.11.6']]
16
15
  GEM_CLEAN = %w[ log pkg coverage ]
17
- GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO History.txt ] } #,
16
+ GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.rdoc LICENSE TODO History.rdoc ] } #,
18
17
  # :executables => %w[ ferret ], :bindir => 'bin' } # FIXME: should this be enabled?
19
18
 
20
19
  PROJECT_NAME = 'datamapper'
data/bin/ferret CHANGED
@@ -7,10 +7,10 @@ require "optparse"
7
7
  require "rinda/tuplespace"
8
8
  require 'rubygems'
9
9
 
10
- gem 'dm-core', '0.9.11'
10
+ gem 'dm-core', '0.10.0'
11
11
  require 'dm-core'
12
12
 
13
- gem 'dm-ferret-adapter', '0.9.11'
13
+ gem 'dm-ferret-adapter', '0.10.0'
14
14
  require 'ferret_adapter'
15
15
 
16
16
  options = {
@@ -1,111 +1,7 @@
1
- require 'rubygems'
2
- require 'pathname'
3
- require Pathname(__FILE__).dirname + 'ferret_adapter/version'
4
-
5
- gem 'dm-core', '0.9.11'
6
- require 'dm-core'
7
-
8
- gem 'ferret', '~>0.11.6'
9
1
  require 'ferret'
10
2
 
11
- module DataMapper
12
- module Adapters
13
- class FerretAdapter < AbstractAdapter
14
- def initialize(name, uri_or_options)
15
- super
16
- unless File.extname(@uri.path) == ".sock"
17
- @index = LocalIndex.new(@uri)
18
- else
19
- @index = RemoteIndex.new(@uri)
20
- end
21
- end
22
-
23
- def create(resources)
24
- resources.each do |resource|
25
- attributes = repository(self.name) do
26
- attrs = resource.attributes
27
- attrs.delete_if { |name, value| !resource.class.properties(self.name).has_property?(name) }
28
- resource.class.new(attrs).attributes
29
- end
30
-
31
- # Since we don't inspect the models before generating the indices,
32
- # we'll map the resource's key to the :id column.
33
- key = resource.class.key.first
34
- attributes[:id] = attributes.delete(key.name) unless key.name == :id
35
- attributes[:_type] = resource.class.name
36
-
37
- @index.add attributes
38
- end
39
- 1
40
- end
41
-
42
- def delete(query)
43
- ferret_query = dm_query_to_ferret_query(query)
44
- @index.delete ferret_query
45
- 1
46
- end
47
-
48
- # This returns an array of Ferret docs (glorified hashes) which can
49
- # be used to instantiate objects by doc[:_type] and doc[:_id]
50
- def read_many(query, limit = query.limit)
51
- ferret_query = dm_query_to_ferret_query(query)
52
- @index.search(ferret_query, :limit => (limit || :all))
53
- end
54
-
55
- def read_one(query)
56
- read_many(query).first
57
- end
58
-
59
- # This returns a hash of the resource constant and the ids returned for it
60
- # from the search.
61
- # { Story => ["1", "2"], Image => ["2"] }
62
- def search(query, limit)
63
- results = Hash.new { |h, k| h[k] = [] }
64
- read_many(query, limit).each do |doc|
65
- results[Object.const_get(doc[:_type])] << doc[:id]
66
- end
67
- results
68
- end
69
-
70
- private
71
-
72
- def dm_query_to_ferret_query(query)
73
- # If we already have a ferret query, do nothing
74
- return query if query.is_a?(String)
75
-
76
- ferret = []
77
-
78
- # We scope the query by the _type field to the query's model.
79
- ferret << "+_type:\"#{query.model.name}\""
80
-
81
- if query.conditions.empty?
82
- ferret << "*"
83
- else
84
- query.conditions.each do |operator, property, value|
85
- # We use property.field here, so that you can declare composite
86
- # fields:
87
- # property :content, String, :field => "title|description"
88
- name = property.field
89
-
90
- # Since DM's query syntax does not support OR's, we prefix
91
- # each condition with ferret's operator of +.
92
- ferret << case operator
93
- when :eql, :like then "+#{name}:\"#{value}\""
94
- when :not then "-#{name}:\"#{value}\""
95
- when :lt then "+#{name}: < #{value}"
96
- when :gt then "+#{name}: > #{value}"
97
- when :lte then "+#{name}: <= #{value}"
98
- when :gte then "+#{name}: >= #{value}"
99
- end
100
- end
101
- end
102
- ferret.join(" ")
103
- end
104
-
105
- end
106
- end
107
- end
108
-
109
- require Pathname(__FILE__).dirname + "ferret_adapter/local_index"
110
- require Pathname(__FILE__).dirname + "ferret_adapter/remote_index"
111
- require Pathname(__FILE__).dirname + "ferret_adapter/repository_ext"
3
+ require 'ferret_adapter/adapter'
4
+ require 'ferret_adapter/local_index'
5
+ require 'ferret_adapter/remote_index'
6
+ require 'ferret_adapter/repository_ext'
7
+ require 'ferret_adapter/version'
@@ -0,0 +1,141 @@
1
+ module DataMapper
2
+ module Adapters
3
+ class FerretAdapter < AbstractAdapter
4
+ def initialize(name, options)
5
+ super
6
+ @index = unless File.extname(@options[:path]) == '.sock'
7
+ LocalIndex.new(@options)
8
+ else
9
+ RemoteIndex.new(@options)
10
+ end
11
+ end
12
+
13
+ def create(resources)
14
+ resources.each do |resource|
15
+ attributes = resource.attributes(:field).to_mash
16
+
17
+ # Since we don't inspect the models before generating the indices,
18
+ # we'll map the resource's key to the :id column.
19
+ attributes[:id] ||= resource.key.first
20
+ attributes[:_type] = resource.model.name
21
+
22
+ @index.add attributes
23
+ end
24
+ end
25
+
26
+ # This returns an array of Ferret docs (glorified hashes) which can
27
+ # be used to instantiate objects by doc[:_type] and doc[:_id]
28
+ def read(query)
29
+ fields = query.fields
30
+ key = query.model.key(name).first
31
+
32
+ ferret_query = dm_query_to_ferret_query(query)
33
+
34
+ @index.search(ferret_query, :limit => query.limit).map do |lazy_doc|
35
+ fields.map { |p| [ p, p.typecast(lazy_doc[p.field]) ] }.to_hash.update(
36
+ key.field => key.typecast(lazy_doc[:id])
37
+ )
38
+ end
39
+ end
40
+
41
+ def delete(collection)
42
+ @index.delete dm_query_to_ferret_query(collection.query)
43
+ 1
44
+ end
45
+
46
+ # This returns a hash of the resource constant and the ids returned for it
47
+ # from the search.
48
+ # { Story => ["1", "2"], Image => ["2"] }
49
+ def search(ferret_query, limit = :all)
50
+ results = {}
51
+ @index.search(ferret_query, :limit => limit).each do |doc|
52
+ resources = results[Object.const_get(doc[:_type])] ||= []
53
+ resources << doc[:id]
54
+ end
55
+ results
56
+ end
57
+
58
+ private
59
+
60
+ def dm_query_to_ferret_query(query)
61
+ # We scope the query by the _type field to the query's model.
62
+ statements = [ "+_type:#{quote_value(query.model.name)}" ]
63
+
64
+ if query.conditions.operands.empty?
65
+ statements << '*'
66
+ else
67
+ # TODO: make this work with the new Query conditions system
68
+ statements << "#{conditions_statement(query.conditions)}"
69
+ end
70
+
71
+ statements.join(' ')
72
+ end
73
+
74
+ def conditions_statement(conditions)
75
+ case conditions
76
+ when Query::Conditions::NotOperation then negate_operation(conditions)
77
+ when Query::Conditions::AbstractOperation then operation_statement(conditions)
78
+ when Query::Conditions::AbstractComparison then comparison_statement(conditions)
79
+ end
80
+ end
81
+
82
+ def negate_operation(operation)
83
+ "NOT (#{conditions_statement(operation.operands.first)})"
84
+ end
85
+
86
+ def operation_statement(operation)
87
+ statements = []
88
+
89
+ operation.each do |operand|
90
+ statement = conditions_statement(operand)
91
+
92
+ if operand.respond_to?(:operands) && operand.operands.size > 1
93
+ statement = "(#{statement})"
94
+ end
95
+
96
+ statements << statement
97
+ end
98
+
99
+ join_with = operation.kind_of?(Query::Conditions::AndOperation) ? 'AND' : 'OR'
100
+ statements.join(" #{join_with} ")
101
+ end
102
+
103
+ def comparison_statement(comparison)
104
+ value = comparison.value
105
+
106
+ # TODO: move exclusive Range handling into another method, and
107
+ # update conditions_statement to use it
108
+
109
+ # break exclusive Range queries up into two comparisons ANDed together
110
+ if value.kind_of?(Range) && value.exclude_end?
111
+ operation = Query::Conditions::BooleanOperation.new(:and,
112
+ Query::Conditions::Comparison.new(:gte, comparison.subject, value.first),
113
+ Query::Conditions::Comparison.new(:lt, comparison.subject, value.last)
114
+ )
115
+
116
+ return "(#{operation_statement(operation)})"
117
+ end
118
+
119
+ operator = case comparison
120
+ when Query::Conditions::EqualToComparison then ''
121
+ when Query::Conditions::InclusionComparison then raise NotImplementedError, 'no support for inclusion match yet'
122
+ when Query::Conditions::RegexpComparison then raise NotImplementedError, 'no support for regexp match yet'
123
+ when Query::Conditions::LikeComparison then raise NotImplementedError, 'no support for like match yet'
124
+ when Query::Conditions::GreaterThanComparison then '>'
125
+ when Query::Conditions::LessThanComparison then '<'
126
+ when Query::Conditions::GreaterThanOrEqualToComparison then '>='
127
+ when Query::Conditions::LessThanOrEqualToComparison then '<='
128
+ end
129
+
130
+ # We use property.field here, so that you can declare composite
131
+ # fields:
132
+ # property :content, String, :field => "title|description"
133
+ [ "+#{comparison.subject.field}:", quote_value(value) ].join(operator)
134
+ end
135
+
136
+ def quote_value(value)
137
+ value.kind_of?(Numeric) ? value : "\"#{value}\""
138
+ end
139
+ end
140
+ end
141
+ end
@@ -1,12 +1,9 @@
1
1
  module DataMapper
2
2
  module Adapters
3
3
  class FerretAdapter::LocalIndex
4
-
5
- attr_accessor :uri
6
-
7
- def initialize(uri)
8
- @uri = uri
9
- @options = { :path => @uri.path, :key => [:id, :_type] }
4
+ def initialize(options)
5
+ @options = options
6
+ @options = { :path => @options[:path], :key => [:id, :_type] }
10
7
  create_or_initialize_index
11
8
  end
12
9
 
@@ -29,7 +26,7 @@ module DataMapper
29
26
  private
30
27
 
31
28
  def create_or_initialize_index
32
- unless File.exists?(@uri.path + "segments")
29
+ unless File.exists?(@options[:path] + "segments")
33
30
  field_infos = ::Ferret::Index::FieldInfos.new(:store => :no)
34
31
  field_infos.add_field(:id, :index => :untokenized, :term_vector => :no, :store => :yes)
35
32
  field_infos.add_field(:_type, :index => :untokenized, :term_vector => :no, :store => :yes)
@@ -5,10 +5,8 @@ module DataMapper
5
5
  class IndexNotFound < Exception; end
6
6
  class SearchError < Exception; end
7
7
 
8
- attr_accessor :uri
9
-
10
- def initialize(uri)
11
- @uri = uri
8
+ def initialize(options)
9
+ @options = options
12
10
 
13
11
  connect_to_remote_index
14
12
  end
@@ -39,7 +37,7 @@ module DataMapper
39
37
  require "rinda/tuplespace"
40
38
 
41
39
  DRb.start_service
42
- tuple_space = DRb::DRbObject.new(nil, "drbunix://#{@uri.path}")
40
+ tuple_space = DRb::DRbObject.new(nil, "drbunix://#{@options[:path]}")
43
41
 
44
42
  # This will throw Errno::ENOENT if the socket does not exist.
45
43
  tuple_space.respond_to?(:write)
@@ -1,5 +1,5 @@
1
1
  module DataMapper
2
2
  class FerretAdapter
3
- VERSION = '0.9.11'
3
+ VERSION = '0.10.0'.freeze
4
4
  end
5
5
  end
@@ -1,35 +1,43 @@
1
- require 'pathname'
2
- require Pathname(__FILE__).dirname + "helper"
1
+ require 'spec_helper'
3
2
 
4
- class User
5
- include DataMapper::Resource
6
- property :id, Serial
7
- end
3
+ INDEX_PATH = Pathname(__FILE__).dirname.expand_path + 'index'
8
4
 
9
- class Photo
10
- include DataMapper::Resource
11
- property :uuid, String, :default => lambda { `uuidgen`.chomp }, :key => true
12
- end
5
+ describe DataMapper::Adapters::FerretAdapter do
6
+ before do
7
+ @adapter = DataMapper.setup(:default, "ferret://#{INDEX_PATH}")
8
+
9
+ Object.send(:remove_const, :User) if defined?(User)
10
+ class ::User
11
+ include DataMapper::Resource
12
+
13
+ property :id, Serial
14
+ end
15
+
16
+ Object.send(:remove_const, :Photo) if defined?(Photo)
17
+ class ::Photo
18
+ include DataMapper::Resource
19
+
20
+ property :uuid, String, :default => proc { UUIDTools::UUID.random_create }, :key => true
21
+ property :happy, Boolean, :default => true
22
+ end
23
+ end
13
24
 
14
- describe "FerretAdapter" do
15
- before :each do
16
- @index = Pathname(__FILE__).dirname.expand_path + "index"
17
- DataMapper.setup :search, "ferret://#{@index}"
25
+ after do
26
+ FileUtils.rm_r(INDEX_PATH)
18
27
  end
19
28
 
20
- after :each do
21
- FileUtils.rm_r(@index)
29
+ it 'should work with a model using id' do
30
+ u = User.create(:id => 2)
31
+ repository.search('*').should == { User => %w[ 2 ] }
22
32
  end
23
33
 
24
- it "should work with a model using id" do
25
- u = User.new(:id => 2)
26
- repository(:search).create([u])
27
- repository(:search).search("*").should == { User => ["2"] }
34
+ it 'should work with a model using another key than id' do
35
+ p = Photo.create
36
+ repository.search('*').should == { Photo => [p.uuid] }
28
37
  end
29
38
 
30
- it "should work with a model using another key than id" do
31
- p = Photo.new
32
- repository(:search).create([p])
33
- repository(:search).search("*").should == { Photo => [p.uuid] }
39
+ it 'should allow lookups using Model#get' do
40
+ u = User.create(:id => 2)
41
+ User.get(2).should == u
34
42
  end
35
43
  end
@@ -1 +1,2 @@
1
1
  --colour
2
+ --loadby random
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'uuidtools'
3
+
4
+ # use local dm-core if running from a typical dev checkout.
5
+ lib = File.join('..', '..', '..', 'dm-core', 'lib')
6
+ $LOAD_PATH.unshift(lib) if File.directory?(lib)
7
+ require 'dm-core'
8
+
9
+ # Support running specs with 'rake spec' and 'spec'
10
+ $LOAD_PATH.unshift('lib') unless $LOAD_PATH.include?('lib')
11
+
12
+ require 'ferret_adapter'
13
+
14
+ def load_driver(name, default_uri)
15
+ return false if ENV['ADAPTER'] != name.to_s
16
+
17
+ begin
18
+ DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
19
+ DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
20
+ true
21
+ rescue LoadError => e
22
+ warn "Could not load do_#{name}: #{e}"
23
+ false
24
+ end
25
+ end
26
+
27
+ ENV['ADAPTER'] ||= 'sqlite3'
28
+
29
+ HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
30
+ HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
31
+ HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
@@ -4,7 +4,7 @@ end
4
4
 
5
5
  desc "Install #{GEM_NAME} #{GEM_VERSION}"
6
6
  task :install => [ :package ] do
7
- sudo_gem "install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
7
+ sudo_gem "install pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
8
8
  end
9
9
 
10
10
  desc "Uninstall #{GEM_NAME} #{GEM_VERSION}"
@@ -1,6 +1,4 @@
1
1
  begin
2
- gem 'rspec', '~>1.2'
3
- require 'spec'
4
2
  require 'spec/rake/spectask'
5
3
 
6
4
  task :default => [ :spec ]
@@ -8,16 +6,18 @@ begin
8
6
  desc 'Run specifications'
9
7
  Spec::Rake::SpecTask.new(:spec) do |t|
10
8
  t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
11
- t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s).map { |f| f.to_s }
9
+ t.libs << 'lib' << 'spec' # needed for CI rake spec task, duplicated in spec_helper
12
10
 
13
11
  begin
14
- gem 'rcov', '~>0.8'
12
+ require 'rcov'
15
13
  t.rcov = JRUBY ? false : (ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true)
16
14
  t.rcov_opts << '--exclude' << 'spec'
17
15
  t.rcov_opts << '--text-summary'
18
16
  t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
19
17
  rescue LoadError
20
18
  # rcov not installed
19
+ rescue SyntaxError
20
+ # rcov syntax invalid
21
21
  end
22
22
  end
23
23
  rescue LoadError
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dm-ferret-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.11
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernerd Schaefer
@@ -9,29 +9,10 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-29 00:00:00 -07:00
12
+ date: 2009-09-16 00:00:00 -07:00
13
13
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: dm-core
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - "="
22
- - !ruby/object:Gem::Version
23
- version: 0.9.11
24
- version:
25
- - !ruby/object:Gem::Dependency
26
- name: ferret
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ~>
32
- - !ruby/object:Gem::Version
33
- version: 0.11.6
34
- version:
14
+ dependencies: []
15
+
35
16
  description: Ferret Adapter for DataMapper
36
17
  email:
37
18
  - bernerd [a] wieck [d] com
@@ -40,35 +21,38 @@ executables:
40
21
  extensions: []
41
22
 
42
23
  extra_rdoc_files:
43
- - README.txt
24
+ - README.rdoc
44
25
  - LICENSE
45
26
  - TODO
46
- - History.txt
27
+ - History.rdoc
47
28
  files:
48
29
  - .gitignore
49
- - History.txt
30
+ - History.rdoc
50
31
  - LICENSE
51
32
  - Manifest.txt
52
- - README.txt
33
+ - README.rdoc
53
34
  - Rakefile
54
35
  - TODO
55
36
  - bin/ferret
56
37
  - lib/ferret_adapter.rb
38
+ - lib/ferret_adapter/adapter.rb
57
39
  - lib/ferret_adapter/local_index.rb
58
40
  - lib/ferret_adapter/remote_index.rb
59
41
  - lib/ferret_adapter/repository_ext.rb
60
42
  - lib/ferret_adapter/version.rb
61
43
  - spec/adapter_spec.rb
62
- - spec/helper.rb
63
44
  - spec/spec.opts
45
+ - spec/spec_helper.rb
64
46
  - tasks/install.rb
65
47
  - tasks/spec.rb
66
48
  has_rdoc: true
67
49
  homepage: http://github.com/sam/dm-more/tree/master/adapters/dm-ferret-adapter
50
+ licenses: []
51
+
68
52
  post_install_message:
69
53
  rdoc_options:
70
54
  - --main
71
- - README.txt
55
+ - README.rdoc
72
56
  require_paths:
73
57
  - lib
74
58
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -86,9 +70,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
70
  requirements: []
87
71
 
88
72
  rubyforge_project: datamapper
89
- rubygems_version: 1.3.1
73
+ rubygems_version: 1.3.5
90
74
  signing_key:
91
- specification_version: 2
75
+ specification_version: 3
92
76
  summary: Ferret Adapter for DataMapper
93
77
  test_files: []
94
78
 
@@ -1,4 +0,0 @@
1
- require 'pathname'
2
- require Pathname(__FILE__).dirname.parent + "lib/ferret_adapter"
3
-
4
- require "spec"