lucid_works 0.7.18 → 0.9.4

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 (68) hide show
  1. data/.rvmrc +2 -3
  2. data/Gemfile +2 -8
  3. data/Gemfile.lock +45 -53
  4. data/README.rdoc +2 -6
  5. data/Rakefile +1 -1
  6. data/config/locales/en.yml +221 -239
  7. data/lib/lucid_works/activity.rb +8 -5
  8. data/lib/lucid_works/base.rb +27 -16
  9. data/lib/lucid_works/cache.rb +13 -0
  10. data/lib/lucid_works/cluster.rb +84 -0
  11. data/lib/lucid_works/collection/settings.rb +15 -6
  12. data/lib/lucid_works/collection.rb +62 -92
  13. data/lib/lucid_works/datasource/history.rb +2 -1
  14. data/lib/lucid_works/datasource/mapping.rb +12 -0
  15. data/lib/lucid_works/datasource/schedule.rb +5 -2
  16. data/lib/lucid_works/datasource/status.rb +3 -2
  17. data/lib/lucid_works/datasource.rb +31 -48
  18. data/lib/lucid_works/datasource_property.rb +2 -1
  19. data/lib/lucid_works/datasource_type.rb +14 -0
  20. data/lib/lucid_works/dynamicfield.rb +12 -0
  21. data/lib/lucid_works/elevation.rb +93 -0
  22. data/lib/lucid_works/exceptions.rb +0 -4
  23. data/lib/lucid_works/field.rb +31 -111
  24. data/lib/lucid_works/field_commons.rb +133 -0
  25. data/lib/lucid_works/gem_version.rb +1 -1
  26. data/lib/lucid_works/inflections.rb +3 -0
  27. data/lib/lucid_works/patch_time.rb +4 -0
  28. data/lib/lucid_works/request_handler.rb +16 -0
  29. data/lib/lucid_works/role.rb +23 -8
  30. data/lib/lucid_works/schema/attribute.rb +1 -1
  31. data/lib/lucid_works/schema/boolean_attribute.rb +1 -1
  32. data/lib/lucid_works/schema/integer_attribute.rb +3 -4
  33. data/lib/lucid_works/server/crawlers_status.rb +15 -0
  34. data/lib/lucid_works/server.rb +35 -14
  35. data/lib/lucid_works/simple_naming.rb +1 -7
  36. data/lib/lucid_works/synonym.rb +1 -1
  37. data/lib/lucid_works/version.rb +1 -0
  38. data/lib/lucid_works.rb +8 -1
  39. data/lucid_works.gemspec +8 -9
  40. data/spec/fixtures/zookeeper/clusterstate.json +30 -0
  41. data/spec/fixtures/zookeeper/clusterstate_broken_shard.json +29 -0
  42. data/spec/fixtures/zookeeper/live_nodes.json +28 -0
  43. data/spec/fixtures/zookeeper/live_nodes_no_children.json +26 -0
  44. data/spec/fixtures/zookeeper/live_nodes_one_child.json +36 -0
  45. data/spec/lib/lucid_works/base_spec.rb +33 -24
  46. data/spec/lib/lucid_works/cache_spec.rb +44 -0
  47. data/spec/lib/lucid_works/cluster_spec.rb +109 -0
  48. data/spec/lib/lucid_works/collection/activity_spec.rb +29 -0
  49. data/spec/lib/lucid_works/collection/prime_activities_spec.rb +1 -1
  50. data/spec/lib/lucid_works/collection/settings_spec.rb +31 -0
  51. data/spec/lib/lucid_works/collection_spec.rb +166 -107
  52. data/spec/lib/lucid_works/datasource/schedule_spec.rb +75 -46
  53. data/spec/lib/lucid_works/datasource/status_spec.rb +5 -5
  54. data/spec/lib/lucid_works/datasource_property_spec.rb +41 -0
  55. data/spec/lib/lucid_works/datasource_spec.rb +40 -12
  56. data/spec/lib/lucid_works/datasource_type_spec.rb +31 -0
  57. data/spec/lib/lucid_works/dynamicfield_spec.rb +214 -0
  58. data/spec/lib/lucid_works/elevation_spec.rb +175 -0
  59. data/spec/lib/lucid_works/field_spec.rb +52 -21
  60. data/spec/lib/lucid_works/fieldtype_spec.rb +0 -1
  61. data/spec/lib/lucid_works/request_handler_spec.rb +11 -0
  62. data/spec/lib/lucid_works/role_spec.rb +77 -0
  63. data/spec/lib/lucid_works/server/crawlers_status_spec.rb +21 -0
  64. data/spec/lib/lucid_works/server_spec.rb +123 -22
  65. data/spec/lib/lucid_works/{collection/synonym_spec.rb → synonym_spec.rb} +23 -22
  66. data/spec/lib/lucid_works/version_spec.rb +6 -0
  67. metadata +132 -64
  68. data/spec/lib/lucid_works/collection/acl_config_spec.rb +0 -212
