mdwan-rsolr 0.8.2

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.
data/lib/mash.rb ADDED
@@ -0,0 +1,148 @@
1
+ # This class has dubious semantics and we only have it so that people can write
2
+ # params[:key] instead of params['key'].
3
+ class Mash < Hash
4
+
5
+ # @param constructor<Object>
6
+ # The default value for the mash. Defaults to an empty hash.
7
+ #
8
+ # @details [Alternatives]
9
+ # If constructor is a Hash, a new mash will be created based on the keys of
10
+ # the hash and no default value will be set.
11
+ def initialize(constructor = {})
12
+ if constructor.is_a?(Hash)
13
+ super()
14
+ update(constructor)
15
+ else
16
+ super(constructor)
17
+ end
18
+ end
19
+
20
+ # @param key<Object> The default value for the mash. Defaults to nil.
21
+ #
22
+ # @details [Alternatives]
23
+ # If key is a Symbol and it is a key in the mash, then the default value will
24
+ # be set to the value matching the key.
25
+ def default(key = nil)
26
+ if key.is_a?(Symbol) && include?(key = key.to_s)
27
+ self[key]
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
34
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
35
+
36
+ # @param key<Object> The key to set.
37
+ # @param value<Object>
38
+ # The value to set the key to.
39
+ #
40
+ # @see Mash#convert_key
41
+ # @see Mash#convert_value
42
+ def []=(key, value)
43
+ regular_writer(convert_key(key), convert_value(value))
44
+ end
45
+
46
+ # @param other_hash<Hash>
47
+ # A hash to update values in the mash with. The keys and the values will be
48
+ # converted to Mash format.
49
+ #
50
+ # @return <Mash> The updated mash.
51
+ def update(other_hash)
52
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
53
+ self
54
+ end
55
+
56
+ alias_method :merge!, :update
57
+
58
+ # @param key<Object> The key to check for. This will be run through convert_key.
59
+ #
60
+ # @return <TrueClass, FalseClass> True if the key exists in the mash.
61
+ def key?(key)
62
+ super(convert_key(key))
63
+ end
64
+
65
+ # def include? def has_key? def member?
66
+ alias_method :include?, :key?
67
+ alias_method :has_key?, :key?
68
+ alias_method :member?, :key?
69
+
70
+ # @param key<Object> The key to fetch. This will be run through convert_key.
71
+ # @param *extras<Array> Default value.
72
+ #
73
+ # @return <Object> The value at key or the default value.
74
+ def fetch(key, *extras)
75
+ super(convert_key(key), *extras)
76
+ end
77
+
78
+ # @param *indices<Array>
79
+ # The keys to retrieve values for. These will be run through +convert_key+.
80
+ #
81
+ # @return <Array> The values at each of the provided keys
82
+ def values_at(*indices)
83
+ indices.collect {|key| self[convert_key(key)]}
84
+ end
85
+
86
+ # @param hash<Hash> The hash to merge with the mash.
87
+ #
88
+ # @return <Mash> A new mash with the hash values merged in.
89
+ def merge(hash)
90
+ self.dup.update(hash)
91
+ end
92
+
93
+ # @param key<Object>
94
+ # The key to delete from the mash.\
95
+ def delete(key)
96
+ super(convert_key(key))
97
+ end
98
+
99
+ # @param *rejected<Array[(String, Symbol)] The mash keys to exclude.
100
+ #
101
+ # @return <Mash> A new mash without the selected keys.
102
+ #
103
+ # @example
104
+ # { :one => 1, :two => 2, :three => 3 }.except(:one)
105
+ # #=> { "two" => 2, "three" => 3 }
106
+ def except(*keys)
107
+ super(*keys.map {|k| convert_key(k)})
108
+ end
109
+
110
+ # Used to provide the same interface as Hash.
111
+ #
112
+ # @return <Mash> This mash unchanged.
113
+ def stringify_keys!; self end
114
+
115
+ # @return <Hash> The mash as a Hash with string keys.
116
+ def to_hash
117
+ Hash.new(default).merge(self)
118
+ end
119
+
120
+ protected
121
+ # @param key<Object> The key to convert.
122
+ #
123
+ # @param <Object>
124
+ # The converted key. If the key was a symbol, it will be converted to a
125
+ # string.
126
+ #
127
+ # @api private
128
+ def convert_key(key)
129
+ key.kind_of?(Symbol) ? key.to_s : key
130
+ end
131
+
132
+ # @param value<Object> The value to convert.
133
+ #
134
+ # @return <Object>
135
+ # The converted value. A Hash or an Array of hashes, will be converted to
136
+ # their Mash equivalents.
137
+ #
138
+ # @api private
139
+ def convert_value(value)
140
+ if value.class == Hash
141
+ value.to_mash
142
+ elsif value.is_a?(Array)
143
+ value.collect { |e| convert_value(e) }
144
+ else
145
+ value
146
+ end
147
+ end
148
+ end
data/lib/rsolr.rb ADDED
@@ -0,0 +1,52 @@
1
+ # add this directory to the load path if it hasn't already been added
2
+ # load xout and rfuzz libs
3
+ proc {|base, files|
4
+ $: << base unless $:.include?(base) || $:.include?(File.expand_path(base))
5
+ files.each {|f| require f}
6
+ }.call(File.dirname(__FILE__), ['core_ext', 'mash'])
7
+
8
+ module RSolr
9
+
10
+ VERSION = '0.8.2'
11
+
12
+ autoload :Message, 'rsolr/message'
13
+ autoload :Connection, 'rsolr/connection'
14
+ autoload :Adapter, 'rsolr/adapter'
15
+ autoload :HTTPClient, 'rsolr/http_client'
16
+
17
+ # factory for creating connections
18
+ # "options" is a hash that gets used by the Connection
19
+ # object AND the adapter object.
20
+ def self.connect(options={})
21
+ adapter_name = options[:adapter] ||= :http
22
+ types = {
23
+ :http=>'HTTP',
24
+ :direct=>'Direct'
25
+ }
26
+ adapter_class = RSolr::Adapter.const_get(types[adapter_name])
27
+ adapter = adapter_class.new(options)
28
+ RSolr::Connection.new(adapter, options)
29
+ end
30
+
31
+ module Char
32
+
33
+ # escape - from the solr-ruby library
34
+ # RSolr.escape('asdf')
35
+ # backslash everything that isn't a word character
36
+ def escape(value)
37
+ value.gsub(/(\W)/, '\\\\\1')
38
+ end
39
+
40
+ end
41
+
42
+ # send the escape method into the Connection class ->
43
+ # solr = RSolr.connect
44
+ # solr.escape('asdf')
45
+ RSolr::Connection.send(:include, Char)
46
+
47
+ # bring escape into this module (RSolr) -> RSolr.escape('asdf')
48
+ extend Char
49
+
50
+ class RequestError < RuntimeError; end
51
+
52
+ end
@@ -0,0 +1,6 @@
1
+ module RSolr::Adapter
2
+
3
+ autoload :Direct, 'rsolr/adapter/direct'
4
+ autoload :HTTP, 'rsolr/adapter/http'
5
+
6
+ end
@@ -0,0 +1,86 @@
1
+ raise "JRuby Required" unless defined?(JRUBY_VERSION)
2
+
3
+ require 'java'
4
+
5
+ #
6
+ # Connection for JRuby + DirectSolrConnection
7
+ #
8
+ class RSolr::Adapter::Direct
9
+
10
+ include RSolr::HTTPClient::Util
11
+
12
+ attr_accessor :opts, :home_dir
13
+
14
+ # required: opts[:home_dir] is absolute path to solr home (the directory with "data", "config" etc.)
15
+ # opts must also contain either
16
+ # :dist_dir => 'absolute path to solr distribution root
17
+ # or
18
+ # :jar_paths => ['array of directories containing the solr lib/jars']
19
+ # OTHER OPTS:
20
+ # :select_path => 'the/select/handler'
21
+ # :update_path => 'the/update/handler'
22
+ def initialize(opts, &block)
23
+ @home_dir = opts[:home_dir].to_s
24
+ opts[:data_dir] ||= File.join(@home_dir, 'data')
25
+ if opts[:dist_dir] and ! opts[:jar_paths]
26
+ # add the standard lib and dist directories to the :jar_paths
27
+ opts[:jar_paths] = [File.join(opts[:dist_dir], 'lib'), File.join(opts[:dist_dir], 'dist')]
28
+ end
29
+ @opts = opts
30
+ end
31
+
32
+ # loads/imports the java dependencies
33
+ # sets the @connection instance variable
34
+ def connection
35
+ @connection ||= (
36
+ require_jars(@opts[:jar_paths]) if @opts[:jar_paths]
37
+ import_dependencies
38
+ DirectSolrConnection.new(@home_dir, @opts[:data_dir], nil)
39
+ )
40
+ end
41
+
42
+ def close
43
+ if @connection
44
+ @connection.close
45
+ @connection=nil
46
+ end
47
+ end
48
+
49
+ # send a request to the connection
50
+ # request '/update', :wt=>:xml, '</commit>'
51
+ def send_request(path, params={}, data=nil)
52
+ data = data.to_xml if data.respond_to?(:to_xml)
53
+ url = build_url(path, params)
54
+ begin
55
+ body = connection.request(url, data)
56
+ rescue
57
+ raise RSolr::RequestError.new($!.message)
58
+ end
59
+ {
60
+ :status_code=>nil,
61
+ :body=>body,
62
+ :url=>url,
63
+ :path=>path,
64
+ :params=>params,
65
+ :data=>data,
66
+ :headers=>nil
67
+ }
68
+ end
69
+
70
+ protected
71
+
72
+ # do the java import thingy
73
+ def import_dependencies
74
+ import org.apache.solr.servlet.DirectSolrConnection
75
+ end
76
+
77
+ # require the jar files
78
+ def require_jars(paths)
79
+ paths = [paths] unless paths.is_a?(Array)
80
+ paths.each do |path|
81
+ jar_pattern = File.join(path,"**", "*.jar")
82
+ Dir[jar_pattern].each {|jar_file| require jar_file}
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,44 @@
1
+ #
2
+ # Connection for standard HTTP Solr server
3
+ #
4
+ class RSolr::Adapter::HTTP
5
+
6
+ attr_reader :opts, :connector, :connection
7
+
8
+ # opts can have:
9
+ # :url => 'http://localhost:8080/solr'
10
+ # :select_path => '/the/url/path/to/the/select/handler'
11
+ # :update_path => '/the/url/path/to/the/update/handler'
12
+ # :luke_path => '/admin/luke'
13
+ #
14
+ def initialize(opts={}, &block)
15
+ opts[:url]||='http://127.0.0.1:8983/solr'
16
+ @opts = opts
17
+ @connector = RSolr::HTTPClient::Connector.new
18
+ end
19
+
20
+ def connection
21
+ @connection ||= @connector.connect(@opts[:url])
22
+ end
23
+
24
+ # send a request to the connection
25
+ # request '/update', :wt=>:xml, '</commit>'
26
+ def send_request(path, params={}, data=nil)
27
+ data = data.to_xml if data.respond_to?(:to_xml)
28
+ if data
29
+ http_context = connection.post(path, data, params, post_headers)
30
+ else
31
+ http_context = connection.get(path, params)
32
+ end
33
+ raise RSolr::RequestError.new(http_context[:body]) unless http_context[:status_code] == 200
34
+ http_context
35
+ end
36
+
37
+ protected
38
+
39
+ # The standard post headers
40
+ def post_headers
41
+ {"Content-Type" => 'text/xml; charset=utf-8'}
42
+ end
43
+
44
+ end
@@ -0,0 +1,107 @@
1
+ class RSolr::Connection
2
+
3
+ attr_reader :adapter, :opts
4
+
5
+ # "adapter" is instance of:
6
+ # RSolr::Adapter::HTTP
7
+ # RSolr::Adapter::Direct (jRuby only)
8
+ def initialize(adapter, opts={})
9
+ @adapter = adapter
10
+ @opts = opts
11
+ end
12
+
13
+ # send a request to the "select" handler
14
+ def select(params, &blk)
15
+ send_request('/select', map_params(params), &blk)
16
+ end
17
+
18
+ # sends data to the update handler
19
+ # data can be a string of xml, or an object that returns xml from its #to_s method
20
+ def update(data, params={}, &blk)
21
+ send_request('/update', map_params(params), data, &blk)
22
+ end
23
+
24
+ # send request solr
25
+ # params is hash with valid solr request params (:q, :fl, :qf etc..)
26
+ # if params[:wt] is not set, the default is :ruby
27
+ # if :wt is something other than :ruby, the raw response body is used
28
+ # otherwise, a simple Hash is returned
29
+ # NOTE: to get raw ruby, use :wt=>'ruby' <- a string, not a symbol like :ruby
30
+ #
31
+ # use a block to get access to the adapter response:
32
+ # solr.send_request('/select', :q=>'blue') do |solr_response, adapter_response|
33
+ # raise 'Woops!' if adapter_response[:status] != 200
34
+ # solr_response[:response][:docs].each {|doc|}
35
+ # end
36
+ #
37
+ def send_request(path, params={}, data=nil, &blk)
38
+ response = @adapter.send_request(path, map_params(params), data)
39
+ adapt_response(response, &blk)
40
+ end
41
+
42
+ #
43
+ # single record:
44
+ # solr.update(:id=>1, :name=>'one')
45
+ #
46
+ # update using an array
47
+ # solr.update([{:id=>1, :name=>'one'}, {:id=>2, :name=>'two'}])
48
+ #
49
+ def add(doc, &block)
50
+ update message.add(doc, &block)
51
+ end
52
+
53
+ # send </commit>
54
+ def commit
55
+ update message.commit
56
+ end
57
+
58
+ # send </optimize>
59
+ def optimize
60
+ update message.optimize
61
+ end
62
+
63
+ # send </rollback>
64
+ # NOTE: solr 1.4 only
65
+ def rollback
66
+ update message.rollback
67
+ end
68
+
69
+ # Delete one or many documents by id
70
+ # solr.delete_by_id 10
71
+ # solr.delete_by_id([12, 41, 199])
72
+ def delete_by_id(id)
73
+ update message.delete_by_id(id)
74
+ end
75
+
76
+ # delete one or many documents by query
77
+ # solr.delete_by_query 'available:0'
78
+ # solr.delete_by_query ['quantity:0', 'manu:"FQ"']
79
+ def delete_by_query(query)
80
+ update message.delete_by_query(query)
81
+ end
82
+
83
+ protected
84
+
85
+ # shortcut to solr::message
86
+ def message
87
+ RSolr::Message
88
+ end
89
+
90
+ # sets default params etc.. - could be used as a mapping hook
91
+ # type of request should be passed in here? -> map_params(:query, {})
92
+ def map_params(params)
93
+ params||={}
94
+ {:wt=>:ruby}.merge(params)
95
+ end
96
+
97
+ #
98
+ def adapt_response(adapter_response)
99
+ if adapter_response[:params][:wt] == :ruby
100
+ data = Kernel.eval(adapter_response[:body]).to_mash
101
+ else
102
+ data = adapter_response[:body]
103
+ end
104
+ block_given? ? yield(data, adapter_response) : data
105
+ end
106
+
107
+ end