pq-wsm 0.0.1

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