sdsykes_acts_as_ferret 0.4.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ require 'drb'
2
+ module ActsAsFerret
3
+
4
+ # This index implementation connects to a remote ferret server instance. It
5
+ # basically forwards all calls to the remote server.
6
+ class RemoteIndex < AbstractIndex
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ @ferret_config = config[:ferret]
11
+ @server = DRbObject.new(nil, config[:remote])
12
+ end
13
+
14
+ def method_missing(method_name, *args)
15
+ args.unshift model_class_name
16
+ @server.send(method_name, *args)
17
+ end
18
+
19
+ def find_id_by_contents(q, options = {}, &proc)
20
+ total_hits, results = @server.find_id_by_contents(model_class_name, q, options)
21
+ block_given? ? yield_results(total_hits, results, &proc) : [ total_hits, results ]
22
+ end
23
+
24
+ def id_multi_search(query, models, options, &proc)
25
+ total_hits, results = @server.id_multi_search(model_class_name, query, models, options)
26
+ block_given? ? yield_results(total_hits, results, &proc) : [ total_hits, results ]
27
+ end
28
+
29
+ # add record to index
30
+ def add(record)
31
+ @server.add record.class.name, record.to_doc
32
+ end
33
+ alias << add
34
+
35
+ private
36
+
37
+ def yield_results(total_hits, results)
38
+ results.each do |result|
39
+ yield result[:model], result[:id], result[:score], result[:data]
40
+ end
41
+ total_hits
42
+ end
43
+
44
+ def model_class_name
45
+ @config[:class_name]
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,53 @@
1
+ module ActsAsFerret
2
+
3
+ # decorator that adds a total_hits accessor and will_paginate compatible
4
+ # paging support to search result arrays
5
+ class SearchResults
6
+ attr_reader :current_page, :per_page, :total_hits
7
+
8
+ def initialize(results, total_hits, current_page = 1, per_page = nil)
9
+ @results = results
10
+ @total_hits = total_hits
11
+ @current_page = current_page
12
+ @per_page = (per_page || total_hits)
13
+ @total_pages = @per_page > 0 ? (@total_hits / @per_page.to_f).ceil : 0
14
+ end
15
+
16
+ def method_missing(symbol, *args, &block)
17
+ @results.send(symbol, *args, &block)
18
+ end
19
+
20
+ def respond_to?(name)
21
+ self.methods.include?(name) || @results.respond_to?(name)
22
+ end
23
+
24
+
25
+ # code from here on was directly taken from will_paginate's collection.rb
26
+
27
+ #
28
+ # The total number of pages.
29
+ def page_count
30
+ @total_pages
31
+ end
32
+
33
+ # Current offset of the paginated collection. If we're on the first page,
34
+ # it is always 0. If we're on the 2nd page and there are 30 entries per page,
35
+ # the offset is 30. This property is useful if you want to render ordinals
36
+ # besides your records: simply start with offset + 1.
37
+ #
38
+ def offset
39
+ (current_page - 1) * per_page
40
+ end
41
+
42
+ # current_page - 1 or nil if there is no previous page
43
+ def previous_page
44
+ current_page > 1 ? (current_page - 1) : nil
45
+ end
46
+
47
+ # current_page + 1 or nil if there is no next page
48
+ def next_page
49
+ current_page < page_count ? (current_page + 1) : nil
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,46 @@
1
+ ################################################################################
2
+ require 'optparse'
3
+
4
+ ################################################################################
5
+ $ferret_server_options = {
6
+ 'environment' => nil,
7
+ 'debug' => nil,
8
+ }
9
+
10
+ ################################################################################
11
+ OptionParser.new do |optparser|
12
+ optparser.banner = "Usage: #{File.basename($0)} [options] {start|stop}"
13
+
14
+ optparser.on('-h', '--help', "This message") do
15
+ puts optparser
16
+ exit
17
+ end
18
+
19
+ optparser.on('-e', '--environment=NAME', 'Set RAILS_ENV to the given string') do |e|
20
+ $ferret_server_options['environment'] = e
21
+ end
22
+
23
+ optparser.on('--debug', 'Include full stack traces on exceptions') do
24
+ $ferret_server_options['debug'] = true
25
+ end
26
+
27
+ $ferret_server_action = optparser.permute!(ARGV)
28
+ (puts optparser; exit(1)) unless $ferret_server_action.size == 1
29
+
30
+ $ferret_server_action = $ferret_server_action.first
31
+ (puts optparser; exit(1)) unless %w(start stop).include?($ferret_server_action)
32
+ end
33
+
34
+ ################################################################################
35
+ begin
36
+ ENV['FERRET_USE_LOCAL_INDEX'] = 'true'
37
+ ENV['RAILS_ENV'] = $ferret_server_options['environment']
38
+ #require(File.join(File.dirname(__FILE__), '../../../../config/environment'))
39
+ require(File.join(File.dirname(ENV['_']), '../config/environment'))
40
+ require 'acts_as_ferret'
41
+ ActsAsFerret::Remote::Server.new.send($ferret_server_action)
42
+ rescue Exception => e
43
+ $stderr.puts(e.message)
44
+ $stderr.puts(e.backtrace.join("\n")) if $ferret_server_options['debug']
45
+ exit(1)
46
+ end
@@ -0,0 +1,14 @@
1
+ module ActsAsFerret
2
+
3
+ class SharedIndex < LocalIndex
4
+
5
+ # build a ferret query matching only the record with the given id and class
6
+ def query_for_record(id, class_name)
7
+ (bq = Ferret::Search::BooleanQuery.new).tap do
8
+ bq.add_query(Ferret::Search::TermQuery.new(:id, id.to_s), :must)
9
+ bq.add_query(Ferret::Search::TermQuery.new(:class_name, class_name), :must)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,90 @@
1
+ module ActsAsFerret
2
+
3
+ # class methods for classes using acts_as_ferret :single_index => true
4
+ module SharedIndexClassMethods
5
+
6
+ def find_id_by_contents(q, options = {}, &block)
7
+ # add class name scoping to query if necessary
8
+ unless options[:models] == :all # search needs to be restricted by one or more class names
9
+ options[:models] ||= []
10
+ # add this class to the list of given models
11
+ options[:models] << self unless options[:models].include?(self)
12
+ # keep original query
13
+ original_query = q
14
+
15
+ if original_query.is_a? String
16
+ model_query = options[:models].map(&:name).join '|'
17
+ q += %{ +class_name:"#{model_query}"}
18
+ else
19
+ q = Ferret::Search::BooleanQuery.new
20
+ q.add_query(original_query, :must)
21
+ model_query = Ferret::Search::BooleanQuery.new
22
+ options[:models].each do |model|
23
+ model_query.add_query(Ferret::Search::TermQuery.new(:class_name, model.name), :should)
24
+ end
25
+ q.add_query(model_query, :must)
26
+ end
27
+ end
28
+ options.delete :models
29
+
30
+ super(q, options, &block)
31
+ end
32
+
33
+ # Overrides the standard find_by_contents for searching a shared index.
34
+ #
35
+ # please note that records from different models will be fetched in
36
+ # separate sql calls, so any sql order_by clause given with
37
+ # find_options[:order] will be ignored.
38
+ def find_by_contents(q, options = {}, find_options = {})
39
+ if order = find_options.delete(:order)
40
+ logger.warn "using a shared index, so ignoring order_by clause #{order}"
41
+ end
42
+ total_hits, result = find_records_lazy_or_not q, options, find_options
43
+ # sort so results have the same order they had when originally retrieved
44
+ # from ferret
45
+ return SearchResults.new(result, total_hits)
46
+ end
47
+
48
+ protected
49
+
50
+ def ar_find_by_contents(q, options = {}, find_options = {})
51
+ total_hits, id_arrays = collect_results(q, options)
52
+ result = retrieve_records(id_arrays, find_options)
53
+ result.sort! { |a, b| id_arrays[a.class.name][a.id.to_s].first <=> id_arrays[b.class.name][b.id.to_s].first }
54
+ [ total_hits, result ]
55
+ end
56
+
57
+ def collect_results(q, options = {})
58
+ id_arrays = {}
59
+ # get object ids for index hits
60
+ rank = 0
61
+ total_hits = find_id_by_contents(q, options) do |model, id, score, data|
62
+ id_arrays[model] ||= {}
63
+ # store result rank and score
64
+ id_arrays[model][id] = [ rank += 1, score ]
65
+ end
66
+ [ total_hits, id_arrays ]
67
+ end
68
+
69
+
70
+ # determine all field names in the shared index
71
+ # TODO unused
72
+ # def single_index_field_names(models)
73
+ # @single_index_field_names ||= (
74
+ # searcher = Ferret::Search::Searcher.new(class_index_dir)
75
+ # if searcher.reader.respond_to?(:get_field_names)
76
+ # (searcher.reader.send(:get_field_names) - ['id', 'class_name']).to_a
77
+ # else
78
+ # puts <<-END
79
+ #unable to retrieve field names for class #{self.name}, please
80
+ #consider naming all indexed fields in your call to acts_as_ferret!
81
+ # END
82
+ # models.map { |m| m.content_columns.map { |col| col.name } }.flatten
83
+ # end
84
+ # )
85
+ #
86
+ # end
87
+
88
+ end
89
+ end
90
+
@@ -0,0 +1,63 @@
1
+ ################################################################################
2
+ module ActsAsFerret
3
+ module Remote
4
+
5
+ ################################################################################
6
+ # methods for becoming a daemon on Unix-like operating systems
7
+ module UnixDaemon
8
+
9
+ ################################################################################
10
+ def platform_daemon (&block)
11
+ safefork do
12
+ write_pid_file
13
+ trap("TERM") { exit(0) }
14
+ sess_id = Process.setsid
15
+ STDIN.reopen("/dev/null")
16
+ STDOUT.reopen("#{RAILS_ROOT}/log/ferret_server.out", "a")
17
+ STDERR.reopen(STDOUT)
18
+ block.call
19
+ end
20
+ end
21
+
22
+ ################################################################################
23
+ # stop the daemon, nicely at first, and then forcefully if necessary
24
+ def stop
25
+ pid = read_pid_file
26
+ raise "ferret_server doesn't appear to be running" unless pid
27
+ $stdout.puts("stopping ferret server...")
28
+ Process.kill("TERM", pid)
29
+ 30.times { Process.kill(0, pid); sleep(0.5) }
30
+ $stdout.puts("using kill -9 #{pid}")
31
+ Process.kill(9, pid)
32
+ rescue Errno::ESRCH => e
33
+ $stdout.puts("process #{pid} has stopped")
34
+ ensure
35
+ File.unlink(@cfg.pid_file) if File.exist?(@cfg.pid_file)
36
+ end
37
+
38
+ ################################################################################
39
+ def safefork (&block)
40
+ @fork_tries ||= 0
41
+ fork(&block)
42
+ rescue Errno::EWOULDBLOCK
43
+ raise if @fork_tries >= 20
44
+ @fork_tries += 1
45
+ sleep 5
46
+ retry
47
+ end
48
+
49
+ #################################################################################
50
+ # create the PID file and install an at_exit handler
51
+ def write_pid_file
52
+ open(@cfg.pid_file, "w") {|f| f << Process.pid << "\n"}
53
+ at_exit { File.unlink(@cfg.pid_file) if read_pid_file == Process.pid }
54
+ end
55
+
56
+ #################################################################################
57
+ def read_pid_file
58
+ File.read(@cfg.pid_file).to_i if File.exist?(@cfg.pid_file)
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,94 @@
1
+ # Ferret Win32 Service Daemon, called by Win 32 service,
2
+ # created by Herryanto Siatono <herryanto@pluitsolutions.com>
3
+ #
4
+ # see doc/README.win32 for usage instructions
5
+ #
6
+ require 'optparse'
7
+ require 'win32/service'
8
+ include Win32
9
+
10
+ # Read options
11
+ options = {}
12
+ ARGV.options do |opts|
13
+ opts.banner = 'Usage: ferret_daemon [options]'
14
+ opts.on("-l", "--log FILE", "Daemon log file") {|file| options[:log] = file }
15
+ opts.on("-c","--console","Run Ferret server on console.") {options[:console] = true}
16
+ opts.on_tail("-h","--help", "Show this help message") {puts opts; exit}
17
+ opts.on("-e", "--environment ENV ", "Rails environment") {|env|
18
+ options[:environment] = env
19
+ ENV['RAILS_ENV'] = env
20
+ }
21
+ opts.parse!
22
+ end
23
+
24
+ require File.dirname(__FILE__) + '/../config/environment'
25
+
26
+ # Ferret Win32 Service Daemon, called by Win 32 service,
27
+ # to run on the console, use -c or --console option.
28
+ module Ferret
29
+ class FerretDaemon < Daemon
30
+ # Standard logger to redirect STDOUT and STDERR to a log file
31
+ class FerretStandardLogger
32
+ def initialize(logger)
33
+ @logger = logger
34
+ end
35
+
36
+ def write(s)
37
+ @logger.info s
38
+ end
39
+ end
40
+
41
+ def initialize(options={})
42
+ @options = options
43
+
44
+ # initialize logger
45
+ if options[:log]
46
+ @logger = Logger.new @options[:log]
47
+ else
48
+ @logger = Logger.new RAILS_ROOT + "/log/ferret_service_#{RAILS_ENV}.log"
49
+ end
50
+
51
+ # redirect stout and stderr to Ferret logger if running as windows service
52
+ $stdout = $stderr = FerretStandardLogger.new(@logger) unless @options[:console]
53
+
54
+ log "Initializing FerretDaemon..."
55
+ if @options[:console]
56
+ self.service_init
57
+ self.service_main
58
+ end
59
+ end
60
+
61
+ def service_main
62
+ log "Service main enterred..."
63
+
64
+ while running?
65
+ log "Listening..."
66
+ sleep
67
+ end
68
+
69
+ log "Service main exit..."
70
+ end
71
+
72
+ def service_init
73
+ log "Starting Ferret DRb server..."
74
+ ActsAsFerret::Remote::Server.start
75
+ log "FerretDaemon started."
76
+ end
77
+
78
+ def service_stop
79
+ log "Stopping service..."
80
+ DRb.stop_service
81
+ log "FerretDaemon stopped."
82
+ end
83
+
84
+ def log(msg)
85
+ @logger.info msg
86
+ puts msg if @options[:console]
87
+ end
88
+ end
89
+ end
90
+
91
+ if __FILE__ == $0
92
+ d = Ferret::FerretDaemon.new(options)
93
+ d.mainloop
94
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require File.join(File.dirname(__FILE__), '../vendor/plugins/acts_as_ferret/lib/server_manager')
5
+ rescue LoadError
6
+ # try the gem
7
+ require 'rubygems'
8
+ gem 'acts_as_ferret'
9
+ require 'server_manager'
10
+ end
@@ -0,0 +1,178 @@
1
+ # Ferret Win32 Service Daemon install script
2
+ # created by Herryanto Siatono <herryanto@pluitsolutions.com>
3
+ #
4
+ # see doc/README.win32 for usage instructions
5
+ #
6
+ require 'optparse'
7
+ require 'win32/service'
8
+ include Win32
9
+
10
+ module Ferret
11
+ # Parse and validate service command and options
12
+ class FerretServiceCommand
13
+ COMMANDS = ['install', 'remove', 'start', 'stop', 'help']
14
+ BANNER = "Usage: ruby script/ferret_service <command> [options]"
15
+
16
+ attr_reader :options, :command
17
+
18
+ def initialize
19
+ @options = {}
20
+ end
21
+
22
+ def valid_command?
23
+ COMMANDS.include?@command
24
+ end
25
+
26
+ def valid_options?
27
+ @options[:name] and !@options[:name].empty?
28
+ end
29
+
30
+ def print_command_list
31
+ puts BANNER
32
+ puts "\nAvailable commands:\n"
33
+ puts COMMANDS.map {|cmd| " - #{cmd}\n"}
34
+ puts "\nUse option -h for each command to help."
35
+ exit
36
+ end
37
+
38
+ def validate_options
39
+ errors = []
40
+ errors << "Service name is required." unless @options[:name]
41
+
42
+ if (errors.size > 0)
43
+ errors << "Error found. Use: 'ruby script/ferret_service #{@command} -h' for to get help."
44
+ puts errors.join("\n")
45
+ exit
46
+ end
47
+ end
48
+
49
+ def run(args)
50
+ @command = args.shift
51
+ @command = @command.dup.downcase if @command
52
+
53
+ # validate command and options
54
+ print_command_list unless valid_command? or @command == 'help'
55
+
56
+ opts_parser = create_options_parser
57
+ begin
58
+ opts_parser.parse!(args)
59
+ rescue OptionParser::ParseError => e
60
+ puts e
61
+ puts opts_parser
62
+ end
63
+
64
+ # validate required options
65
+ validate_options
66
+ end
67
+
68
+ def create_options_parser
69
+ opts_parser = OptionParser.new
70
+ opts_parser.banner = BANNER
71
+ opts_parser.on("-n", "--name=NAME", "Service name") {|name| @options[:name] = name }
72
+ opts_parser.on_tail("-t", "--trace", "Display stack trace when exception thrown") { @options[:trace] = true }
73
+ opts_parser.on_tail("-h", "--help", "Show this help message") { puts opts_parser; exit }
74
+
75
+ if ['install'].include?@command
76
+ opts_parser.on("-d", "--display=NAME", "Service display name") {|name| @options[:display] = name }
77
+
78
+ opts_parser.on("-l", "--log FILE", "Service log file") {|file| @options[:log] = file }
79
+ opts_parser.on("-e", "--environment ENV ", "Rails environment") { |env|
80
+ @options[:environment] = env
81
+ ENV['RAILS_ENV'] = env
82
+ }
83
+ end
84
+ opts_parser
85
+ end
86
+ end
87
+
88
+ # Install, Remove, Start and Stop Ferret DRb server Win32 service
89
+ class FerretService
90
+ FERRET_DAEMON = 'ferret_daemon'
91
+
92
+ def initialize
93
+ end
94
+
95
+ def install
96
+ svc = Service.new
97
+
98
+ begin
99
+ if Service.exists?(@options[:name])
100
+ puts "Service name '#{@options[:name]}' already exists."
101
+ return
102
+ end
103
+
104
+ svc.create_service do |s|
105
+ s.service_name = @options[:name]
106
+ s.display_name = @options[:display]
107
+ s.binary_path_name = binary_path_name
108
+ s.dependencies = []
109
+ end
110
+
111
+ svc.close
112
+ puts "'#{@options[:name]}' service installed."
113
+ rescue => e
114
+ handle_error(e)
115
+ end
116
+ end
117
+
118
+ def remove
119
+ begin
120
+ Service.stop(@options[:name])
121
+ rescue
122
+ end
123
+
124
+ begin
125
+ Service.delete(@options[:name])
126
+ puts "'#{@options[:name]}' service removed."
127
+ rescue => e
128
+ handle_error(e)
129
+ end
130
+ end
131
+
132
+ def start
133
+ begin
134
+ Service.start(@options[:name])
135
+ puts "'#{@options[:name]}' successfully started."
136
+ rescue => e
137
+ handle_error(e)
138
+ end
139
+ end
140
+
141
+ def stop
142
+ begin
143
+ Service.stop(@options[:name])
144
+ puts "'#{@options[:name]}' successfully stopped.\n"
145
+ rescue => e
146
+ handle_error(e)
147
+ end
148
+ end
149
+
150
+ def run(args)
151
+ svc_cmd = FerretServiceCommand.new
152
+ svc_cmd.run(args)
153
+ @options = svc_cmd.options
154
+ self.send(svc_cmd.command.to_sym)
155
+ end
156
+
157
+ protected
158
+ def handle_error(e)
159
+ if @options[:trace]
160
+ raise e
161
+ else
162
+ puts e
163
+ end
164
+ end
165
+
166
+ def binary_path_name
167
+ path = ""
168
+ path << "#{ENV['RUBY_HOME']}/bin/" if ENV['RUBY_HOME']
169
+ path << "ruby.exe "
170
+ path << File.expand_path("script/" + FERRET_DAEMON)
171
+ path << " -e #{@options[:environment]} " if @options[:environment]
172
+ path << " -l #{@options[:log]} " if @options[:log]
173
+ path
174
+ end
175
+ end
176
+ end
177
+
178
+ Ferret::FerretService.new.run(ARGV)