@@ -0,0 +1,93 @@
1
+ class Array
2
+ def move(from, to)
3
+ insert(to, delete_at(from))
4
+ end
5
+ end
6
+
7
+ module LucidWorks
8
+ class Elevation
9
+ include ActiveModel::Conversion
10
+ extend ActiveModel::Naming
11
+ extend SimpleNaming
12
+
13
+ attr_reader :id, :collection, :query, :doc_id, :excluded
14
+ alias :excluded? :excluded
15
+
16
+ def initialize(attributes = {})
17
+ @doc_id = attributes[:doc_id]
18
+ @collection = attributes[:collection]
19
+ @query = attributes[:query]
20
+ @excluded = [true, "true"].include? attributes[:excluded]
21
+ @persisted = [true, "true"].include? attributes[:persisted]
22
+ @id = "#{CGI.escape query}~~#{doc_id}"
23
+ end
24
+
25
+ def save
26
+ settings.elevations[query] ||= []
27
+ settings.elevations[query] << {'doc' => doc_id, 'exclude' => excluded?}
28
+ settings.save
29
+ @persisted = true
30
+ rescue
31
+ false
32
+ end
33
+
34
+ def persisted?
35
+ @persisted
36
+ end
37
+
38
+ def destroy
39
+ return unless settings.elevations.include?(query)
40
+ settings.elevations[query].delete_if {|elevation| "#{CGI.escape query}~~#{elevation['doc']}" == id }
41
+ settings.elevations.delete(query) if settings.elevations[query].empty?
42
+ settings.save
43
+ end
44
+
45
+ def first?
46
+ index_of_in_elevations(doc_id) == 0
47
+ end
48
+
49
+ def last?
50
+ index_of_in_elevations(doc_id) == elevations.size - 1
51
+ end
52
+
53
+ def move_up
54
+ return if first?
55
+ current_index = index_of_in_settings(doc_id)
56
+ new_index = elevation_indexes[elevation_indexes.index(current_index) - 1]
57
+ settings.elevations[query].move(current_index, new_index)
58
+ settings.save
59
+ end
60
+
61
+ def move_down
62
+ return if last?
63
+ current_index = index_of_in_settings(doc_id)
64
+ new_index = elevation_indexes[elevation_indexes.index(current_index) + 1]
65
+ settings.elevations[query].move(current_index, new_index)
66
+ settings.save
67
+ end
68
+
69
+ private
70
+
71
+ def settings
72
+ collection.build_settings(:elevations => collection.settings.elevations)
73
+ end
74
+
75
+ def index_of_in_settings(doc_id)
76
+ settings.elevations[query].index {|elevation| elevation['doc'] == doc_id }
77
+ end
78
+
79
+ def elevations
80
+ settings.elevations[query].index.select {|elevation| !elevation['exclude'] }
81
+ end
82
+
83
+ def index_of_in_elevations(doc_id)
84
+ elevations.index {|elevation| elevation['doc'] == doc_id }
85
+ end
86
+
87
+ def elevation_indexes
88
+ indexes = []
89
+ settings.elevations[query].each_with_index {|elevation, index| indexes << index if !elevation['exclude'] }
90
+ indexes
91
+ end
92
+ end
93
+ end
@@ -4,8 +4,4 @@ module LucidWorks
4
4
 
5
5
  class ResourceNotFound < Exception ; end
6
6
  class RecordInvalid < Exception ; end
7
- class AclConfigInvalid < Exception
8
- attr_accessor :messages;
9
- def initialize(messages); @messages = messages; end
10
- end
11
7
  end
@@ -1,39 +1,38 @@
1
+ require 'lucid_works/field_commons'
2
+
1
3
  module LucidWorks
