pq-wsm 0.0.1

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 (35) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +86 -0
  3. data/Rakefile +38 -0
  4. data/lib/generators/pc_queues/install_generator.rb +9 -0
  5. data/lib/generators/pc_queues/migration_generator.rb +15 -0
  6. data/lib/generators/pc_queues/templates/README +15 -0
  7. data/lib/generators/pc_queues/templates/initializer.rb +6 -0
  8. data/lib/generators/pc_queues/templates/migration.rb +36 -0
  9. data/lib/pc_queues.rb +43 -0
  10. data/lib/pc_queues/acts_as_enqueable.rb +31 -0
  11. data/lib/pc_queues/acts_as_queue_owner.rb +30 -0
  12. data/lib/pc_queues/priority_queue_item.rb +13 -0
  13. data/lib/pc_queues/queue.rb +312 -0
  14. data/lib/pc_queues/queue_item.rb +58 -0
  15. data/lib/pc_queues/queue_rule.rb +32 -0
  16. data/lib/pc_queues/queue_rule_set.rb +40 -0
  17. data/lib/pc_queues/queue_rules/boolean_queue_rule.rb +34 -0
  18. data/lib/pc_queues/queue_rules/numeric_queue_rule.rb +42 -0
  19. data/lib/pc_queues/queue_rules/sample_queue_rule.rb +36 -0
  20. data/lib/pc_queues/queue_rules/string_match_queue_rule.rb +44 -0
  21. data/lib/pc_queues/railtie.rb +10 -0
  22. data/lib/pc_queues/version.rb +3 -0
  23. data/lib/tasks/pc_queues_tasks.rake +4 -0
  24. data/spec/boolean_queue_rule_spec.rb +34 -0
  25. data/spec/numeric_queue_rule_spec.rb +123 -0
  26. data/spec/queue_spec.rb +494 -0
  27. data/spec/sample_queue_rule_spec.rb +34 -0
  28. data/spec/spec_helper.rb +30 -0
  29. data/spec/string_match_rule_spec.rb +76 -0
  30. data/spec/support/active_record.rb +42 -0
  31. data/spec/support/application.rb +122 -0
  32. data/spec/support/queue_helpers.rb +16 -0
  33. data/spec/support/redis_helpers.rb +32 -0
  34. data/spec/support/time.rb +23 -0
  35. metadata +131 -0
