thinking-sphinx 1.2.12
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENCE +20 -0
- data/README.textile +157 -0
- data/VERSION.yml +4 -0
- data/lib/thinking_sphinx.rb +211 -0
- data/lib/thinking_sphinx/active_record.rb +307 -0
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +48 -0
- data/lib/thinking_sphinx/active_record/delta.rb +87 -0
- data/lib/thinking_sphinx/active_record/has_many_association.rb +28 -0
- data/lib/thinking_sphinx/active_record/scopes.rb +39 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +42 -0
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +54 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +136 -0
- data/lib/thinking_sphinx/association.rb +164 -0
- data/lib/thinking_sphinx/attribute.rb +342 -0
- data/lib/thinking_sphinx/class_facet.rb +15 -0
- data/lib/thinking_sphinx/configuration.rb +282 -0
- data/lib/thinking_sphinx/core/array.rb +7 -0
- data/lib/thinking_sphinx/core/string.rb +15 -0
- data/lib/thinking_sphinx/deltas.rb +30 -0
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +50 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +68 -0
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +30 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +24 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +27 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +26 -0
- data/lib/thinking_sphinx/deploy/capistrano.rb +100 -0
- data/lib/thinking_sphinx/excerpter.rb +22 -0
- data/lib/thinking_sphinx/facet.rb +125 -0
- data/lib/thinking_sphinx/facet_search.rb +134 -0
- data/lib/thinking_sphinx/field.rb +82 -0
- data/lib/thinking_sphinx/index.rb +99 -0
- data/lib/thinking_sphinx/index/builder.rb +286 -0
- data/lib/thinking_sphinx/index/faux_column.rb +110 -0
- data/lib/thinking_sphinx/property.rb +162 -0
- data/lib/thinking_sphinx/rails_additions.rb +150 -0
- data/lib/thinking_sphinx/search.rb +707 -0
- data/lib/thinking_sphinx/search_methods.rb +421 -0
- data/lib/thinking_sphinx/source.rb +150 -0
- data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
- data/lib/thinking_sphinx/source/sql.rb +128 -0
- data/lib/thinking_sphinx/tasks.rb +165 -0
- data/rails/init.rb +14 -0
- data/spec/lib/thinking_sphinx/active_record/delta_spec.rb +130 -0
- data/spec/lib/thinking_sphinx/active_record/has_many_association_spec.rb +49 -0
- data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +96 -0
- data/spec/lib/thinking_sphinx/active_record_spec.rb +364 -0
- data/spec/lib/thinking_sphinx/association_spec.rb +239 -0
- data/spec/lib/thinking_sphinx/attribute_spec.rb +500 -0
- data/spec/lib/thinking_sphinx/configuration_spec.rb +268 -0
- data/spec/lib/thinking_sphinx/core/array_spec.rb +9 -0
- data/spec/lib/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/lib/thinking_sphinx/excerpter_spec.rb +49 -0
- data/spec/lib/thinking_sphinx/facet_search_spec.rb +176 -0
- data/spec/lib/thinking_sphinx/facet_spec.rb +333 -0
- data/spec/lib/thinking_sphinx/field_spec.rb +154 -0
- data/spec/lib/thinking_sphinx/index/builder_spec.rb +455 -0
- data/spec/lib/thinking_sphinx/index/faux_column_spec.rb +30 -0
- data/spec/lib/thinking_sphinx/index_spec.rb +45 -0
- data/spec/lib/thinking_sphinx/rails_additions_spec.rb +203 -0
- data/spec/lib/thinking_sphinx/search_methods_spec.rb +152 -0
- data/spec/lib/thinking_sphinx/search_spec.rb +1092 -0
- data/spec/lib/thinking_sphinx/source_spec.rb +227 -0
- data/spec/lib/thinking_sphinx_spec.rb +162 -0
- data/tasks/distribution.rb +50 -0
- data/tasks/rails.rake +1 -0
- data/tasks/testing.rb +83 -0
- data/vendor/after_commit/LICENSE +20 -0
- data/vendor/after_commit/README +16 -0
- data/vendor/after_commit/Rakefile +22 -0
- data/vendor/after_commit/init.rb +8 -0
- data/vendor/after_commit/lib/after_commit.rb +45 -0
- data/vendor/after_commit/lib/after_commit/active_record.rb +114 -0
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +103 -0
- data/vendor/after_commit/test/after_commit_test.rb +53 -0
- data/vendor/delayed_job/lib/delayed/job.rb +251 -0
- data/vendor/delayed_job/lib/delayed/message_sending.rb +7 -0
- data/vendor/delayed_job/lib/delayed/performable_method.rb +55 -0
- data/vendor/delayed_job/lib/delayed/worker.rb +54 -0
- data/vendor/riddle/lib/riddle.rb +30 -0
- data/vendor/riddle/lib/riddle/client.rb +635 -0
- data/vendor/riddle/lib/riddle/client/filter.rb +53 -0
- data/vendor/riddle/lib/riddle/client/message.rb +66 -0
- data/vendor/riddle/lib/riddle/client/response.rb +84 -0
- data/vendor/riddle/lib/riddle/configuration.rb +33 -0
- data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +48 -0
- data/vendor/riddle/lib/riddle/configuration/index.rb +142 -0
- data/vendor/riddle/lib/riddle/configuration/indexer.rb +19 -0
- data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
- data/vendor/riddle/lib/riddle/configuration/searchd.rb +25 -0
- data/vendor/riddle/lib/riddle/configuration/section.rb +43 -0
- data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
- data/vendor/riddle/lib/riddle/configuration/sql_source.rb +34 -0
- data/vendor/riddle/lib/riddle/configuration/xml_source.rb +28 -0
- data/vendor/riddle/lib/riddle/controller.rb +53 -0
- metadata +172 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'thinking_sphinx/deltas/default_delta'
|
2
|
+
require 'thinking_sphinx/deltas/delayed_delta'
|
3
|
+
require 'thinking_sphinx/deltas/datetime_delta'
|
4
|
+
|
5
|
+
module ThinkingSphinx
|
6
|
+
module Deltas
|
7
|
+
def self.parse(index)
|
8
|
+
delta_option = index.local_options.delete(:delta)
|
9
|
+
case delta_option
|
10
|
+
when TrueClass, :default
|
11
|
+
DefaultDelta.new index, index.local_options
|
12
|
+
when :delayed
|
13
|
+
DelayedDelta.new index, index.local_options
|
14
|
+
when :datetime
|
15
|
+
DatetimeDelta.new index, index.local_options
|
16
|
+
when FalseClass, nil
|
17
|
+
nil
|
18
|
+
else
|
19
|
+
if delta_option.is_a?(String)
|
20
|
+
delta_option = Kernel.const_get(delta_option)
|
21
|
+
end
|
22
|
+
if delta_option.ancestors.include?(ThinkingSphinx::Deltas::DefaultDelta)
|
23
|
+
delta_option.new index, index.local_options
|
24
|
+
else
|
25
|
+
raise "Unknown delta type"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module Deltas
|
3
|
+
class DatetimeDelta < ThinkingSphinx::Deltas::DefaultDelta
|
4
|
+
attr_accessor :column, :threshold
|
5
|
+
|
6
|
+
def initialize(index, options)
|
7
|
+
@index = index
|
8
|
+
@column = options.delete(:delta_column) || :updated_at
|
9
|
+
@threshold = options.delete(:threshold) || 1.day
|
10
|
+
end
|
11
|
+
|
12
|
+
def index(model, instance = nil)
|
13
|
+
# do nothing
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def delayed_index(model)
|
18
|
+
config = ThinkingSphinx::Configuration.instance
|
19
|
+
rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
|
20
|
+
|
21
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{delta_index_name model}`
|
22
|
+
output += `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} --merge #{core_index_name model} #{delta_index_name model} --merge-dst-range sphinx_deleted 0 0`
|
23
|
+
puts output unless ThinkingSphinx.suppress_delta_output?
|
24
|
+
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def toggle(instance)
|
29
|
+
# do nothing
|
30
|
+
end
|
31
|
+
|
32
|
+
def toggled(instance)
|
33
|
+
instance.send(@column) > @threshold.ago
|
34
|
+
end
|
35
|
+
|
36
|
+
def reset_query(model)
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def clause(model, toggled)
|
41
|
+
if toggled
|
42
|
+
"#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
|
43
|
+
" > #{adapter.time_difference(@threshold)}"
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module Deltas
|
3
|
+
class DefaultDelta
|
4
|
+
attr_accessor :column
|
5
|
+
|
6
|
+
def initialize(index, options)
|
7
|
+
@index = index
|
8
|
+
@column = options.delete(:delta_column) || :delta
|
9
|
+
end
|
10
|
+
|
11
|
+
def index(model, instance = nil)
|
12
|
+
return true unless ThinkingSphinx.updates_enabled? &&
|
13
|
+
ThinkingSphinx.deltas_enabled?
|
14
|
+
return true if instance && !toggled(instance)
|
15
|
+
|
16
|
+
config = ThinkingSphinx::Configuration.instance
|
17
|
+
client = Riddle::Client.new config.address, config.port
|
18
|
+
rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
|
19
|
+
|
20
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{delta_index_name model}`
|
21
|
+
puts(output) unless ThinkingSphinx.suppress_delta_output?
|
22
|
+
|
23
|
+
client.update(
|
24
|
+
core_index_name(model),
|
25
|
+
['sphinx_deleted'],
|
26
|
+
{instance.sphinx_document_id => [1]}
|
27
|
+
) if instance && ThinkingSphinx.sphinx_running? && instance.in_both_indexes?
|
28
|
+
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def toggle(instance)
|
33
|
+
instance.delta = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def toggled(instance)
|
37
|
+
instance.delta
|
38
|
+
end
|
39
|
+
|
40
|
+
def reset_query(model)
|
41
|
+
"UPDATE #{model.quoted_table_name} SET " +
|
42
|
+
"#{model.connection.quote_column_name(@column.to_s)} = #{adapter.boolean(false)} " +
|
43
|
+
"WHERE #{model.connection.quote_column_name(@column.to_s)} = #{adapter.boolean(true)}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def clause(model, toggled)
|
47
|
+
"#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
|
48
|
+
" = #{adapter.boolean(toggled)}"
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def core_index_name(model)
|
54
|
+
"#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_core"
|
55
|
+
end
|
56
|
+
|
57
|
+
def delta_index_name(model)
|
58
|
+
"#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_delta"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def adapter
|
64
|
+
@adapter = @index.model.sphinx_database_adapter
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'delayed/job'
|
2
|
+
|
3
|
+
require 'thinking_sphinx/deltas/delayed_delta/delta_job'
|
4
|
+
require 'thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job'
|
5
|
+
require 'thinking_sphinx/deltas/delayed_delta/job'
|
6
|
+
|
7
|
+
module ThinkingSphinx
|
8
|
+
module Deltas
|
9
|
+
class DelayedDelta < ThinkingSphinx::Deltas::DefaultDelta
|
10
|
+
def index(model, instance = nil)
|
11
|
+
return true unless ThinkingSphinx.updates_enabled? && ThinkingSphinx.deltas_enabled?
|
12
|
+
return true if instance && !toggled(instance)
|
13
|
+
|
14
|
+
ThinkingSphinx::Deltas::Job.enqueue(
|
15
|
+
ThinkingSphinx::Deltas::DeltaJob.new(delta_index_name(model)),
|
16
|
+
ThinkingSphinx::Configuration.instance.delayed_job_priority
|
17
|
+
)
|
18
|
+
|
19
|
+
Delayed::Job.enqueue(
|
20
|
+
ThinkingSphinx::Deltas::FlagAsDeletedJob.new(
|
21
|
+
core_index_name(model), instance.sphinx_document_id
|
22
|
+
),
|
23
|
+
ThinkingSphinx::Configuration.instance.delayed_job_priority
|
24
|
+
) if instance
|
25
|
+
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module Deltas
|
3
|
+
class DeltaJob
|
4
|
+
attr_accessor :index
|
5
|
+
|
6
|
+
def initialize(index)
|
7
|
+
@index = index
|
8
|
+
end
|
9
|
+
|
10
|
+
def perform
|
11
|
+
return true unless ThinkingSphinx.updates_enabled? &&
|
12
|
+
ThinkingSphinx.deltas_enabled?
|
13
|
+
|
14
|
+
config = ThinkingSphinx::Configuration.instance
|
15
|
+
client = Riddle::Client.new config.address, config.port
|
16
|
+
|
17
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} --rotate #{index}`
|
18
|
+
puts output unless ThinkingSphinx.suppress_delta_output?
|
19
|
+
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module Deltas
|
3
|
+
class FlagAsDeletedJob
|
4
|
+
attr_accessor :index, :document_id
|
5
|
+
|
6
|
+
def initialize(index, document_id)
|
7
|
+
@index, @document_id = index, document_id
|
8
|
+
end
|
9
|
+
|
10
|
+
def perform
|
11
|
+
return true unless ThinkingSphinx.updates_enabled?
|
12
|
+
|
13
|
+
config = ThinkingSphinx::Configuration.instance
|
14
|
+
client = Riddle::Client.new config.address, config.port
|
15
|
+
|
16
|
+
client.update(
|
17
|
+
@index,
|
18
|
+
['sphinx_deleted'],
|
19
|
+
{@document_id => [1]}
|
20
|
+
) if ThinkingSphinx.sphinx_running? &&
|
21
|
+
ThinkingSphinx::Search.search_for_id(@document_id, @index)
|
22
|
+
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module Deltas
|
3
|
+
class Job < Delayed::Job
|
4
|
+
def self.enqueue(object, priority = 0)
|
5
|
+
super unless duplicates_exist(object)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.cancel_thinking_sphinx_jobs
|
9
|
+
if connection.tables.include?("delayed_jobs")
|
10
|
+
delete_all("handler LIKE '--- !ruby/object:ThinkingSphinx::Deltas::%'")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.duplicates_exist(object)
|
17
|
+
count(
|
18
|
+
:conditions => {
|
19
|
+
:handler => object.to_yaml,
|
20
|
+
:locked_at => nil
|
21
|
+
}
|
22
|
+
) > 0
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
+
namespace :thinking_sphinx do
|
3
|
+
namespace :install do
|
4
|
+
desc <<-DESC
|
5
|
+
Install Sphinx by source
|
6
|
+
|
7
|
+
If Postgres is available, Sphinx will use it.
|
8
|
+
|
9
|
+
If the variable :thinking_sphinx_configure_args is set, it will
|
10
|
+
be passed to the Sphinx configure script. You can use this to
|
11
|
+
install Sphinx in a non-standard location:
|
12
|
+
|
13
|
+
set :thinking_sphinx_configure_args, "--prefix=$HOME/software"
|
14
|
+
DESC
|
15
|
+
|
16
|
+
task :sphinx do
|
17
|
+
with_postgres = false
|
18
|
+
begin
|
19
|
+
run "which pg_config" do |channel, stream, data|
|
20
|
+
with_postgres = !(data.nil? || data == "")
|
21
|
+
end
|
22
|
+
rescue Capistrano::CommandError => e
|
23
|
+
puts "Continuing despite error: #{e.message}"
|
24
|
+
end
|
25
|
+
|
26
|
+
args = []
|
27
|
+
if with_postgres
|
28
|
+
run "pg_config --pkgincludedir" do |channel, stream, data|
|
29
|
+
args << "--with-pgsql=#{data}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
args << fetch(:thinking_sphinx_configure_args, '')
|
33
|
+
|
34
|
+
commands = <<-CMD
|
35
|
+
wget -q http://www.sphinxsearch.com/downloads/sphinx-0.9.8.1.tar.gz >> sphinx.log
|
36
|
+
tar xzvf sphinx-0.9.8.1.tar.gz
|
37
|
+
cd sphinx-0.9.8.1
|
38
|
+
./configure #{args.join(" ")}
|
39
|
+
make
|
40
|
+
#{try_sudo} make install
|
41
|
+
rm -rf sphinx-0.9.8.1 sphinx-0.9.8.1.tar.gz
|
42
|
+
CMD
|
43
|
+
run commands.split(/\n\s+/).join(" && ")
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Install Thinking Sphinx as a gem from GitHub"
|
47
|
+
task :ts do
|
48
|
+
run "#{try_sudo} gem install thinking-sphinx --source http://gemcutter.org"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Generate the Sphinx configuration file"
|
53
|
+
task :configure do
|
54
|
+
rake "thinking_sphinx:configure"
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "Index data"
|
58
|
+
task :index do
|
59
|
+
rake "thinking_sphinx:index"
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Start the Sphinx daemon"
|
63
|
+
task :start do
|
64
|
+
configure
|
65
|
+
rake "thinking_sphinx:start"
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Stop the Sphinx daemon"
|
69
|
+
task :stop do
|
70
|
+
configure
|
71
|
+
rake "thinking_sphinx:stop"
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "Stop and then start the Sphinx daemon"
|
75
|
+
task :restart do
|
76
|
+
stop
|
77
|
+
start
|
78
|
+
end
|
79
|
+
|
80
|
+
desc "Stop, re-index and then start the Sphinx daemon"
|
81
|
+
task :rebuild do
|
82
|
+
stop
|
83
|
+
index
|
84
|
+
start
|
85
|
+
end
|
86
|
+
|
87
|
+
desc "Add the shared folder for sphinx files for the production environment"
|
88
|
+
task :shared_sphinx_folder, :roles => :web do
|
89
|
+
run "mkdir -p #{shared_path}/db/sphinx/production"
|
90
|
+
end
|
91
|
+
|
92
|
+
def rake(*tasks)
|
93
|
+
rails_env = fetch(:rails_env, "production")
|
94
|
+
rake = fetch(:rake, "rake")
|
95
|
+
tasks.each do |t|
|
96
|
+
run "cd #{current_path}; #{rake} RAILS_ENV=#{rails_env} #{t}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
class Excerpter
|
3
|
+
CoreMethods = %w( kind_of? object_id respond_to? should should_not stub! )
|
4
|
+
# Hide most methods, to allow them to be passed through to the instance.
|
5
|
+
instance_methods.select { |method|
|
6
|
+
method.to_s[/^__/].nil? && !CoreMethods.include?(method.to_s)
|
7
|
+
}.each { |method|
|
8
|
+
undef_method method
|
9
|
+
}
|
10
|
+
|
11
|
+
def initialize(search, instance)
|
12
|
+
@search = search
|
13
|
+
@instance = instance
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(method, *args, &block)
|
17
|
+
string = @instance.send(method, *args, &block).to_s
|
18
|
+
|
19
|
+
@search.excerpt_for(string, @instance.class)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
class Facet
|
3
|
+
attr_reader :property
|
4
|
+
|
5
|
+
def initialize(property)
|
6
|
+
@property = property
|
7
|
+
|
8
|
+
if property.columns.length != 1
|
9
|
+
raise "Can't translate Facets on multiple-column field or attribute"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.name_for(facet)
|
14
|
+
case facet
|
15
|
+
when Facet
|
16
|
+
facet.name
|
17
|
+
when String, Symbol
|
18
|
+
facet.to_s.gsub(/(_facet|_crc)$/,'').to_sym
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.attribute_name_for(name)
|
23
|
+
name.to_s == 'class' ? 'class_crc' : "#{name}_facet"
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.attribute_name_from_value(name, value)
|
27
|
+
case value
|
28
|
+
when String
|
29
|
+
attribute_name_for(name)
|
30
|
+
when Array
|
31
|
+
if value.all? { |val| val.is_a?(Integer) }
|
32
|
+
name
|
33
|
+
else
|
34
|
+
attribute_name_for(name)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.translate?(property)
|
42
|
+
return true if property.is_a?(Field)
|
43
|
+
|
44
|
+
case property.type
|
45
|
+
when :string
|
46
|
+
true
|
47
|
+
when :integer, :boolean, :datetime, :float
|
48
|
+
false
|
49
|
+
when :multi
|
50
|
+
!property.all_ints?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def name
|
55
|
+
property.unique_name
|
56
|
+
end
|
57
|
+
|
58
|
+
def attribute_name
|
59
|
+
if translate?
|
60
|
+
Facet.attribute_name_for(@property.unique_name)
|
61
|
+
else
|
62
|
+
@property.unique_name.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def translate?
|
67
|
+
Facet.translate?(@property)
|
68
|
+
end
|
69
|
+
|
70
|
+
def type
|
71
|
+
@property.is_a?(Field) ? :string : @property.type
|
72
|
+
end
|
73
|
+
|
74
|
+
def value(object, attribute_value)
|
75
|
+
return translate(object, attribute_value) if translate? || float?
|
76
|
+
|
77
|
+
case @property.type
|
78
|
+
when :datetime
|
79
|
+
Time.at(attribute_value)
|
80
|
+
when :boolean
|
81
|
+
attribute_value > 0
|
82
|
+
else
|
83
|
+
attribute_value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_s
|
88
|
+
name
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def translate(object, attribute_value)
|
94
|
+
objects = source_objects(object)
|
95
|
+
return nil if objects.nil? || objects.empty?
|
96
|
+
|
97
|
+
if objects.length > 1
|
98
|
+
objects.collect { |item| item.send(column.__name) }.detect { |item|
|
99
|
+
item.to_crc32 == attribute_value
|
100
|
+
}
|
101
|
+
else
|
102
|
+
objects.first.send(column.__name)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def source_objects(object)
|
107
|
+
column.__stack.each { |method|
|
108
|
+
object = Array(object).collect { |item|
|
109
|
+
item.send(method)
|
110
|
+
}.flatten.compact
|
111
|
+
|
112
|
+
return nil if object.empty?
|
113
|
+
}
|
114
|
+
Array(object)
|
115
|
+
end
|
116
|
+
|
117
|
+
def column
|
118
|
+
@property.columns.first
|
119
|
+
end
|
120
|
+
|
121
|
+
def float?
|
122
|
+
@property.type == :float
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|