2
4
 
3
5
  class Field < Base
4
- belongs_to :collection
5
-
6
- INDEXING_OPTIONS = [
7
- 'document_only',
8
- 'document_termfreq',
9
- 'document_termfreq_termpos'
10
- ]
6
+ include LucidWorks::FieldCommons
7
+
8
+ class << self
9
+ attr_accessor :include_dynamic
10
+
11
+ def collection_url(parent)
12
+ include_dynamic ? "#{super}?include_dynamic=true" : super
13
+ end
11
14
 
12
- BOOST_VALUES = [
13
- 'none',
14
- 'moderate',
15
- 'high'
16
- ]
15
+ def member_url(parent, id = nil)
16
+ include_dynamic ? "#{super}?include_dynamic=true" : super
17
+ end
18
+ end
17
19
 
18
20
  schema do
19
- attribute :name, :string, :primary_key => true, :omit_during_update => true
20
- attribute :editable, :boolean, :omit_during_update => true
21
- attribute :dynamic_base, :string, :omit_during_update => true
22
- attributes :indexed, :stored, :facet, :include_in_results,
23
- :search_by_default, :highlight, :multi_valued,
24
- :term_vectors, :omit_tf, :omit_positions,
25
- :use_for_deduplication, :synonym_expansion,
26
- :index_for_spellcheck, :index_for_autocomplete,
27
- :query_time_stopword_handling,
28
- :use_in_find_similar,
29
- :type => :boolean
30
21
  attribute :default_boost, :integer
31
- attributes :field_type, :short_field_boost, :term_vectors, :default_value, :type => :string
32
- attribute :copy_fields, :list
33
- attribute :indexing_options, :custom, :values => INDEXING_OPTIONS
34
- end
35
-
36
- validates_presence_of :name
22
+ attribute :default_value, :string
23
+ attribute :dynamic_base, :string, :omit_during_update => true
24
+ attribute :facet, :boolean
25
+ attribute :highlight, :boolean
26
+ attribute :include_in_results, :boolean
27
+ attribute :num_facets, :integer
28
+ attribute :query_time_stopword_handling, :boolean
29
+ attribute :search_by_default, :boolean
30
+ attribute :short_field_boost, :string
31
+ attribute :synonym_expansion, :boolean
32
+ attribute :use_for_deduplication, :boolean
33
+ attribute :use_in_find_similar, :boolean
34
+ end
35
+
37
36
  validates_each :name, :unless => :persisted?, :allow_blank => true do |model, attr, value|
38
37
  model.errors.add(attr, 'must be unique') if model.collection.fields.any? {|f| f.name == value }
39
38
  end
@@ -52,92 +51,15 @@ module LucidWorks
52
51
  validates_each :use_in_find_similar do |model, attr, value|
53
52
  model.errors.add(attr, 'a field must be indexed for it to be used for find-similar') if value == true && !model.indexed?
54
53
  end
55
- validates_each :indexing_options do |model, attr, value|
56
- if model.persisted?
57
- if value
58
- if model.indexed?
59
- model.errors.add(attr, "must be one of #{INDEXING_OPTIONS.join(', ')}") unless INDEXING_OPTIONS.include?(value.to_s)
60
- end
61
- else
62
- # If the model is already persisted, but the user has not manually set indexing_options,
63
- # just go with the omit_tf/omit_positions we have now.
64
- end
65
- else # new model
66
- if model.indexed?
67
- # If this is a new model, and the user set 'indexed' they MUST also set indexing_options
68
- model.errors.add(attr, "must be one of #{INDEXING_OPTIONS.join(', ')}") unless INDEXING_OPTIONS.include?(value.to_s)
69
- end
70
- end
71
- end
72
-
73
- before_save :convert_indexing_options_to_term_freq_and_pos
74
54
 
75
55
  def initialize(options)
76
- super(options.reverse_merge(:omit_tf => false, :short_field_boost => 'high'))
56
+ super(options.reverse_merge(:short_field_boost => 'high'))
77
57
  end
78
58
 
79
- def dynamically_generated?
59
+ def dynamically_generated
80
60
  !dynamic_base.blank?
81
61
  end