@@ -0,0 +1,58 @@
1
+ #
2
+ # This source file is part of project: PcQueues
3
+ #
4
+ # A Proctoring Workflow Platform
5
+ #
6
+ # Copyright (c) ProctorCam Inc. 2014 All rights reserved
7
+ #
8
+ module PcQueues
9
+ # QueueItems are the container for object instances that are
10
+ # enqueued and dequeued from a Queue
11
+ class QueueItem < ActiveRecord::Base
12
+ # <<<<<<< HEAD
13
+ # attr_accessible :enqueued_time, :position, :queue, :type, :enqueable
14
+ def self.accessible_attributes
15
+ [:enqueued_time, :position, :queue, :type, :enqueable]
16
+ end
17
+ # =======
18
+ # attr_accessible :enqueued_time, :position, :queue, :type, :enqueable
19
+
20
+ # attr_accessor :cur_position
21
+
22
+ # >>>>>>> 5a7112729e02a8228e3591999ec3a5d0b912b86f
23
+ belongs_to :queue, :class_name => 'PcQueues::Queue'
24
+ belongs_to :enqueable, :polymorphic => true
25
+
26
+ # Ensure there are no duplicate enqueables within any queue
27
+ validates_uniqueness_of :enqueable_id, :scope => :queue_id
28
+
29
+ # Attributes include
30
+ # :position -> the 1-based position when added to the queue
31
+ # :equeued_time -> the seconds since the epoch when the item was added
32
+ # to the queue
33
+
34
+ def stats
35
+ { :position => cur_position, estimated_time_left: time_remaining }
36
+ end
37
+
38
+ # The advancement rate for this item
39
+ # -> round to nearest thousandth digit
40
+ def rate()
41
+ @rate ||= (wait_time > 0 && advancement / wait_time.to_f) || 0
42
+ end
43
+
44
+ # number of positions this item has travelled
45
+ def advancement()
46
+ position - cur_position
47
+ end
48
+
49
+ # The wait time in seconds for this item
50
+ def wait_time()
51
+ Time.now.to_i - enqueued_time
52
+ end
53
+
54
+ def time_remaining()
55
+ rate * position || wait_time
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,32 @@
1
+ #
2
+ # This source file is part of project: PcQueues
3
+ #
4
+ # Copyright (c) ProctorCam Inc. 2014 All rights reserved
5
+ #
6
+ module PcQueues
7
+ # QueueRule is the base class (STI) for Rules that are evaluated as part of a QueueRuleSet
8
+ # There must be at least one implmentor of the passes? method in the hierachy of QueueRule
9
+ # being stored in a QueueRuleSet.
10
+ class QueueRule < ActiveRecord::Base
11
+ # attr_accessible :numeric_value, :string_value, :bool_value, :type
12
+ def self.accessible_attributes
13
+ [:numeric_value, :string_value, :bool_value, :type]
14
+ end
15
+ belongs_to :queue_rule_set
16
+
17
+ # A convenience method for creating QueueRule instances with more declaritive
18
+ # code. Derived classes can implement and super to this.
19
+ #
20
+ # Eg. rule_set.queue_rules.create(MyNewRule.options special_opt1: value)
21
+ #
22
+ def self.options(opts = {})
23
+ opts[:type] = self.name
24
+ opts
25
+ end
26
+
27
+ # All derived classes must implement this method and it must return a Boolean
28
+ def passes?(enqueable, *args)
29
+ raise NotImplementedError, "The class: #{self.class.name} requires an implementation of #{__method__}"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ #
2
+ # This source file is part of project: PcQueues
3
+ #
4
+ # A Proctoring Workflow Platform
5
+ #
6
+ # Copyright (c) ProctorCam Inc. 2014 All rights reserved
7
+ #
8
+ module PcQueues
9
+ # A QueueRuleSet is a collection of QueueRules that can is evaluated
10
+ # as an AND/OR clause depending on the value of is_any which indicates
11
+ # an OR
12
+ class QueueRuleSet < ActiveRecord::Base
13
+ # attr_accessible :is_any
14
+ def self.accessible_attributes
15
+ [:is_any]
16
+ end
17
+ belongs_to :queue
18
+
19
+ has_many :queue_rules, dependent: :destroy
20
+
21
+ def passes?(enqueuable, *args)
22
+ if is_any
23
+ any_pass?(enqueuable, *args)
24
+ else
25
+ all_pass?(enqueuable, *args)
26
+ end
27
+ end
28
+
29
+ def any_pass?(enqueuable, *args)
30
+ queue_rules.all.each { |rule| return true if rule.passes?(enqueuable, *args) }
31
+ false
32
+ end
33
+
34
+ def all_pass?(enqueuable, *args)
35
+ queue_rules.all.each { |rule| return false unless rule.passes?(enqueuable, *args) }
36
+ true
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,34 @@
1
+ #
2
+ # This source file is part of project: PcQueues
3
+ #
4
+ # Copyright (c) ProctorCam Inc. 2014 All rights reserved
5
+ #
6
+
7
+ module PcQueues
8
+ module QueueRules
9
+ ###
10
+ # A BooleanQueueRule is a QueueRule that returns true when
11
+ # the value matches the bool_value attribute
12
+ #
13
+ class BooleanQueueRule < QueueRule
14
+
15
+ def self.options(opts = {})
16
+ opts[:bool_value] ||= opts[:value]
17
+ opts[:bool_value] = true if opts[:bool_value].nil?
18
+ opts.except! :value
19
+ super
20
+ end
21
+
22
+ # Implementors must provide an implementation of this method that takes
23
+ # the args splat passed to the queue and returns a numberic value
24
+ def value(obj, *args)
25
+ raise NotImplementedError, "The class: #{self.class.name} requires an implementation of #{__method__}"
26
+ end
27
+
28
+ def passes?(obj, *args)
29
+ value(obj, *args) == bool_value
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,42 @@
1
+ #
2
+ # This source file is part of project: PcQueues
3
+ #
4
+ # Copyright (c) ProctorCam Inc. 2014 All rights reserved
5
+ #
6
+
7
+ module PcQueues
8
+ module QueueRules
9
+ ###
10
+ # A NumericQueueRule is a QueueRule that returns true when
11
+ # comparing the value to the numeric_value using an operator
12
+ # stored as a string.
13
+ #
14
+ # These operators must be one of: <, >, ==, >=, <=
15
+ #
16
+ class NumericQueueRule < QueueRule
17
+ validates :string_value, inclusion: { in: %w[< > == <= >= !=] }
18
+
19
+ def self.options(opts = {})
20
+ opts[:numeric_value] ||= opts[:value]
21
+ opts[:string_value] ||= opts[:operator]
22
+ opts.except! :operator, :value
23
+ super
24
+ end
25
+
26
+ def operator
27
+ string_value
28
+ end
29
+
30
+ # Implementors must provide an implementation of this method that takes
31
+ # the args splat passed to the queue and returns a numberic value
32
+ def value(obj, *args)
33
+ raise NotImplementedError, "The class: #{self.class.name} requires an implementation of #{__method__}"
34
+ end
35
+
36
+ def passes?(obj, *args)
37
+ value(obj, *args).send(operator, numeric_value)
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
1
+ #
2
+ # This source file is part of project: PcQueues
3
+ #
4
+ # Copyright (c) ProctorCam Inc. 2014 All rights reserved
5
+ #
6
+
7
+ module PcQueues
8
+ module QueueRules
9
+ ###
10
+ # A SampleQueueRule is a QueueRule that will 'pass' for a perscribed
11
+ # sample of occurrences
12
+ #
13
+ # Sampling is random, so you many find slightly more or less than the perscribed
14
+ # number of items passing.
15
+ #
16
+ class SampleQueueRule < QueueRule
17
+
18
+ def self.options(opts = {})
19
+ opts[:numeric_value] ||= opts[:sample_percent]
20
+ opts.except! :sample_percent
21
+ super
22
+ end
23
+
24
+ @@die = Random.new
25
+
26
+ def sample_percent
27
+ numeric_value
28
+ end
29
+
30
+ def passes?(obj, *args)
31
+ sample_percent == 100 || @@die.rand(100) < sample_percent
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ #
2
+ # This source file is part of project: PcQueues
3
+ #
4
+ # Copyright (c) ProctorCam Inc. 2014 All rights reserved
5
+ #
6
+
7
+ module PcQueues
8
+ module QueueRules
9
+ ###
10
+ # A StringMatchQueueRule is a QueueRule that will 'pass' if the
11
+ # current value exactly matches a regular expression.
12
+ #
13
+ class StringMatchQueueRule < QueueRule
14
+
15
+ # Possible options for StringMatchQueueRule include:
16
+ #
17
+ # * _:regular_expression_ - an exact regular expression
18
+ # * _:contains_ - an exact string exists within the value
19
+ # * _:icontains_ - ignore case with string contained within the value
20
+ def self.options(opts = {})
21
+ opts[:string_value] ||= "/^#{opts[:regular_expression]}$/" if opts.key? :regular_expression
22
+ opts[:string_value] ||= "/#{opts[:contains]}/" if opts.key? :contains
23
+ opts[:string_value] ||= "/#{opts[:icontains]}/i" if opts.key? :icontains
24
+ opts.except! :regular_expression, :contains, :icontains
25
+ super
26
+ end
27
+
28
+ def regular_expression
29
+ eval(string_value)
30
+ end
31
+
32
+ # Implementors must provide an implementation of this method that takes
33
+ # the args splat passed to the queue and returns a String
34
+ def value(obj, *args)
35
+ raise NotImplementedError, "The class: #{self.class.name} requires an implementation of #{__method__}"
36
+ end
37
+
38
+ def passes?(obj, *args)
39
+ !regular_expression.match(value(obj, *args)).nil?
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,10 @@
1
+ require 'active_record/railtie'
2
+ require 'active_support/core_ext'
3
+
4
+ module PcQueues
5
+ class Railtie < Rails::Railtie
6
+ if defined?(ActiveRecord::Base)
7
+ # Add anything we want to use to extend ActiveRecordBase
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module PcQueues
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :pc_queues do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,34 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe PcQueues::QueueRules::BooleanQueueRule do
4
+
5
+ before :each do
6
+ PcQueues.redis { |r| r.flushall }
7
+
8
+ @company = Company.create :name => "ProctorCam"
9
+ @company.create_hr_queue
10
+
11
+ @people = {
12
+ :john => Person.create(:name => "John", :age => 42, :is_female => false),
13
+ :sally => Person.create(:name => "Sally", :age => 29, :is_female => true),
14
+ :mildred => Person.create(:name => "Mildred", :age => 67, :is_female => true),
15
+ :lars => Person.create(:name => "Lars", :age => 34, :is_female => false)
16
+ }
17
+ end
18
+
19
+ after :each do
20
+ @company.hr_queue.destroy
21
+ @company.destroy
22
+ end
23
+
24
+ it "does not pass if the boolean provided is different from the value passed" do
25
+ queue_rule = GenderQueueRule.create GenderQueueRule.options({:is_female => true})
26
+ expect(queue_rule.passes? @people[:john]).to eq(false)
27
+ end
28
+
29
+ it "does should pass if the boolean provided is the same as the value passed" do
30
+ queue_rule = GenderQueueRule.create GenderQueueRule.options({:is_female => true})
31
+ expect(queue_rule.passes? @people[:mildred]).to eq(true)
32
+ end
33
+
34
+ end
@@ -0,0 +1,123 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe PcQueues::QueueRules::NumericQueueRule do
4
+
5
+ before :each do
6
+ PcQueues.redis { |r| r.flushall }
7
+
8
+ @company = Company.create :name => "ProctorCam"
9
+ @company.create_hr_queue
10
+
11
+ @people = {
12
+ :john => Person.create(:name => "John", :age => 42, :is_female => false),
13
+ :sally => Person.create(:name => "Sally", :age => 29, :is_female => true),
14
+ :mildred => Person.create(:name => "Mildred", :age => 67, :is_female => true),
15
+ :lars => Person.create(:name => "Lars", :age => 34, :is_female => false)
16
+ }
17
+ end
18
+
19
+ after :each do
20
+ @company.hr_queue.destroy
21
+ @company.destroy
22
+ end
23
+
24
+ it "passes if the numeric value does pass the assertion" do
25
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:sally].age + 1, :operator => '>='})
26
+ expect(queue_rule.passes? @people[:john]).to eq(true)
27
+ end
28
+
29
+ it "does not pass if the numeric value does not pass the assertion" do
30
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:sally].age + 1, :operator => '>='})
31
+ expect(queue_rule.passes? @people[:sally]).to eq(false)
32
+ end
33
+
34
+ describe "when using == operator" do
35
+
36
+ it "passes when values are the same" do
37
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age, :operator => '=='})
38
+ expect(queue_rule.passes? @people[:john]).to eq(true)
39
+ end
40
+
41
+ it "does not pass when values are not the same" do
42
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age + 1, :operator => '=='})
43
+ expect(queue_rule.passes? @people[:john]).to eq(false)
44
+ end
45
+
46
+ end
47
+
48
+ describe "when using != operator" do
49
+
50
+ it "passes when values are the same" do
51
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age + 3, :operator => '!='})
52
+ expect(queue_rule.passes? @people[:john]).to eq(true)
53
+ end
54
+
55
+ it "does not pass when values are not the same" do
56
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age, :operator => '!='})
57
+ expect(queue_rule.passes? @people[:john]).to eq(false)
58
+ end
59
+
60
+ end
61
+
62
+ describe "when using < operator" do
63
+
64
+ it "passes when values are the same" do
65
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age + 1, :operator => '<'})
66
+ expect(queue_rule.passes? @people[:john]).to eq(true)
67
+ end
68
+
69
+ it "does not pass when values are not the same" do
70
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age, :operator => '<'})
71
+ expect(queue_rule.passes? @people[:john]).to eq(false)
72
+ end
73
+
74
+ end
75
+
76
+ describe "when using <= operator" do
77
+
78
+ it "passes when values are the same" do
79
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age + 1, :operator => '<='})
80
+ expect(queue_rule.passes? @people[:john]).to eq(true)
81
+ end
82
+
83
+ it "does not pass when values are not the same" do
84
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age - 1, :operator => '<='})
85
+ expect(queue_rule.passes? @people[:john]).to eq(false)
86
+ end
87
+
88
+ end
89
+
90
+ describe "when using > operator" do
91
+
92
+ it "passes when values are the same" do
93
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age - 1, :operator => '>'})
94
+ expect(queue_rule.passes? @people[:john]).to eq(true)
95
+ end
96
+
97
+ it "does not pass when values are not the same" do
98
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age, :operator => '>'})
99
+ expect(queue_rule.passes? @people[:john]).to eq(false)
100
+ end
101
+
102
+ end
103
+
104
+ describe "when using >= operator" do
105
+
106
+ it "passes when values are the same" do
107
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age - 1, :operator => '>='})
108
+ expect(queue_rule.passes? @people[:john]).to eq(true)
109
+ end
110
+
111
+ it "does not pass when values are not the same" do
112
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age + 1, :operator => '>='})
113
+ expect(queue_rule.passes? @people[:john]).to eq(false)
114
+ end
115
+
116
+ end
117
+
118
+ it "does not take arbitrary assignments for operator" do
119
+ queue_rule = AgeQueueRule.create AgeQueueRule.options({:value => @people[:john].age - 1, :operator => 'NOT A THING'})
120
+ expect(queue_rule.valid?).to eq(false)
121
+ end
122
+
123
+ end