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.
Files changed (33) hide show
  1. data/README.rdoc +33 -3
  2. data/config/locales/en.yml +21 -12
  3. data/lib/lucid_works.rb +10 -0
  4. data/lib/lucid_works/associations.rb +14 -12
  5. data/lib/lucid_works/base.rb +13 -13
  6. data/lib/lucid_works/collection.rb +65 -5
  7. data/lib/lucid_works/collection/activity.rb +33 -0
  8. data/lib/lucid_works/collection/activity/history.rb +20 -0
  9. data/lib/lucid_works/collection/activity/status.rb +14 -0
  10. data/lib/lucid_works/collection/settings.rb +28 -8
  11. data/lib/lucid_works/crawler.rb +3 -3
  12. data/lib/lucid_works/datasource.rb +29 -3
  13. data/lib/lucid_works/datasource/job.rb +9 -0
  14. data/lib/lucid_works/datasource/status.rb +6 -11
  15. data/lib/lucid_works/patch_time.rb +13 -0
  16. data/lib/lucid_works/schema.rb +36 -8
  17. data/lib/lucid_works/utils.rb +22 -0
  18. data/lib/lucid_works/version.rb +1 -1
  19. data/lucid_works.gemspec +2 -0
  20. data/spec/lib/lucid_works/associations_spec.rb +12 -1
  21. data/spec/lib/lucid_works/base_spec.rb +26 -10
  22. data/spec/lib/lucid_works/collection/activity/history_spec.rb +33 -0
  23. data/spec/lib/lucid_works/collection/activity/status_spec.rb +20 -0
  24. data/spec/lib/lucid_works/collection/activity_spec.rb +88 -0
  25. data/spec/lib/lucid_works/collection/prime_activities_spec.rb +86 -0
  26. data/spec/lib/lucid_works/collection_spec.rb +140 -1
  27. data/spec/lib/lucid_works/datasource/history_spec.rb +11 -7
  28. data/spec/lib/lucid_works/datasource/status_spec.rb +64 -32
  29. data/spec/lib/lucid_works/datasource_spec.rb +48 -13
  30. data/spec/lib/lucid_works/schema_spec.rb +56 -4
  31. data/spec/lib/lucid_works/utils_spec.rb +62 -0
  32. data/spec/spec_helper.rb +17 -14
  33. 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 = %w{ off overwrite tag }
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
- attributes :unsupervised_feedback_emphasis, :unknown_type_handling,
12
- :click_boost_field, :click_boost_data, :query_parser, :default_sort,
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
- attributes :spellcheck, :display_facets, :ssl, :unsupervised_feedback, :query_time_stopwords,
16
- :auto_complete, :boost_recent, :click_enabled, :show_similar, :query_time_synonyms,
17
- :index_time_stopwords, :type => :boolean
18
- attributes :search_server_list, :update_server_list, :stopword_list, :boosts, :synonym_list # Arrays
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
@@ -1,8 +1,8 @@
1
1
  module LucidWorks
2
2
 
3
3
  class Crawler < Base
4
-
5
- self.primary_key = :name
6
-
4
+ schema do
5
+ primary_key :name
6
+ end
7
7
  end
8
8
  end
@@ -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
@@ -0,0 +1,9 @@
1
+ module LucidWorks
2
+ class Datasource
3
+
4
+ class Job < Base
5
+ self.singleton = true
6
+ belongs_to :datasource
7
+ end
8
+ end
9
+ 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
@@ -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
- # Specify an attribute for this model.
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
@@ -1,3 +1,3 @@
1
1
  module LucidWorks
2
- VERSION = "0.3.9"
2
+ VERSION = "0.4.9"
3
3
  end
data/lucid_works.gemspec CHANGED
@@ -23,4 +23,6 @@ Gem::Specification.new do |s|
23
23
  s.add_runtime_dependency 'activemodel', '>= 3'
24
24
  s.add_runtime_dependency 'rest-client', '>= 1.6.1'
25
25
  s.add_runtime_dependency 'json'
26
+ s.add_runtime_dependency 'rsolr'
27
+ s.add_runtime_dependency 'nokogiri'
26
28
  end
@@ -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
- launch_party = @blog.launch_party
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 without a schema" do
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
- [['off', 'Off'], ['overwrite', 'Overwrite'], ['tag', 'Tag']]
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
- self.primary_key = :name
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