82
-
83
- def t_field_type
84
- self.class.t_field_type(self.field_type)
85
- end
86
-
87
- def self.t_field_type(type)
88
- I18n.translate(type, :scope => 'activemodel.models.lucid_works.collection.field.field_type')
89
- end
90
-
91
- # Indexing_options is a fake attribute that wraps the omit_tf and omit_positions combinations.
92
- # Translations are:
93
- #
94
- # INDEXED? indexing_options omit_tf omit_positions
95
- #
96
- # false - true true
97
- # true :document_only true true
98
- # true :document_termfreq false true
99
- # true :documents_termfreq_termpos false false
100
- #
101
- def indexing_options
102
- @attributes[:indexing_options] ||=
103
- if !indexed? || omit_tf?
104
- :document_only
105
- else
106
- omit_positions? ? :document_termfreq : :document_termfreq_termpos
107
- end
108
- end
109
-
110
- # We have to remember the caller's choice of indexing_options, as we can't determine the correct
111
- # omit_tf/omit_positions settins until we also know whether 'indexed' is set or not.
112
- # So we keep it as an attribute for now, and remove it in a before_save.
113
- def indexing_options=(new_value)
114
- @attributes[:indexing_options] = new_value
115
- end
116
-
117
- # Convert indexing_options to the correct values for omit_tf and omit_positions.
118
- # Delete indexing_options from attributes so it is not saved.
119
- def convert_indexing_options_to_term_freq_and_pos
120
- i_opt = @attributes.delete(:indexing_options)
121
- if indexed? && i_opt
122
- case i_opt.to_sym
123
- when :document_only
124
- self.omit_tf = true
125
- self.omit_positions = true
126
- when :document_termfreq
127
- self.omit_tf = false
128
- self.omit_positions = true
129
- when :document_termfreq_termpos
130
- self.omit_tf = false
131
- self.omit_positions = false
132
- else
133
- raise "Unknown indexing option: '#{i_opt}'. Allowed values are: #{INDEXING_OPTIONS.join(', ')}"
134
- end
135
- else # !indexed?
136
- self.omit_tf = true
137
- self.omit_positions = true
138
- end
139
- true
140
- end
62
+ alias :dynamically_generated? :dynamically_generated
141
63
 
142
64
  def update_attributes(attrs)
143
65
  attrs = attrs.with_indifferent_access
@@ -148,8 +70,6 @@ module LucidWorks
148
70
  if [false, '0'].include?(attrs[:indexed])
149
71
  attrs[:facet] ||= false
150
72
  attrs[:synonym_expansion] ||= false
151
- attrs[:omit_tf] ||= false
152
- attrs[:omit_positions] ||= false
153
73
  attrs[:short_field_boost] ||= 'high'
154
74
  attrs[:search_by_default] ||= false
155
75
  attrs[:use_in_find_similar] ||= false
