lucid_works 0.3.9 → 0.4.9

Sign up to get free protection for your applications and to get access to all the features.
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