dm-ferret-adapter 0.9.11 → 0.10.0

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.
@@ -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"