@@ -0,0 +1,133 @@
1
+ module LucidWorks
2
+
3
+ module FieldCommons
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ belongs_to :collection
8
+
9
+ INDEXING_OPTIONS = [
10
+ 'document_only',
11
+ 'document_termfreq',
12
+ 'document_termfreq_termpos'
13
+ ] unless defined?(INDEXING_OPTIONS)
14
+
15
+ BOOST_VALUES = [
16
+ 'none',
17
+ 'moderate',
18
+ 'high'
19
+ ] unless defined?(BOOST_VALUES)
20
+
21
+ schema do
22
+ attribute :name, :string, :primary_key => true, :omit_during_update => true
23
+ attribute :copy_fields, :list
24
+ attribute :editable, :boolean, :omit_during_update => true
25
+ attribute :field_type, :string
26
+ attribute :index_for_autocomplete, :boolean
27
+ attribute :index_for_spellcheck, :boolean
28
+ attribute :indexed, :boolean
29
+ attribute :indexing_options, :custom, :values => INDEXING_OPTIONS
30
+ attribute :multi_valued, :boolean
31
+ attribute :omit_positions, :boolean
32
+ attribute :omit_tf, :boolean
33
+ attribute :stored, :boolean
34
+ attribute :term_vectors, :boolean
35
+ end
36
+
37
+ validates_presence_of :name, :field_type
38
+ validates_each :indexing_options do |model, attr, value|
39
+ if model.persisted?
40
+ if value
41
+ if model.indexed?
42
+ model.errors.add(attr, "must be one of #{INDEXING_OPTIONS.join(', ')}") unless INDEXING_OPTIONS.include?(value.to_s)
43
+ end
44
+ else
45
+ # If the model is already persisted, but the user has not manually set indexing_options,
46
+ # just go with the omit_tf/omit_positions we have now.
47
+ end
48
+ else # new model
49
+ if model.indexed?
50
+ # If this is a new model, and the user set 'indexed' they MUST also set indexing_options
51
+ model.errors.add(attr, "must be one of #{INDEXING_OPTIONS.join(', ')}") unless INDEXING_OPTIONS.include?(value.to_s)
52
+ end
53
+ end
54
+ end
55
+
56
+ before_save :convert_indexing_options_to_term_freq_and_pos
57
+ end
58
+
59
+ module ClassMethods
60
+ def t_field_type(type)
61
+ I18n.translate(type, :scope => 'activemodel.models.collection.field.field_type')
62
+ end
63
+ end
64
+
65
+ def initialize(options)
66
+ super(options.reverse_merge(:omit_tf => false))
67
+ end
68
+
69
+ def t_field_type
70
+ self.class.t_field_type(self.field_type)
71
+ end
72
+
73
+ # Indexing_options is a fake attribute that wraps the omit_tf and omit_positions combinations.
74
+ # Translations are:
75
+ #
76
+ # INDEXED? indexing_options omit_tf omit_positions
77
+ #
78
+ # false - true true
79
+ # true :document_only true true
80
+ # true :document_termfreq false true
81
+ # true :documents_termfreq_termpos false false
82
+ #
83
+ def indexing_options
84
+ @attributes[:indexing_options] ||=
85
+ if !indexed? || omit_tf?
86
+ :document_only
87
+ else
88
+ omit_positions? ? :document_termfreq : :document_termfreq_termpos
89
+ end
90
+ end
91
+
92
+ # We have to remember the caller's choice of indexing_options, as we can't determine the correct
93
+ # omit_tf/omit_positions settins until we also know whether 'indexed' is set or not.
94
+ # So we keep it as an attribute for now, and remove it in a before_save.
95
+ def indexing_options=(new_value)
96
+ @attributes[:indexing_options] = new_value
97
+ end
98
+
99
+ # Convert indexing_options to the correct values for omit_tf and omit_positions.
100
+ # Delete indexing_options from attributes so it is not saved.
101
+ def convert_indexing_options_to_term_freq_and_pos
102
+ i_opt = @attributes.delete(:indexing_options)
103
+ if indexed? && i_opt
104
+ case i_opt.to_sym
105
+ when :document_only
106
+ self.omit_tf = true
107
+ self.omit_positions = true
108
+ when :document_termfreq
109
+ self.omit_tf = false
110
+ self.omit_positions = true
111
+ when :document_termfreq_termpos
112
+ self.omit_tf = false
113
+ self.omit_positions = false
114
+ else
115
+ raise "Unknown indexing option: '#{i_opt}'. Allowed values are: #{INDEXING_OPTIONS.join(', ')}"
116
+ end
117
+ else # !indexed?
118
+ self.omit_tf = true
119
+ self.omit_positions = true
120
+ end
121
+ true
122
+ end
123
+
124
+ def update_attributes(attrs)
125
+ attrs = attrs.with_indifferent_access
126
+ if [false, '0'].include?(attrs[:indexed])
127
+ attrs[:omit_positions] ||= false
128
+ attrs[:omit_tf] ||= false
129
+ end
130
+ super(attrs)
131
+ end
132
+ end
133
+ end
@@ -1,3 +1,3 @@
1
1
  module LucidWorks
2
- VERSION = "0.7.18"
2
+ VERSION = "0.9.4"
3
3
  end
@@ -0,0 +1,3 @@
1
+ ActiveSupport::Inflector.inflections do |inflect|
2
+ inflect.singular /^(cache)s/i, '\1'
3
+ end
@@ -21,6 +21,10 @@ class Time #:nodoc:
21
21
  end
22
22
 
23
23
  alias_method_chain :iso8601, :support_for_timezones_without_colons
24
+
25
+ def ceil(seconds = 60)
26
+ Time.at((self.to_f / seconds).ceil * seconds)
27
+ end
24
28
  end
25
29
 
26
30
  class Fixnum
