lucid_works 0.6.18 → 0.6.19
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/lib/lucid_works/{collection/activity → activity}/history.rb +1 -1
- data/lib/lucid_works/{collection/activity → activity}/status.rb +2 -2
- data/lib/lucid_works/activity.rb +190 -0
- data/lib/lucid_works/gem_version.rb +1 -1
- data/lib/lucid_works/synonym.rb +60 -0
- data/lib/lucid_works.rb +4 -5
- data/spec/lib/lucid_works/collection/activity/history_spec.rb +2 -2
- data/spec/lib/lucid_works/collection/activity/status_spec.rb +2 -2
- data/spec/lib/lucid_works/collection/activity_spec.rb +1 -1
- data/spec/lib/lucid_works/collection/prime_activities_spec.rb +9 -9
- data/spec/lib/lucid_works/collection/synonym_spec.rb +11 -12
- data/spec/lib/lucid_works/collection_spec.rb +5 -5
- metadata +5 -5
- data/lib/lucid_works/collection/activity.rb +0 -191
- data/lib/lucid_works/collection/synonym.rb +0 -63
@@ -1,12 +1,12 @@
|
|
1
1
|
module LucidWorks
|
2
|
-
class
|
2
|
+
class Activity
|
3
3
|
class Status < Base
|
4
4
|
self.singleton = true
|
5
5
|
belongs_to :activity
|
6
6
|
|
7
7
|
schema do
|
8
8
|
attribute :running, :boolean
|
9
|
-
attribute :type, :string, :values => LucidWorks::
|
9
|
+
attribute :type, :string, :values => LucidWorks::Activity::TYPES
|
10
10
|
end
|
11
11
|
|
12
12
|
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'active_support/core_ext/numeric/time.rb'
|
2
|
+
module LucidWorks
|
3
|
+
|
4
|
+
class Activity < Base
|
5
|
+
TYPES = %w{ optimize spelling click autocomplete}
|
6
|
+
|
7
|
+
# Unlike Datasource schedules, the API allows multiples of these
|
8
|
+
# So, to be honest with gem users, we can't really say this:
|
9
|
+
# self.singleton = true
|
10
|
+
belongs_to :collection
|
11
|
+
has_many :histories, :class_name => :history
|
12
|
+
has_one :status
|
13
|
+
|
14
|
+
schema do
|
15
|
+
attribute :period
|
16
|
+
attribute :start_time, :iso8601
|
17
|
+
attribute :active, :boolean
|
18
|
+
attribute :type, :string, :values => TYPES, :omit_during_update => true
|
19
|
+
end
|
20
|
+
|
21
|
+
validates_presence_of :type, :start_time
|
22
|
+
validates_numericality_of :period, :allow_blank => true
|
23
|
+
|
24
|
+
def frequency
|
25
|
+
case period
|
26
|
+
when 1.weeks.seconds then 'weekly'
|
27
|
+
when 1.days.seconds then 'daily'
|
28
|
+
when 1.hours.seconds then 'hourly'
|
29
|
+
when 0 then nil
|
30
|
+
else 'custom'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# accepts hourly, daily, weekly, and sets period to respective # of seconds
|
36
|
+
#
|
37
|
+
def frequency=(frequency)
|
38
|
+
self.period = case frequency
|
39
|
+
when 'hourly' then 1.hours.seconds
|
40
|
+
when 'daily' then 1.days.seconds
|
41
|
+
when 'weekly' then 1.weeks.seconds
|
42
|
+
else raise "unknown frequency"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# predict when action will occur next if active at that time
|
48
|
+
#
|
49
|
+
def next_start
|
50
|
+
return start_time if (now = Time.now) <= start_time
|
51
|
+
# require 'ruby-debug'; debugger
|
52
|
+
time_since_start = now - start_time
|
53
|
+
last_interval_num = (time_since_start / period).to_i
|
54
|
+
next_interval_num = if (time_since_start % period) == 0
|
55
|
+
# this is sort of a stupid condition b/c time precision is millisecond or less
|
56
|
+
# for human purposes we wouldn't care if the result were as though the call
|
57
|
+
# happened a millisecond later. But whatever.
|
58
|
+
last_interval_num # the next interval *is* the last interval if it is exactly now
|
59
|
+
else
|
60
|
+
last_interval_num + 1
|
61
|
+
end
|
62
|
+
start_time + period * next_interval_num
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# phantom attribute for compiling real start time and frequency from appropriate form data
|
67
|
+
# Allows the user to specify a schedule simply with repeat interval (hourly, daily, weekly)
|
68
|
+
# and time within the interval to run (e.g 5 past, or Tuesdays at 2:15)
|
69
|
+
#
|
70
|
+
# We have to figure out when the actual start time will be because we are not asking
|
71
|
+
# the user for this. It needs to be:
|
72
|
+
# * at the requested time relative to the interval chosen
|
73
|
+
# * as soon as possible
|
74
|
+
# * in the future
|
75
|
+
#
|
76
|
+
# This is obvious to humans, but when computing it, you run into the following problem. It
|
77
|
+
# happens with all interval sizes, but for the sake of example, we'll use weekly:
|
78
|
+
#
|
79
|
+
# Problem 1) If today is Thursday, and the user asks for "weekly on Wednesdays," Wednesday has
|
80
|
+
# already happened this week. We have to make sure we pick the nearest wednesday that is in
|
81
|
+
# the future
|
82
|
+
#
|
83
|
+
# Problem 2) If today is Tuesday, and the user asks for "weekly on Wednesdays,", the simple solution
|
84
|
+
# to problem 1 (start next week instead of this week) causes you to skip this Tuesday, even though
|
85
|
+
# it is valid and is what the user would expect
|
86
|
+
#
|
87
|
+
# The algorith to solve both problems at once is this:
|
88
|
+
# * From Time.now, back up to the beginning of the interval even if it is in the past.
|
89
|
+
# * Fast forward to the requested siimple start time
|
90
|
+
# * If you are still in the past, advance by one interval
|
91
|
+
#
|
92
|
+
def schedule=(all_attributes)
|
93
|
+
# convert to format accepted by Time.advance
|
94
|
+
if all_attributes['start']
|
95
|
+
all_attributes['start'].to_options!
|
96
|
+
all_attributes['start'].each{|k,v| all_attributes['start'][k]=v.to_i}
|
97
|
+
end
|
98
|
+
|
99
|
+
self.active = all_attributes['active'] if all_attributes.keys.include?('active')
|
100
|
+
|
101
|
+
now = Time.now
|
102
|
+
self.frequency = all_attributes['frequency']
|
103
|
+
self.start_time =
|
104
|
+
case all_attributes['frequency']
|
105
|
+
when 'weekly'
|
106
|
+
# require 'ruby-debug'; debugger
|
107
|
+
start = now.beginning_of_week.advance(all_attributes['start'])
|
108
|
+
start < now ? start.advance(:weeks => 1) : start
|
109
|
+
when 'daily'
|
110
|
+
start = now.beginning_of_day.advance(all_attributes['start'])
|
111
|
+
start < now ? start.advance(:days => 1) : start
|
112
|
+
when 'hourly'
|
113
|
+
start = now.change(:min => 0).advance(all_attributes['start'])
|
114
|
+
start < now ? start.advance(:hours => 1) : start
|
115
|
+
else
|
116
|
+
puts "*** frequency: <#{all_attributes[:frequency]}>"
|
117
|
+
raise "unexpected frequency encountered"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# convenince method for setting the current value in a view selector
|
123
|
+
#
|
124
|
+
def hour
|
125
|
+
self.start_time.localtime.hour rescue nil
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# convenince method for setting the current value in a view selector
|
130
|
+
#
|
131
|
+
def min
|
132
|
+
self.start_time.localtime.min rescue nil
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# convenince method for setting the current value in a view selector
|
137
|
+
#
|
138
|
+
def day_of_week
|
139
|
+
# subract 1 because '%u' returns 1-7, and this is working with zero-indexed ruby arrays
|
140
|
+
self.start_time.localtime.strftime('%u').to_i - 1 rescue nil
|
141
|
+
end
|
142
|
+
|
143
|
+
def start_is_more_than_one_period_in_the_future?
|
144
|
+
# This works fine with start times that are multiple periods in the past
|
145
|
+
# because that gives a negative difference, and period is always >= 0
|
146
|
+
(self.start_time - Time.now) > self.period
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# convenience method for detecting whether schedule can be represented in simple format
|
151
|
+
# or should be described as custom (meaning it is beyond the capabilities of the convenience methods)
|
152
|
+
#
|
153
|
+
def custom?
|
154
|
+
return false unless self.start_time
|
155
|
+
return true if self.frequency == 'custom'
|
156
|
+
return true if self.start_is_more_than_one_period_in_the_future?
|
157
|
+
return false
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
# convenience method for setting defaults in a UI that make sense for a user
|
162
|
+
#
|
163
|
+
def ui_appropriate_defaults!
|
164
|
+
if self.start_time.blank? || self.period == 0
|
165
|
+
self.active = true
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def t_type
|
170
|
+
I18n.t(type, :scope => 'activemodel.models.lucid_works.collection.activity.type')
|
171
|
+
end
|
172
|
+
|
173
|
+
def start
|
174
|
+
self.start_time = 0
|
175
|
+
self.active = true
|
176
|
+
save
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def exact(interval, period)
|
182
|
+
if period % (result = 1.send(interval)) == 0
|
183
|
+
period / result
|
184
|
+
else
|
185
|
+
false
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module LucidWorks
|
2
|
+
|
3
|
+
class Synonym
|
4
|
+
include ActiveModel::Validations
|
5
|
+
include ActiveModel::Conversion
|
6
|
+
extend ActiveModel::Naming
|
7
|
+
extend SimpleNaming
|
8
|
+
|
9
|
+
attr_reader :id, :collection, :persisted
|
10
|
+
attr_accessor :mapping
|
11
|
+
|
12
|
+
validates_presence_of :mapping
|
13
|
+
validates_each :mapping, :allow_blank => true do |model, attr, value|
|
14
|
+
model.errors.add(attr, 'must be a comma-separated list') if value =~ /=>/ || value.split(',').size < 2
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(attributes = {})
|
18
|
+
@id = attributes[:id]
|
19
|
+
@collection = attributes[:collection]
|
20
|
+
@mapping = attributes[:mapping]
|
21
|
+
@persisted = attributes[:persisted] || false
|
22
|
+
end
|
23
|
+
|
24
|
+
def persisted?
|
25
|
+
@persisted
|
26
|
+
end
|
27
|
+
|
28
|
+
def save
|
29
|
+
return false unless valid?
|
30
|
+
if persisted?
|
31
|
+
settings.synonym_list[id] = mapping
|
32
|
+
else
|
33
|
+
settings.synonym_list << mapping
|
34
|
+
@id = settings.synonym_list.length - 1
|
35
|
+
end
|
36
|
+
settings.save
|
37
|
+
@persisted = true
|
38
|
+
rescue
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def update_attributes(attrs_and_values)
|
43
|
+
attrs_and_values.each do |attr,value|
|
44
|
+
self.send("#{attr}=", value)
|
45
|
+
end
|
46
|
+
save
|
47
|
+
end
|
48
|
+
|
49
|
+
def destroy
|
50
|
+
settings.synonym_list.delete_at(id)
|
51
|
+
settings.save
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def settings
|
57
|
+
collection.settings
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/lucid_works.rb
CHANGED
@@ -27,14 +27,12 @@ require 'lucid_works/base'
|
|
27
27
|
require 'lucid_works/schema'
|
28
28
|
|
29
29
|
require 'lucid_works/collection'
|
30
|
-
require 'lucid_works/collection/activity'
|
31
|
-
require 'lucid_works/collection/activity/status'
|
32
|
-
require 'lucid_works/collection/activity/history'
|
33
|
-
#require 'lucid_works/collection/activity/schedule'
|
34
30
|
require 'lucid_works/collection/info'
|
35
31
|
require 'lucid_works/collection/index'
|
36
32
|
require 'lucid_works/collection/settings'
|
37
|
-
require 'lucid_works/
|
33
|
+
require 'lucid_works/activity'
|
34
|
+
require 'lucid_works/activity/status'
|
35
|
+
require 'lucid_works/activity/history'
|
38
36
|
require 'lucid_works/crawler'
|
39
37
|
require 'lucid_works/datasource'
|
40
38
|
require 'lucid_works/datasource/index'
|
@@ -44,6 +42,7 @@ require 'lucid_works/datasource/schedule'
|
|
44
42
|
require 'lucid_works/datasource/crawldata'
|
45
43
|
require 'lucid_works/datasource/job'
|
46
44
|
require 'lucid_works/field'
|
45
|
+
require 'lucid_works/synonym'
|
47
46
|
require 'lucid_works/logs'
|
48
47
|
require 'lucid_works/logs/query'
|
49
48
|
require 'lucid_works/logs/query/summary'
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'active_support/core_ext'
|
3
3
|
|
4
|
-
describe LucidWorks::
|
4
|
+
describe LucidWorks::Activity::History do
|
5
5
|
before :all do
|
6
6
|
@server = connect_to_live_server
|
7
7
|
@server.reset_collections!
|
@@ -23,7 +23,7 @@ describe LucidWorks::Collection::Activity::History do
|
|
23
23
|
it "should subtract crawlStarted form crawlStopped and return the difference in seconds" do
|
24
24
|
include ActiveSupport::Duration
|
25
25
|
|
26
|
-
history = LucidWorks::
|
26
|
+
history = LucidWorks::Activity::History.new(:parent => @activity)
|
27
27
|
now = Time.now
|
28
28
|
history.stub(:activity_started) { now.advance(:seconds => -15) }
|
29
29
|
history.stub(:activity_finished) { now.advance(:seconds => -10) }
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe LucidWorks::
|
3
|
+
describe LucidWorks::Activity::Status do
|
4
4
|
before :all do
|
5
5
|
@server = connect_to_live_server
|
6
6
|
@server.reset_collections!
|
@@ -11,7 +11,7 @@ describe LucidWorks::Collection::Activity::Status do
|
|
11
11
|
describe "find" do
|
12
12
|
it "should retrieve status" do
|
13
13
|
status = @activity.status
|
14
|
-
status.should be_a LucidWorks::
|
14
|
+
status.should be_a LucidWorks::Activity::Status
|
15
15
|
|
16
16
|
status.running.should be_false
|
17
17
|
status.type.should == 'optimize'
|
@@ -20,13 +20,13 @@ describe "LucidWorks::Collection#prime_activities" do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should produce activities of the appropriate type" do
|
23
|
-
@collection.optimize_activity.should be_a LucidWorks::
|
23
|
+
@collection.optimize_activity.should be_a LucidWorks::Activity
|
24
24
|
@collection.optimize_activity.type.should == 'optimize'
|
25
|
-
@collection.spelling_activity.should be_a LucidWorks::
|
25
|
+
@collection.spelling_activity.should be_a LucidWorks::Activity
|
26
26
|
@collection.spelling_activity.type.should == 'spelling'
|
27
|
-
@collection.click_activity.should be_a LucidWorks::
|
27
|
+
@collection.click_activity.should be_a LucidWorks::Activity
|
28
28
|
@collection.click_activity.type.should == 'click'
|
29
|
-
@collection.autocomplete_activity.should be_a LucidWorks::
|
29
|
+
@collection.autocomplete_activity.should be_a LucidWorks::Activity
|
30
30
|
@collection.autocomplete_activity.type.should == 'autocomplete'
|
31
31
|
end
|
32
32
|
end
|
@@ -37,7 +37,7 @@ describe "LucidWorks::Collection#prime_activities" do
|
|
37
37
|
before :all do
|
38
38
|
@collection = @server.create_collection(:name => 'collection_with_no_activities_2', :parent => @server)
|
39
39
|
spelling_activity = @collection.create_activity(:type => 'spelling', :start_time => 3600, :active => true)
|
40
|
-
spelling_activity.should be_a LucidWorks::
|
40
|
+
spelling_activity.should be_a LucidWorks::Activity
|
41
41
|
end
|
42
42
|
|
43
43
|
it "should not create duplicates for existing types" do
|
@@ -61,13 +61,13 @@ describe "LucidWorks::Collection#prime_activities" do
|
|
61
61
|
|
62
62
|
it "should have a virtual attribute for each activity type" do
|
63
63
|
@collection.activities!.count.should == 4
|
64
|
-
@collection.optimize_activity.should be_a LucidWorks::
|
64
|
+
@collection.optimize_activity.should be_a LucidWorks::Activity
|
65
65
|
@collection.optimize_activity.type.should == 'optimize'
|
66
|
-
@collection.spelling_activity.should be_a LucidWorks::
|
66
|
+
@collection.spelling_activity.should be_a LucidWorks::Activity
|
67
67
|
@collection.spelling_activity.type.should == 'spelling'
|
68
|
-
@collection.click_activity.should be_a LucidWorks::
|
68
|
+
@collection.click_activity.should be_a LucidWorks::Activity
|
69
69
|
@collection.click_activity.type.should == 'click'
|
70
|
-
@collection.autocomplete_activity.should be_a LucidWorks::
|
70
|
+
@collection.autocomplete_activity.should be_a LucidWorks::Activity
|
71
71
|
@collection.autocomplete_activity.type.should == 'autocomplete'
|
72
72
|
end
|
73
73
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'lucid_works/collection/synonym'
|
3
2
|
|
4
|
-
describe LucidWorks::
|
3
|
+
describe LucidWorks::Synonym do
|
5
4
|
before do
|
6
5
|
@server = connect_to_live_server.tap {|server| server.reset_collections! }
|
7
6
|
load_collection
|
@@ -14,15 +13,15 @@ describe LucidWorks::Collection::Synonym do
|
|
14
13
|
|
15
14
|
describe "naming support" do
|
16
15
|
it "demodulizes names" do
|
17
|
-
LucidWorks::
|
18
|
-
LucidWorks::
|
19
|
-
LucidWorks::
|
16
|
+
LucidWorks::Synonym.model_name.singular.should == "synonym"
|
17
|
+
LucidWorks::Synonym.model_name.plural.should == "synonyms"
|
18
|
+
LucidWorks::Synonym.model_name.collection.should == "synonyms"
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
23
22
|
describe "conversion support" do
|
24
23
|
it "can be converted to a key" do
|
25
|
-
synonym = LucidWorks::
|
24
|
+
synonym = LucidWorks::Synonym.new(:collection => @collection, :mapping => "car, auto")
|
26
25
|
synonym.save
|
27
26
|
synonym.to_key.should == [8]
|
28
27
|
end
|
@@ -30,19 +29,19 @@ describe LucidWorks::Collection::Synonym do
|
|
30
29
|
|
31
30
|
describe "validations" do
|
32
31
|
it "requires a mapping" do
|
33
|
-
synonym = LucidWorks::
|
32
|
+
synonym = LucidWorks::Synonym.new(:mapping => "")
|
34
33
|
synonym.should_not be_valid
|
35
34
|
synonym.errors[:mapping].should == ["can't be blank"]
|
36
35
|
end
|
37
36
|
|
38
37
|
it "requires the mapping to be a list" do
|
39
|
-
synonym = LucidWorks::
|
38
|
+
synonym = LucidWorks::Synonym.new(:mapping => "car")
|
40
39
|
synonym.should_not be_valid
|
41
40
|
synonym.errors[:mapping].should == ["must be a comma-separated list"]
|
42
41
|
end
|
43
42
|
|
44
43
|
it "does not allow '=>' style mappings (for now)" do
|
45
|
-
synonym = LucidWorks::
|
44
|
+
synonym = LucidWorks::Synonym.new(:mapping => "car => auto")
|
46
45
|
synonym.should_not be_valid
|
47
46
|
synonym.errors[:mapping].should == ["must be a comma-separated list"]
|
48
47
|
end
|
@@ -50,7 +49,7 @@ describe LucidWorks::Collection::Synonym do
|
|
50
49
|
|
51
50
|
describe :save do
|
52
51
|
context "when valid" do
|
53
|
-
subject { LucidWorks::
|
52
|
+
subject { LucidWorks::Synonym.new(:collection => @collection, :mapping => "car, auto") }
|
54
53
|
|
55
54
|
it "gets saved in the collection settings" do
|
56
55
|
subject.save
|
@@ -73,7 +72,7 @@ describe LucidWorks::Collection::Synonym do
|
|
73
72
|
end
|
74
73
|
|
75
74
|
context "when invalid" do
|
76
|
-
subject { LucidWorks::
|
75
|
+
subject { LucidWorks::Synonym.new(:collection => @collection, :mapping => "car") }
|
77
76
|
|
78
77
|
it "doesn't get saved in the collection settings" do
|
79
78
|
subject.save
|
@@ -91,7 +90,7 @@ describe LucidWorks::Collection::Synonym do
|
|
91
90
|
end
|
92
91
|
|
93
92
|
context "when orphan" do
|
94
|
-
subject { LucidWorks::
|
93
|
+
subject { LucidWorks::Synonym.new(:mapping => "car, auto") }
|
95
94
|
|
96
95
|
it "returns false" do
|
97
96
|
subject.save.should be(false)
|
@@ -94,13 +94,13 @@ describe LucidWorks::Collection do
|
|
94
94
|
|
95
95
|
context "for a collection with some activities" do
|
96
96
|
before do
|
97
|
-
activity = LucidWorks::
|
97
|
+
activity = LucidWorks::Activity.create(
|
98
98
|
:type => 'optimize',
|
99
99
|
:start_time => 8600,
|
100
100
|
:collection => @collection
|
101
101
|
)
|
102
102
|
activity.should be_persisted
|
103
|
-
activity = LucidWorks::
|
103
|
+
activity = LucidWorks::Activity.create(
|
104
104
|
:type => 'spelling',
|
105
105
|
:start_time => 8600,
|
106
106
|
:collection => @collection
|
@@ -112,7 +112,7 @@ describe LucidWorks::Collection do
|
|
112
112
|
activities = @collection.activities
|
113
113
|
activities.count.should == 2
|
114
114
|
activities.each do |activity|
|
115
|
-
activity.should be_a(LucidWorks::
|
115
|
+
activity.should be_a(LucidWorks::Activity)
|
116
116
|
end
|
117
117
|
end
|
118
118
|
end
|
@@ -122,7 +122,7 @@ describe LucidWorks::Collection do
|
|
122
122
|
before do
|
123
123
|
@collection = @server.collection('collection1')
|
124
124
|
# require 'ruby-debug'; debugger
|
125
|
-
# activity = LucidWorks::
|
125
|
+
# activity = LucidWorks::Activity.create(
|
126
126
|
# :type => 'optimize',
|
127
127
|
# :start_time => 8600,
|
128
128
|
# :collection => @collection
|
@@ -137,7 +137,7 @@ describe LucidWorks::Collection do
|
|
137
137
|
:template => @template,
|
138
138
|
:parent => @server)
|
139
139
|
|
140
|
-
LucidWorks::
|
140
|
+
LucidWorks::Activity.create(activity_params.merge(:parent => c))
|
141
141
|
c.activities.size.should == 1
|
142
142
|
c.prime_activities
|
143
143
|
c.activities.size.should == 4
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: lucid_works
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.6.
|
5
|
+
version: 0.6.19
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Sam Pierson
|
@@ -111,19 +111,18 @@ files:
|
|
111
111
|
- Rakefile
|
112
112
|
- config/locales/en.yml
|
113
113
|
- lib/lucid_works.rb
|
114
|
+
- lib/lucid_works/activity.rb
|
115
|
+
- lib/lucid_works/activity/history.rb
|
116
|
+
- lib/lucid_works/activity/status.rb
|
114
117
|
- lib/lucid_works/associations.rb
|
115
118
|
- lib/lucid_works/associations/has_many.rb
|
116
119
|
- lib/lucid_works/associations/has_one.rb
|
117
120
|
- lib/lucid_works/associations/proxy.rb
|
118
121
|
- lib/lucid_works/base.rb
|
119
122
|
- lib/lucid_works/collection.rb
|
120
|
-
- lib/lucid_works/collection/activity.rb
|
121
|
-
- lib/lucid_works/collection/activity/history.rb
|
122
|
-
- lib/lucid_works/collection/activity/status.rb
|
123
123
|
- lib/lucid_works/collection/index.rb
|
124
124
|
- lib/lucid_works/collection/info.rb
|
125
125
|
- lib/lucid_works/collection/settings.rb
|
126
|
-
- lib/lucid_works/collection/synonym.rb
|
127
126
|
- lib/lucid_works/crawler.rb
|
128
127
|
- lib/lucid_works/datasource.rb
|
129
128
|
- lib/lucid_works/datasource/crawldata.rb
|
@@ -145,6 +144,7 @@ files:
|
|
145
144
|
- lib/lucid_works/schema.rb
|
146
145
|
- lib/lucid_works/server.rb
|
147
146
|
- lib/lucid_works/simple_naming.rb
|
147
|
+
- lib/lucid_works/synonym.rb
|
148
148
|
- lib/lucid_works/utils.rb
|
149
149
|
- lib/lucid_works/version.rb
|
150
150
|
- lucid_works.gemspec
|
@@ -1,191 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/numeric/time.rb'
|
2
|
-
module LucidWorks
|
3
|
-
class Collection
|
4
|
-
class Activity < Base
|
5
|
-
TYPES = %w{ optimize spelling click autocomplete}
|
6
|
-
|
7
|
-
# Unlike Datasource schedules, the API allows multiples of these
|
8
|
-
# So, to be honest with gem users, we can't really say this:
|
9
|
-
# self.singleton = true
|
10
|
-
belongs_to :collection
|
11
|
-
has_many :histories, :class_name => :history
|
12
|
-
has_one :status
|
13
|
-
|
14
|
-
schema do
|
15
|
-
attribute :period
|
16
|
-
attribute :start_time, :iso8601
|
17
|
-
attribute :active, :boolean
|
18
|
-
attribute :type, :string, :values => TYPES, :omit_during_update => true
|
19
|
-
end
|
20
|
-
|
21
|
-
validates_presence_of :type, :start_time
|
22
|
-
validates_numericality_of :period, :allow_blank => true
|
23
|
-
|
24
|
-
def frequency
|
25
|
-
case period
|
26
|
-
when 1.weeks.seconds then 'weekly'
|
27
|
-
when 1.days.seconds then 'daily'
|
28
|
-
when 1.hours.seconds then 'hourly'
|
29
|
-
when 0 then nil
|
30
|
-
else 'custom'
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
#
|
35
|
-
# accepts hourly, daily, weekly, and sets period to respective # of seconds
|
36
|
-
#
|
37
|
-
def frequency=(frequency)
|
38
|
-
self.period = case frequency
|
39
|
-
when 'hourly' then 1.hours.seconds
|
40
|
-
when 'daily' then 1.days.seconds
|
41
|
-
when 'weekly' then 1.weeks.seconds
|
42
|
-
else raise "unknown frequency"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
#
|
47
|
-
# predict when action will occur next if active at that time
|
48
|
-
#
|
49
|
-
def next_start
|
50
|
-
return start_time if (now = Time.now) <= start_time
|
51
|
-
# require 'ruby-debug'; debugger
|
52
|
-
time_since_start = now - start_time
|
53
|
-
last_interval_num = (time_since_start / period).to_i
|
54
|
-
next_interval_num = if (time_since_start % period) == 0
|
55
|
-
# this is sort of a stupid condition b/c time precision is millisecond or less
|
56
|
-
# for human purposes we wouldn't care if the result were as though the call
|
57
|
-
# happened a millisecond later. But whatever.
|
58
|
-
last_interval_num # the next interval *is* the last interval if it is exactly now
|
59
|
-
else
|
60
|
-
last_interval_num + 1
|
61
|
-
end
|
62
|
-
start_time + period * next_interval_num
|
63
|
-
end
|
64
|
-
|
65
|
-
#
|
66
|
-
# phantom attribute for compiling real start time and frequency from appropriate form data
|
67
|
-
# Allows the user to specify a schedule simply with repeat interval (hourly, daily, weekly)
|
68
|
-
# and time within the interval to run (e.g 5 past, or Tuesdays at 2:15)
|
69
|
-
#
|
70
|
-
# We have to figure out when the actual start time will be because we are not asking
|
71
|
-
# the user for this. It needs to be:
|
72
|
-
# * at the requested time relative to the interval chosen
|
73
|
-
# * as soon as possible
|
74
|
-
# * in the future
|
75
|
-
#
|
76
|
-
# This is obvious to humans, but when computing it, you run into the following problem. It
|
77
|
-
# happens with all interval sizes, but for the sake of example, we'll use weekly:
|
78
|
-
#
|
79
|
-
# Problem 1) If today is Thursday, and the user asks for "weekly on Wednesdays," Wednesday has
|
80
|
-
# already happened this week. We have to make sure we pick the nearest wednesday that is in
|
81
|
-
# the future
|
82
|
-
#
|
83
|
-
# Problem 2) If today is Tuesday, and the user asks for "weekly on Wednesdays,", the simple solution
|
84
|
-
# to problem 1 (start next week instead of this week) causes you to skip this Tuesday, even though
|
85
|
-
# it is valid and is what the user would expect
|
86
|
-
#
|
87
|
-
# The algorith to solve both problems at once is this:
|
88
|
-
# * From Time.now, back up to the beginning of the interval even if it is in the past.
|
89
|
-
# * Fast forward to the requested siimple start time
|
90
|
-
# * If you are still in the past, advance by one interval
|
91
|
-
#
|
92
|
-
def schedule=(all_attributes)
|
93
|
-
# convert to format accepted by Time.advance
|
94
|
-
if all_attributes['start']
|
95
|
-
all_attributes['start'].to_options!
|
96
|
-
all_attributes['start'].each{|k,v| all_attributes['start'][k]=v.to_i}
|
97
|
-
end
|
98
|
-
|
99
|
-
self.active = all_attributes['active'] if all_attributes.keys.include?('active')
|
100
|
-
|
101
|
-
now = Time.now
|
102
|
-
self.frequency = all_attributes['frequency']
|
103
|
-
self.start_time =
|
104
|
-
case all_attributes['frequency']
|
105
|
-
when 'weekly'
|
106
|
-
# require 'ruby-debug'; debugger
|
107
|
-
start = now.beginning_of_week.advance(all_attributes['start'])
|
108
|
-
start < now ? start.advance(:weeks => 1) : start
|
109
|
-
when 'daily'
|
110
|
-
start = now.beginning_of_day.advance(all_attributes['start'])
|
111
|
-
start < now ? start.advance(:days => 1) : start
|
112
|
-
when 'hourly'
|
113
|
-
start = now.change(:min => 0).advance(all_attributes['start'])
|
114
|
-
start < now ? start.advance(:hours => 1) : start
|
115
|
-
else
|
116
|
-
puts "*** frequency: <#{all_attributes[:frequency]}>"
|
117
|
-
raise "unexpected frequency encountered"
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
#
|
122
|
-
# convenince method for setting the current value in a view selector
|
123
|
-
#
|
124
|
-
def hour
|
125
|
-
self.start_time.localtime.hour rescue nil
|
126
|
-
end
|
127
|
-
|
128
|
-
#
|
129
|
-
# convenince method for setting the current value in a view selector
|
130
|
-
#
|
131
|
-
def min
|
132
|
-
self.start_time.localtime.min rescue nil
|
133
|
-
end
|
134
|
-
|
135
|
-
#
|
136
|
-
# convenince method for setting the current value in a view selector
|
137
|
-
#
|
138
|
-
def day_of_week
|
139
|
-
# subract 1 because '%u' returns 1-7, and this is working with zero-indexed ruby arrays
|
140
|
-
self.start_time.localtime.strftime('%u').to_i - 1 rescue nil
|
141
|
-
end
|
142
|
-
|
143
|
-
def start_is_more_than_one_period_in_the_future?
|
144
|
-
# This works fine with start times that are multiple periods in the past
|
145
|
-
# because that gives a negative difference, and period is always >= 0
|
146
|
-
(self.start_time - Time.now) > self.period
|
147
|
-
end
|
148
|
-
|
149
|
-
#
|
150
|
-
# convenience method for detecting whether schedule can be represented in simple format
|
151
|
-
# or should be described as custom (meaning it is beyond the capabilities of the convenience methods)
|
152
|
-
#
|
153
|
-
def custom?
|
154
|
-
return false unless self.start_time
|
155
|
-
return true if self.frequency == 'custom'
|
156
|
-
return true if self.start_is_more_than_one_period_in_the_future?
|
157
|
-
return false
|
158
|
-
end
|
159
|
-
|
160
|
-
#
|
161
|
-
# convenience method for setting defaults in a UI that make sense for a user
|
162
|
-
#
|
163
|
-
def ui_appropriate_defaults!
|
164
|
-
if self.start_time.blank? || self.period == 0
|
165
|
-
self.active = true
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def t_type
|
170
|
-
I18n.t(type, :scope => 'activemodel.models.lucid_works.collection.activity.type')
|
171
|
-
end
|
172
|
-
|
173
|
-
def start
|
174
|
-
self.start_time = 0
|
175
|
-
self.active = true
|
176
|
-
save
|
177
|
-
end
|
178
|
-
|
179
|
-
private
|
180
|
-
|
181
|
-
def exact(interval, period)
|
182
|
-
if period % (result = 1.send(interval)) == 0
|
183
|
-
period / result
|
184
|
-
else
|
185
|
-
false
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
module LucidWorks
|
2
|
-
|
3
|
-
class Collection
|
4
|
-
|
5
|
-
class Synonym
|
6
|
-
include ActiveModel::Validations
|
7
|
-
include ActiveModel::Conversion
|
8
|
-
extend ActiveModel::Naming
|
9
|
-
extend SimpleNaming
|
10
|
-
|
11
|
-
attr_reader :id, :collection, :persisted
|
12
|
-
attr_accessor :mapping
|
13
|
-
|
14
|
-
validates_presence_of :mapping
|
15
|
-
validates_each :mapping, :allow_blank => true do |model, attr, value|
|
16
|
-
model.errors.add(attr, 'must be a comma-separated list') if value =~ /=>/ || value.split(',').size < 2
|
17
|
-
end
|
18
|
-
|
19
|
-
def initialize(attributes = {})
|
20
|
-
@id = attributes[:id]
|
21
|
-
@collection = attributes[:collection]
|
22
|
-
@mapping = attributes[:mapping]
|
23
|
-
@persisted = attributes[:persisted] || false
|
24
|
-
end
|
25
|
-
|
26
|
-
def persisted?
|
27
|
-
@persisted
|
28
|
-
end
|
29
|
-
|
30
|
-
def save
|
31
|
-
return false unless valid?
|
32
|
-
if persisted?
|
33
|
-
settings.synonym_list[id] = mapping
|
34
|
-
else
|
35
|
-
settings.synonym_list << mapping
|
36
|
-
@id = settings.synonym_list.length - 1
|
37
|
-
end
|
38
|
-
settings.save
|
39
|
-
@persisted = true
|
40
|
-
rescue
|
41
|
-
false
|
42
|
-
end
|
43
|
-
|
44
|
-
def update_attributes(attrs_and_values)
|
45
|
-
attrs_and_values.each do |attr,value|
|
46
|
-
self.send("#{attr}=", value)
|
47
|
-
end
|
48
|
-
save
|
49
|
-
end
|
50
|
-
|
51
|
-
def destroy
|
52
|
-
settings.synonym_list.delete_at(id)
|
53
|
-
settings.save
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def settings
|
59
|
-
collection.settings
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|