lucid_works 0.3.9 → 0.4.9
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/README.rdoc +33 -3
- data/config/locales/en.yml +21 -12
- data/lib/lucid_works.rb +10 -0
- data/lib/lucid_works/associations.rb +14 -12
- data/lib/lucid_works/base.rb +13 -13
- data/lib/lucid_works/collection.rb +65 -5
- data/lib/lucid_works/collection/activity.rb +33 -0
- data/lib/lucid_works/collection/activity/history.rb +20 -0
- data/lib/lucid_works/collection/activity/status.rb +14 -0
- data/lib/lucid_works/collection/settings.rb +28 -8
- data/lib/lucid_works/crawler.rb +3 -3
- data/lib/lucid_works/datasource.rb +29 -3
- data/lib/lucid_works/datasource/job.rb +9 -0
- data/lib/lucid_works/datasource/status.rb +6 -11
- data/lib/lucid_works/patch_time.rb +13 -0
- data/lib/lucid_works/schema.rb +36 -8
- data/lib/lucid_works/utils.rb +22 -0
- data/lib/lucid_works/version.rb +1 -1
- data/lucid_works.gemspec +2 -0
- data/spec/lib/lucid_works/associations_spec.rb +12 -1
- data/spec/lib/lucid_works/base_spec.rb +26 -10
- data/spec/lib/lucid_works/collection/activity/history_spec.rb +33 -0
- data/spec/lib/lucid_works/collection/activity/status_spec.rb +20 -0
- data/spec/lib/lucid_works/collection/activity_spec.rb +88 -0
- data/spec/lib/lucid_works/collection/prime_activities_spec.rb +86 -0
- data/spec/lib/lucid_works/collection_spec.rb +140 -1
- data/spec/lib/lucid_works/datasource/history_spec.rb +11 -7
- data/spec/lib/lucid_works/datasource/status_spec.rb +64 -32
- data/spec/lib/lucid_works/datasource_spec.rb +48 -13
- data/spec/lib/lucid_works/schema_spec.rb +56 -4
- data/spec/lib/lucid_works/utils_spec.rb +62 -0
- data/spec/spec_helper.rb +17 -14
- metadata +41 -3
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'lucid_works/field'
|
2
|
+
|
1
3
|
module LucidWorks
|
2
4
|
class Collection
|
3
5
|
|
@@ -5,18 +7,36 @@ module LucidWorks
|
|
5
7
|
self.singleton = true
|
6
8
|
belongs_to :collection
|
7
9
|
|
8
|
-
DEDUP_OPTIONS
|
10
|
+
DEDUP_OPTIONS = %w{ off overwrite tag }
|
11
|
+
QUERY_PARSERS = %w{ lucid dismax edismax lucene }
|
12
|
+
DEFAULT_SORTS = %w{ relevance date random }
|
13
|
+
FEEDBACK_EMPHASIS = %w{ relevancy recall }
|
9
14
|
|
10
15
|
schema do
|
11
|
-
|
12
|
-
|
13
|
-
:type => :string
|
16
|
+
# Indexing Settings
|
17
|
+
attribute :unknown_type_handling, :string, :values => LucidWorks::Field::TYPES
|
14
18
|
attribute :de_duplication, :string, :values => DEDUP_OPTIONS
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
+
attribute :index_time_stopwords, :boolean
|
20
|
+
|
21
|
+
# Querying Settings
|
22
|
+
attribute :unsupervised_feedback_emphasis, :string, :values => FEEDBACK_EMPHASIS
|
23
|
+
attribute :default_sort, :string, :values => DEFAULT_SORTS
|
24
|
+
attribute :query_parser, :string, :values => QUERY_PARSERS
|
25
|
+
attributes :spellcheck, :display_facets, :unsupervised_feedback, :query_time_stopwords,
|
26
|
+
:auto_complete, :boost_recent, :show_similar, :query_time_synonyms,
|
27
|
+
:type => :boolean
|
28
|
+
attributes :stopword_list, :boosts, :synonym_list # Arrays
|
29
|
+
|
30
|
+
# Click Settings
|
31
|
+
attributes :click_boost_field, :click_boost_data, :type => :string
|
32
|
+
attribute :click_enabled, :boolean
|
33
|
+
|
34
|
+
# Other Settings
|
35
|
+
attribute :ssl, :boolean
|
19
36
|
attribute :elevations # Hash
|
37
|
+
|
38
|
+
# Distrubuted Search Settings
|
39
|
+
attributes :search_server_list, :update_server_list # Arrays
|
20
40
|
end
|
21
41
|
end
|
22
42
|
end
|
data/lib/lucid_works/crawler.rb
CHANGED
@@ -4,12 +4,12 @@ module LucidWorks
|
|
4
4
|
belongs_to :collection
|
5
5
|
has_many :histories, :class_name => :history
|
6
6
|
has_one :status, :schedule, :crawldata
|
7
|
-
has_one :index, :has_content => false
|
7
|
+
has_one :index, :job, :has_content => false
|
8
8
|
|
9
9
|
schema do
|
10
10
|
# common
|
11
11
|
attributes :name, :type, :crawler
|
12
|
-
attributes :crawl_depth, :max_bytes, :type => :integer
|
12
|
+
attributes :crawl_depth, :max_bytes, :max_docs, :type => :integer
|
13
13
|
attribute :include_paths
|
14
14
|
attribute :exclude_paths
|
15
15
|
attribute :mapping # Hash
|
@@ -20,6 +20,9 @@ module LucidWorks
|
|
20
20
|
# file
|
21
21
|
attribute :path
|
22
22
|
attribute :follow_links, :boolean
|
23
|
+
|
24
|
+
# new un-evaluated
|
25
|
+
# attributes :commitWithin
|
23
26
|
end
|
24
27
|
|
25
28
|
validates_presence_of :type, :crawler, :name, :crawl_depth
|
@@ -28,7 +31,7 @@ module LucidWorks
|
|
28
31
|
|
29
32
|
before_save :remove_blank_max_bytes
|
30
33
|
|
31
|
-
TYPES = %w{ file web solrxml jdbc sharepoint }
|
34
|
+
TYPES = %w{ external file lwelogs web solrxml jdbc sharepoint }
|
32
35
|
BOUNDS = %w{ tree none }
|
33
36
|
CRAWLERS = {
|
34
37
|
# Later we may change these to be arrays if we decide to support more than one choice
|
@@ -45,6 +48,29 @@ module LucidWorks
|
|
45
48
|
index.destroy
|
46
49
|
end
|
47
50
|
|
51
|
+
def editable?
|
52
|
+
# lwelogs is not editable
|
53
|
+
type != 'lwelogs'
|
54
|
+
end
|
55
|
+
|
56
|
+
def destroyable?
|
57
|
+
# Don't let user destroy 'lwelogs'
|
58
|
+
type != 'lwelogs'
|
59
|
+
end
|
60
|
+
|
61
|
+
def crawlable?
|
62
|
+
# Don't let user schedule crawl of 'lwelogs'
|
63
|
+
!%w{ external lwelogs }.include?(type)
|
64
|
+
end
|
65
|
+
|
66
|
+
def start_crawl!
|
67
|
+
job.save
|
68
|
+
end
|
69
|
+
|
70
|
+
def stop_crawl!
|
71
|
+
job.destroy
|
72
|
+
end
|
73
|
+
|
48
74
|
def t_type
|
49
75
|
I18n.t(type, :scope => 'activemodel.models.lucid_works.datasource.type')
|
50
76
|
end
|
@@ -5,15 +5,16 @@ module LucidWorks
|
|
5
5
|
self.singleton = true
|
6
6
|
belongs_to :datasource
|
7
7
|
|
8
|
-
schema do
|
9
|
-
attributes :crawlStarted, :crawlState, :crawlStopped, :jobId
|
10
|
-
attributes :numUnchanged, :numUpdated, :numNew, :numFailed, :numDeleted, :type => :integer
|
11
|
-
end
|
12
|
-
|
13
8
|
STOPPED_STATES = %w{ IDLE STOPPED ABORTED EXCEPTION FINISHED }
|
14
9
|
POST_PROCESSING_STATES = %w{ STOPPING ABORTING }
|
15
10
|
CRAWLSTATES = STOPPED_STATES + [ 'RUNNING' ] + POST_PROCESSING_STATES
|
16
11
|
|
12
|
+
schema do
|
13
|
+
attribute :crawlState, :string, :values => CRAWLSTATES
|
14
|
+
attributes :crawlStarted, :crawlStopped, :jobId
|
15
|
+
attributes :numUnchanged, :numUpdated, :numNew, :numFailed, :numDeleted, :numTotal, :type => :integer
|
16
|
+
end
|
17
|
+
|
17
18
|
# Create predicate methods for all the crawl states
|
18
19
|
CRAWLSTATES.each do |state|
|
19
20
|
method_name = state.downcase + "?"
|
@@ -36,12 +37,6 @@ module LucidWorks
|
|
36
37
|
numUpdated + numNew + numUnchanged
|
37
38
|
end
|
38
39
|
|
39
|
-
def t_crawl_state
|
40
|
-
I18n.translate(crawlState.downcase,
|
41
|
-
:scope => 'activemodel.models.lucid_works.datasource.status.crawl_state',
|
42
|
-
:default => crawlState)
|
43
|
-
end
|
44
|
-
|
45
40
|
def crawl_started
|
46
41
|
Time.iso8601 crawlStarted
|
47
42
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
class Time
|
4
|
+
class << self
|
5
|
+
# Ruby's 'Time.is08601 can't handle timezone offsets expressed as +nnnn.
|
6
|
+
# It prefers +nn:nn. Morph the former to the latter.
|
7
|
+
def iso8601_with_support_for_timezones_without_colons(datetime_string)
|
8
|
+
iso8601_without_support_for_timezones_without_colons(datetime_string.gsub /([+-])(\d\d)(\d\d)$/, '\1\2:\3')
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method_chain :iso8601, :support_for_timezones_without_colons
|
12
|
+
end
|
13
|
+
end
|
data/lib/lucid_works/schema.rb
CHANGED
@@ -1,10 +1,30 @@
|
|
1
1
|
module LucidWorks
|
2
2
|
|
3
|
+
# Specify an attributes for a model.
|
3
4
|
class Schema < ActiveSupport::HashWithIndifferentAccess
|
4
5
|
|
5
|
-
|
6
|
+
def initialize
|
7
|
+
@primary_key = :id
|
8
|
+
@dynamic_attributes = true
|
9
|
+
end
|
10
|
+
|
11
|
+
def primary_key(key=nil)
|
12
|
+
@primary_key = key.to_sym if key
|
13
|
+
@primary_key
|
14
|
+
end
|
15
|
+
|
16
|
+
# Specify whether attributes unrecognized during retrieval should raise an error (false)
|
17
|
+
# or cause a new attribute to be added to the schema (true) [default].
|
18
|
+
#
|
19
|
+
def dynamic_attributes(on=nil)
|
20
|
+
@dynamic_attributes = !!on unless on.nil?
|
21
|
+
@dynamic_attributes
|
22
|
+
end
|
23
|
+
|
24
|
+
alias :dynamic_attributes? :dynamic_attributes
|
6
25
|
|
7
26
|
def attribute(name, type=:string, options={})
|
27
|
+
primary_key name if options.delete(:primary_key)
|
8
28
|
self[name] = options.merge(:type => type)
|
9
29
|
end
|
10
30
|
|
@@ -13,7 +33,7 @@ module LucidWorks
|
|
13
33
|
# schema do
|
14
34
|
# attributes :first_name, :last_name, :type => :string
|
15
35
|
# end
|
16
|
-
|
36
|
+
#
|
17
37
|
def attributes(*attributes_and_options)
|
18
38
|
options = attributes_and_options.last.is_a?(Hash) ? attributes_and_options.pop : {}
|
19
39
|
attributes = attributes_and_options
|
@@ -26,6 +46,10 @@ module LucidWorks
|
|
26
46
|
has_key? sanitize_identifier(name)
|
27
47
|
end
|
28
48
|
|
49
|
+
def attrs_to_omit_during_update
|
50
|
+
select { |k,v| v['omit_during_update'] == true }.keys
|
51
|
+
end
|
52
|
+
|
29
53
|
# Used for classes that have no predefined schema.
|
30
54
|
# When the class is retrieved.
|
31
55
|
def add_attribute(klass, name, type=:string) # :nodoc:
|
@@ -47,10 +71,6 @@ module LucidWorks
|
|
47
71
|
def #{attribute} # def foo
|
48
72
|
@attributes[:#{attribute}] # @attributes[:foo]
|
49
73
|
end # end
|
50
|
-
|
51
|
-
def #{attribute}=(new_value) # def foo=(new_value)
|
52
|
-
@attributes[:#{attribute}] = new_value # @attributes[:foo] = new_value
|
53
|
-
end # end
|
54
74
|
EOF
|
55
75
|
|
56
76
|
if self[attribute][:type] == :boolean
|
@@ -58,12 +78,20 @@ module LucidWorks
|
|
58
78
|
def #{attribute}? # def foo?
|
59
79
|
@attributes[:#{attribute}] # @attributes[:foo]
|
60
80
|
end # end
|
81
|
+
|
82
|
+
def #{attribute}=(new_value) # def foo=(new_value)
|
83
|
+
@attributes[:#{attribute}] = to_bool(new_value) # @attributes[:foo] = to_bool(new_value)
|
84
|
+
end # end
|
85
|
+
EOF
|
86
|
+
else
|
87
|
+
klass.class_eval <<-EOF, __FILE__, __LINE__+1
|
88
|
+
def #{attribute}=(new_value) # def foo=(new_value)
|
89
|
+
@attributes[:#{attribute}] = new_value # @attributes[:foo] = new_value
|
90
|
+
end # end
|
61
91
|
EOF
|
62
92
|
end
|
63
93
|
end
|
64
94
|
|
65
|
-
private
|
66
|
-
|
67
95
|
# Change any characters illegal for an identifier to _
|
68
96
|
def sanitize_identifier(identifier) # :nodoc:
|
69
97
|
identifier.to_s.gsub(/[^\w]/, '_').to_sym
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module LucidWorks
|
2
|
+
module Utils
|
3
|
+
module BoolConverter
|
4
|
+
TRUE_VALUES = %w(t true 1 yes)
|
5
|
+
FALSE_VALUES = %w(f false 0 no)
|
6
|
+
|
7
|
+
def to_bool(thing)
|
8
|
+
if thing.kind_of?(TrueClass) || thing.kind_of?(FalseClass)
|
9
|
+
thing
|
10
|
+
elsif thing.kind_of?(String)
|
11
|
+
raise "Sorry, I can't to_bool \"#{thing}\"" unless (TRUE_VALUES + FALSE_VALUES).include?(thing.downcase)
|
12
|
+
TRUE_VALUES.include?(thing.downcase)
|
13
|
+
elsif thing.kind_of?(Integer)
|
14
|
+
raise "Sorry, I can't to_bool #{thing}" unless [0, 1].include?(thing)
|
15
|
+
thing == 1
|
16
|
+
else
|
17
|
+
raise "Sorry, I can't to_bool things of class #{thing.class.name}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/lucid_works/version.rb
CHANGED
data/lucid_works.gemspec
CHANGED
@@ -30,9 +30,20 @@ describe LucidWorks::Associations do
|
|
30
30
|
describe ".has_one" do
|
31
31
|
context "without content" do
|
32
32
|
describe "#<child>" do
|
33
|
+
it "should call child! the first time then return the cached value thereafter" do
|
34
|
+
mock_launch_party = double('launch_party')
|
35
|
+
LaunchParty.should_receive(:new).once.and_return(mock_launch_party)
|
36
|
+
|
37
|
+
@blog.launch_party.should == mock_launch_party
|
38
|
+
@blog.launch_party.should == mock_launch_party
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#<child!>" do
|
33
43
|
it "should build a new model, and not call REST API to retrieve" do
|
34
44
|
LaunchParty.should_not_receive(:find)
|
35
|
-
|
45
|
+
|
46
|
+
launch_party = @blog.launch_party!
|
36
47
|
|
37
48
|
launch_party.should be_a(LaunchParty)
|
38
49
|
launch_party.should be_persisted # All singletons are always persisted
|
@@ -38,10 +38,6 @@ describe LucidWorks::Base do
|
|
38
38
|
|
39
39
|
describe ".schema" do
|
40
40
|
context "for a class with a schema" do
|
41
|
-
it "should set has_schema" do
|
42
|
-
WidgetWithSchema.has_schema.should be_true
|
43
|
-
end
|
44
|
-
|
45
41
|
it "should have accessors for the attributes defined in the schema" do
|
46
42
|
[:name, :name=, :frooble, :frooble=].each do |method|
|
47
43
|
WidgetWithSchema.new(:parent => @server).should respond_to(method)
|
@@ -66,10 +62,7 @@ describe LucidWorks::Base do
|
|
66
62
|
end
|
67
63
|
end
|
68
64
|
|
69
|
-
context "for a class
|
70
|
-
it "should return false for has_schema" do
|
71
|
-
WidgetWithoutSchema.has_schema.should be_false
|
72
|
-
end
|
65
|
+
context "for a class with default schema (i.e. dynamic_attributes == true)" do
|
73
66
|
|
74
67
|
it "should create accessors upon retrieval" do
|
75
68
|
widget = WidgetWithoutSchema.new(:parent => @server)
|
@@ -354,7 +347,7 @@ describe LucidWorks::Base do
|
|
354
347
|
it "should create an array of arrays suitable for a form select" do
|
355
348
|
# Use Collection::Settings because we know de_duplication as translations
|
356
349
|
LucidWorks::Collection::Settings.to_select(:de_duplication).should ==
|
357
|
-
[['
|
350
|
+
[['Off', 'off'], ['Overwrite', 'overwrite'], ['Tag', 'tag']]
|
358
351
|
end
|
359
352
|
end
|
360
353
|
end
|
@@ -472,6 +465,27 @@ describe LucidWorks::Base do
|
|
472
465
|
@widget.save
|
473
466
|
end
|
474
467
|
end
|
468
|
+
|
469
|
+
context "for a persisted instance of a model with an attribute marked :omit_during_update" do
|
470
|
+
before do
|
471
|
+
class WidgetWithOmit < LucidWorks::Base
|
472
|
+
schema do
|
473
|
+
attribute :foo, :string
|
474
|
+
attribute :bar, :string, :omit_during_update => true
|
475
|
+
end
|
476
|
+
end
|
477
|
+
@widget_attrs = { :foo => 'frooble', :bar => 'barfalicious' }
|
478
|
+
@widget_with_omit = WidgetWithOmit.new(@widget_attrs.merge :id => 1234, :parent => @server, :persisted => true)
|
479
|
+
end
|
480
|
+
|
481
|
+
it "should omit said attribute when performing the save" do
|
482
|
+
expected_json = @widget_attrs.reject { |k,v| k.to_sym == :bar }.to_json
|
483
|
+
correct_url = "#{@fake_server_uri}/widget_with_omits/1234"
|
484
|
+
RestClient.should_receive(:put).with(correct_url, expected_json, :content_type => :json)
|
485
|
+
|
486
|
+
@widget_with_omit.save
|
487
|
+
end
|
488
|
+
end
|
475
489
|
end
|
476
490
|
|
477
491
|
describe "#update_attributes" do
|
@@ -507,7 +521,9 @@ describe LucidWorks::Base do
|
|
507
521
|
describe "for a model with primary key other than 'id'" do
|
508
522
|
before :all do
|
509
523
|
class NamedWidget < LucidWorks::Base
|
510
|
-
|
524
|
+
schema do
|
525
|
+
primary_key :name
|
526
|
+
end
|
511
527
|
end
|
512
528
|
NAMED_WIDGET1_JSON = '{"name":"widget1","size":"small"}'
|
513
529
|
NAMED_WIDGET2_JSON = '{"name":"widget2","size":"medium"}'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
|
4
|
+
describe LucidWorks::Collection::Activity::History do
|
5
|
+
before :all do
|
6
|
+
@server = connect_to_live_server
|
7
|
+
@server.reset_collections!
|
8
|
+
@collection = @server.collections!.first
|
9
|
+
@activity = @collection.build_activity(:type => 'optimize', :start_time => 0, :active => true)
|
10
|
+
end
|
11
|
+
|
12
|
+
context "after running" do
|
13
|
+
before :all do
|
14
|
+
@activity.save.should be_true
|
15
|
+
sleep 1
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have a history array with one element" do
|
19
|
+
histories = @activity.histories
|
20
|
+
histories.count.should == 1
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should subtract crawlStarted form crawlStopped and return the difference in seconds" do
|
24
|
+
include ActiveSupport::Duration
|
25
|
+
|
26
|
+
history = LucidWorks::Collection::Activity::History.new(:parent => @activity)
|
27
|
+
now = Time.now
|
28
|
+
history.stub(:activity_started) { now.advance(:seconds => -15) }
|
29
|
+
history.stub(:activity_finished) { now.advance(:seconds => -10) }
|
30
|
+
history.duration.should == 5.seconds
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LucidWorks::Collection::Activity::Status do
|
4
|
+
before :all do
|
5
|
+
@server = connect_to_live_server
|
6
|
+
@server.reset_collections!
|
7
|
+
@collection = @server.collections!.first
|
8
|
+
@activity = @collection.create_activity(:type => 'optimize', :start_time => 8600)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "find" do
|
12
|
+
it "should retrieve status" do
|
13
|
+
status = @activity.status
|
14
|
+
status.should be_a LucidWorks::Collection::Activity::Status
|
15
|
+
|
16
|
+
status.running.should be_false
|
17
|
+
status.type.should == 'optimize'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|