@@ -0,0 +1,16 @@
1
+ module LucidWorks
2
+
3
+ class RequestHandler
4
+
5
+ attr_reader :name
6
+
7
+ def initialize(collection, name)
8
+ @collection = collection
9
+ @name = name
10
+ end
11
+
12
+ def default_params
13
+ @collection.search(:qt => '/lucid', :echoParams => 'all')['responseHeader']['params'].symbolize_keys
14
+ end
15
+ end
16
+ end
@@ -18,10 +18,17 @@ module LucidWorks
18
18
  end
19
19
 
20
20
  def filters
21
- # require 'ruby-debug'; debugger
22
21
  @attributes[:filters]
23
22
  end
24
23
 
24
+ def users_for_text_field
25
+ @attributes[:users].to_a.join(", ")
26
+ end
27
+
28
+ def groups_for_text_field
29
+ @attributes[:groups].to_a.join(", ")
30
+ end
31
+
25
32
  def filters_for_text_area
26
33
  @attributes[:filters].to_a.join("\n")
27
34
  end
@@ -30,24 +37,27 @@ module LucidWorks
30
37
  # Accept a comma or space separated list of users, convert to array
31
38
  #
32
39
  def users=(val)
33
- @attributes[:users] = string_or_array(val, /[\s,]+/) rescue "Can't interpret #{val.class} as list of users"
40
+ @attributes[:users] = string_or_array(val, :space_or_comma) rescue "Can't interpret #{val.class} as list of users"
34
41
  end
35
42
 
36
43
  def groups=(val)
37
- @attributes[:groups] = string_or_array(val, /[\s,]+/) rescue "Can't interpret #{val.class} as list of groups"
44
+ @attributes[:groups] = string_or_array(val, :space_or_comma) rescue "Can't interpret #{val.class} as list of groups"
38
45
  end
39
46
 
40
47
  def filters=(val)
41
- @attributes[:filters] = string_or_array(val, /\r+/) rescue "Can't interpret #{val.class} as list of groups"
42
- # require 'ruby-debug'; debugger
43
- # @attributes[:filters]
48
+ @attributes[:filters] = string_or_array(val, :newline) rescue "Can't interpret #{val.class} as list of groups"
44
49
  end
45
50
 
46
51
  private
47
52
 
48
- def string_or_array(val, pattern)
53
+ def string_or_array(val, method)
49
54
  if val.is_a?(String)
50
- val.split(pattern)
55
+ pattern = case method
56
+ when :space_or_comma ; then (val =~ /,/) ? /[,]+/ : /\s+/
57
+ when :newline ; then /\r+/
58
+ else raise "Unrecognized split method #{method}, can't convert #{val} to an Array"
59
+ end
60
+ val.split(pattern).map{|item| remove_leading_and_trailing_spaces(item)}
51
61
  elsif val.is_a?(Array)
52
62
  val
53
63
  else
@@ -55,5 +65,10 @@ module LucidWorks
55
65
  end
56
66
 
57
67
  end
68
+
69
+ def remove_leading_and_trailing_spaces(string)
70
+ string.sub(/^\s*(.*?)\s*$/,'\1') # remember everything in the middle, but non-greedy
71
+ end
72
+
58
73
  end
59
74
  end
@@ -60,7 +60,7 @@ module LucidWorks
60
60
  if values
61
61
  # If this attribute was defined with a set of :values,
62
62
  # assume we can get translations for those valuesfrom the L10n database.
63
- l10n_scope = %w{activemodel models} + schema.model.name.underscore.split('/') + [name]
63
+ l10n_scope = %w{activemodel models} + schema.model.name.underscore.split('/').drop(1) + [name]
64
64
  return I18n.t(value, :scope => l10n_scope, :default => value)
65
65
  end
66
66
  value.to_s
@@ -27,7 +27,7 @@ module LucidWorks
27
27
  end
28
28
 
29
29
  def human_value(value)
30
- value.to_yesno
30
+ (!!value).to_yesno
31
31
  end
32
32
  end
33
33
  end
@@ -13,11 +13,10 @@ module LucidWorks
13
13
 
14
14
  klass.class_eval <<-EOF, __FILE__, __LINE__+1
15
15
  def #{name}=(new_value)
16
- if new_value.blank? && (self.class.schema[:#{name}].nil_when_blank? ||
17
- self.class.schema[:#{name}].omit_when_blank?)
18
- new_value = nil
16
+ new_value = if new_value.blank? && (self.class.schema[:#{name}].nil_when_blank? || self.class.schema[:#{name}].omit_when_blank?)
17
+ nil
19
18
  else
20
- new_value = new_value.to_i
19
+ Integer(new_value.to_s) rescue new_value
21
20
  end
22
21
  @attributes[:#{name}] = new_value
23
22
  end
@@ -0,0 +1,15 @@
1
+ module LucidWorks
2
+ class Server
3
+ class CrawlersStatus < Base
4
+ self.singleton = true
5
+
6
+ def self.member_url(parent, id = nil) # :nodoc:
7
+ "#{parent.uri}/crawlers/status"
8
+ end
9
+
10
+ def ok?
11
+ self.status == 'OK' rescue false
12
+ end
13
+ end
14
+ end
15
+ end
@@ -8,32 +8,53 @@ module LucidWorks
8
8
  has_many :collections, :crawlers
9
9
  has_one :logs, :has_content => false
10
10
  has_one :version
11
+ has_one :crawlers_status
11
12
 
12
- DEFAULT_REST_API_PATH = "/api"
13
+ attr_accessor :server_uri, :path, :protocol, :host, :port, :user, :password
13
14
 
14
- attr_accessor :host, :path
15
+ def initialize(server_uri)
16
+ @server_uri = server_uri
17
+ uri = URI.parse(@server_uri)
18
+ @protocol, @host, @port, @user, @password = "#{uri.scheme}://", uri.host, uri.port, uri.user, uri.password
19
+ end
15
20
 
16
- def initialize(server_uri, options = {})
17
- @host = server_uri
18
- @path = options.delete(:path) || DEFAULT_REST_API_PATH
21
+ def api_uri
22
+ "#{server_uri}/api"
19
23
  end
24
+ alias :uri :api_uri # used by associations
20
25
 
21
- def uri
22
- @host + @path
26
+ def solr_uri
27
+ "#{server_uri}/solr"
28
+ end
29
+
30
+ # Location where raw log files may be retrieved
31
+ def logs_uri
32
+ "#{server_uri}/logs"
23
33
  end
24
- alias :rest_api_uri :uri
25
34
 
26
35
  def crawler(name)
27
- crawlers.detect { |c| c.name == name }
36
+ crawlers.detect {|c| c.name == name }
28
37
  end
29
38
 
30
- def solr_uri
31
- "#{host}/solr/"
39
+ def datasource_type(type_name, crawler_name)
40
+ crawler(crawler_name).datasource_type(type_name)
32
41
  end
33
42
 
34
- # Location where raw log files may be retrieved
35
- def logs_url
36
- "#{host}/logs/"
43
+ def clustered?
44
+ begin
45
+ RestClient.get(solr_uri + "/zookeeper")
46
+ true
47
+ rescue RestClient::ResourceNotFound
48
+ false
49
+ end
50
+ end
51
+
52
+ def cluster
53
+ Cluster.new(solr_uri)
54
+ end
55
+
56
+ def datasource_types
57
+ crawlers.collect(&:datasource_types).flatten
37
58
  end
38
59
  end
39
60
  end
@@ -1,13 +1,7 @@
1
1
  module LucidWorks
2
2
  module SimpleNaming
3
3
  def model_name
4
- super.tap do |name|
5
- name.instance_eval do
6
- def singular; ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).tr('/', '_').freeze; end
7
- def plural; singular.pluralize.freeze; end
8
- def collection; ActiveSupport::Inflector.tableize(ActiveSupport::Inflector.demodulize(self)).freeze; end
9
- end
10
- end
4
+ ActiveModel::Name.new(self, nil, self.name.sub(/^LucidWorks::/, ''))
11
5
  end
12
6
  end
13
7
  end
@@ -6,7 +6,7 @@ module LucidWorks
6
6
  extend ActiveModel::Naming
7
7
  extend SimpleNaming
8
8
 
9
- attr_reader :id, :collection, :persisted
9
+ attr_reader :id, :collection
10
10
  attr_accessor :mapping
11
11
 
12
12
  validates_presence_of :mapping
@@ -1,4 +1,5 @@
1
1
  module LucidWorks
2
2
  class Version < Base
3
+ self.singleton = true
3
4
  end
4
5
  end