rails-queue-it 0.1.0
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.
- checksums.yaml +7 -0
- data/.circleci/config.yml +116 -0
- data/.circleci/setup-rubygems.sh +3 -0
- data/.editorconfig +24 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.rubocop.yml +503 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +277 -0
- data/Guardfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +202 -0
- data/Rakefile +10 -0
- data/app/assets/config/queue_it_manifest.js +1 -0
- data/app/assets/images/queue_it/.keep +0 -0
- data/app/assets/stylesheets/queue_it/application.css +15 -0
- data/app/controllers/queue_it/application_controller.rb +5 -0
- data/app/helpers/queue_it/application_helper.rb +4 -0
- data/app/jobs/queue_it/application_job.rb +4 -0
- data/app/mailers/queue_it/application_mailer.rb +6 -0
- data/app/models/concerns/queue_it/queable.rb +89 -0
- data/app/models/queue_it/application_record.rb +5 -0
- data/app/models/queue_it/node.rb +68 -0
- data/app/models/queue_it/queue.rb +111 -0
- data/app/views/layouts/queue_it/application.html.erb +15 -0
- data/bin/rails +25 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20210723140008_create_queue_it_queues.rb +10 -0
- data/db/migrate/20210723140434_create_queue_it_nodes.rb +12 -0
- data/lib/generators/queue_it/install/USAGE +5 -0
- data/lib/generators/queue_it/install/install_generator.rb +19 -0
- data/lib/generators/queue_it/install/templates/initializer.rb +2 -0
- data/lib/queue_it.rb +25 -0
- data/lib/queue_it/engine.rb +15 -0
- data/lib/queue_it/example_class.rb +7 -0
- data/lib/queue_it/version.rb +3 -0
- data/lib/tasks/auto_annotate_models.rake +67 -0
- data/lib/tasks/queue_it_tasks.rake +4 -0
- data/queue_it.gemspec +38 -0
- data/spec/concerns/queue_it/queable_spec.rb +492 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/javascript/packs/application.js +15 -0
- data/spec/dummy/app/jobs/application_job.rb +7 -0
- data/spec/dummy/app/mailers/application_mailer.rb +4 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/models/task.rb +3 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +33 -0
- data/spec/dummy/config.ru +5 -0
- data/spec/dummy/config/application.rb +30 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/cable.yml +10 -0
- data/spec/dummy/config/database.yml +27 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +81 -0
- data/spec/dummy/config/environments/production.rb +112 -0
- data/spec/dummy/config/environments/test.rb +49 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/dummy/config/initializers/assets.rb +12 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +33 -0
- data/spec/dummy/config/puma.rb +38 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/spring.rb +6 -0
- data/spec/dummy/config/storage.yml +34 -0
- data/spec/dummy/db/migrate/20210723142357_create_users.rb +9 -0
- data/spec/dummy/db/migrate/20210723172002_create_tasks.rb +9 -0
- data/spec/dummy/db/schema.rb +52 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/spec/models/task_spec.rb +7 -0
- data/spec/dummy/spec/models/user_spec.rb +7 -0
- data/spec/factories/queue_it/nodes.rb +18 -0
- data/spec/factories/queue_it/queues.rb +26 -0
- data/spec/factories/tasks.rb +11 -0
- data/spec/factories/users.rb +5 -0
- data/spec/fixtures/files/image.png +0 -0
- data/spec/fixtures/files/video.mp4 +0 -0
- data/spec/models/queue_it/node_spec.rb +43 -0
- data/spec/models/queue_it/queue_spec.rb +116 -0
- data/spec/queue_it_spec.rb +16 -0
- data/spec/rails_helper.rb +52 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/test_helpers.rb +5 -0
- metadata +441 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require "bundler/setup"
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
|
8
|
+
load "rails/tasks/engine.rake"
|
|
9
|
+
load "rails/tasks/statistics.rake"
|
|
10
|
+
Bundler::GemHelper.install_tasks
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//= link_directory ../stylesheets/queue_it .css
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
|
11
|
+
* It is generally better to create a new file per style scope.
|
|
12
|
+
*
|
|
13
|
+
*= require_tree .
|
|
14
|
+
*= require_self
|
|
15
|
+
*/
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module QueueIt::Queable
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
has_one :queue,
|
|
6
|
+
as: :queable, inverse_of: :queable, dependent: :destroy, class_name: 'QueueIt::Queue'
|
|
7
|
+
|
|
8
|
+
def find_or_create_queue!
|
|
9
|
+
QueueIt::Queue.find_or_create_by!(queable: self)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def push_to_queue(nodable, in_head = true)
|
|
13
|
+
if local_queue.empty?
|
|
14
|
+
local_queue.push_node_when_queue_length_is_zero(nodable)
|
|
15
|
+
elsif local_queue.one_node?
|
|
16
|
+
local_queue.push_node_when_queue_length_is_one(nodable, in_head)
|
|
17
|
+
else
|
|
18
|
+
in_head ? local_queue.push_in_head(nodable) : local_queue.push_in_tail(nodable)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get_next_in_queue
|
|
23
|
+
get_next_node_in_queue&.nodable
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def get_next_node_in_queue
|
|
27
|
+
return if local_queue.empty?
|
|
28
|
+
|
|
29
|
+
if local_queue.one_node?
|
|
30
|
+
local_queue.head_node
|
|
31
|
+
elsif local_queue.two_nodes?
|
|
32
|
+
local_queue.get_next_in_queue_with_length_two
|
|
33
|
+
else
|
|
34
|
+
local_queue.get_next_in_queue_generic
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def formatted_queue(nodable_attribute)
|
|
39
|
+
return if local_queue.empty?
|
|
40
|
+
|
|
41
|
+
if local_queue.one_node?
|
|
42
|
+
[local_queue.head_node.nodable.send(nodable_attribute)]
|
|
43
|
+
elsif local_queue.two_nodes?
|
|
44
|
+
[local_queue.head_node.nodable.send(nodable_attribute),
|
|
45
|
+
local_queue.tail_node.nodable.send(nodable_attribute)]
|
|
46
|
+
else
|
|
47
|
+
current_node = local_queue.head_node
|
|
48
|
+
array = []
|
|
49
|
+
while !current_node.nil?
|
|
50
|
+
array.push(current_node.nodable.send(nodable_attribute))
|
|
51
|
+
current_node = current_node.child_node
|
|
52
|
+
end
|
|
53
|
+
array
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def delete_queue_nodes
|
|
58
|
+
queue.nodes.delete_all
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def remove_from_queue(nodable)
|
|
62
|
+
return if local_queue.empty? || local_queue.nodes.where(nodable: nodable).empty?
|
|
63
|
+
|
|
64
|
+
ActiveRecord::Base.transaction do
|
|
65
|
+
local_queue.lock!
|
|
66
|
+
local_queue.nodes.where(nodable: nodable).find_each do |node|
|
|
67
|
+
remove_node(node)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def local_queue
|
|
75
|
+
@local_queue ||= find_or_create_queue!
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def remove_node(node)
|
|
79
|
+
node.reload
|
|
80
|
+
previous_node = node.parent_node
|
|
81
|
+
child_node = node.child_node
|
|
82
|
+
node_kind = node.kind
|
|
83
|
+
node.destroy
|
|
84
|
+
new_child_node_kind = node_kind == 'head' ? node_kind : child_node&.kind
|
|
85
|
+
child_node&.update!(parent_node: previous_node, kind: new_child_node_kind)
|
|
86
|
+
previous_node&.update!(kind: node_kind) if node_kind == 'tail' && previous_node&.any?
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module QueueIt
|
|
2
|
+
class Node < ApplicationRecord
|
|
3
|
+
belongs_to :nodable, polymorphic: true
|
|
4
|
+
belongs_to :queue, optional: true, counter_cache: :count_of_nodes
|
|
5
|
+
belongs_to :parent_node, class_name: 'QueueIt::Node', optional: true
|
|
6
|
+
has_one :child_node,
|
|
7
|
+
class_name: 'QueueIt::Node',
|
|
8
|
+
dependent: :nullify,
|
|
9
|
+
foreign_key: 'parent_node_id',
|
|
10
|
+
inverse_of: :parent_node
|
|
11
|
+
|
|
12
|
+
enum kind: { head: 0, any: 1, tail: 2 }
|
|
13
|
+
|
|
14
|
+
validate :only_one_head, :only_one_tail
|
|
15
|
+
|
|
16
|
+
def only_one_head
|
|
17
|
+
if repeated_kind?('head')
|
|
18
|
+
errors.add(:kind, 'There can not be more than 1 head node in each queue')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def only_one_tail
|
|
23
|
+
if repeated_kind?('tail')
|
|
24
|
+
errors.add(:kind, 'There can not be more than 1 tail node in each queue')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def head?
|
|
29
|
+
kind == 'head'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def tail?
|
|
33
|
+
kind == 'tail'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def any?
|
|
37
|
+
kind == 'any'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def repeated_kind?(kind)
|
|
43
|
+
changes['kind'].present? && (changes['kind'].first == 'any' ||
|
|
44
|
+
changes['kind'].first.nil?) && changes['kind'].last == kind &&
|
|
45
|
+
queue.nodes.find_by(kind: kind).present?
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# == Schema Information
|
|
51
|
+
#
|
|
52
|
+
# Table name: queue_it_nodes
|
|
53
|
+
#
|
|
54
|
+
# id :bigint(8) not null, primary key
|
|
55
|
+
# nodable_type :string
|
|
56
|
+
# nodable_id :bigint(8)
|
|
57
|
+
# queue_id :bigint(8)
|
|
58
|
+
# parent_node_id :bigint(8)
|
|
59
|
+
# kind :integer
|
|
60
|
+
# created_at :datetime not null
|
|
61
|
+
# updated_at :datetime not null
|
|
62
|
+
#
|
|
63
|
+
# Indexes
|
|
64
|
+
#
|
|
65
|
+
# index_queue_it_nodes_on_nodable (nodable_type,nodable_id)
|
|
66
|
+
# index_queue_it_nodes_on_parent_node_id (parent_node_id)
|
|
67
|
+
# index_queue_it_nodes_on_queue_id (queue_id)
|
|
68
|
+
#
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
module QueueIt
|
|
2
|
+
class Queue < ApplicationRecord
|
|
3
|
+
belongs_to :queable, polymorphic: true
|
|
4
|
+
has_many :nodes, dependent: :destroy
|
|
5
|
+
|
|
6
|
+
def head_node
|
|
7
|
+
nodes.find_by(kind: :head)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def tail_node
|
|
11
|
+
nodes.find_by(kind: :tail)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def size
|
|
15
|
+
nodes.size
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def empty?
|
|
19
|
+
size.zero?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def one_node?
|
|
23
|
+
size == 1
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def two_nodes?
|
|
27
|
+
size == 2
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def get_next_in_queue_with_length_two
|
|
31
|
+
next_node = ActiveRecord::Base.transaction do
|
|
32
|
+
lock!
|
|
33
|
+
old_head_node = head_node.lock!
|
|
34
|
+
old_tail_node = tail_node.lock!
|
|
35
|
+
nodes.where.not(kind: :any).each { |node| node.update!(kind: :any) }
|
|
36
|
+
old_head_node.update!(kind: :tail, parent_node: old_tail_node)
|
|
37
|
+
old_tail_node.update!(kind: :head, parent_node: nil)
|
|
38
|
+
old_head_node
|
|
39
|
+
end
|
|
40
|
+
next_node
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get_next_in_queue_generic
|
|
44
|
+
next_node = ActiveRecord::Base.transaction do
|
|
45
|
+
lock!
|
|
46
|
+
old_head_node = head_node.lock!
|
|
47
|
+
old_second_node = old_head_node.child_node.lock!
|
|
48
|
+
old_tail_node = tail_node.lock!
|
|
49
|
+
nodes.where.not(kind: :any).find_each { |node| node.update!(kind: :any) }
|
|
50
|
+
old_head_node.update!(kind: :tail, parent_node: old_tail_node)
|
|
51
|
+
old_second_node.update!(kind: :head, parent_node: nil)
|
|
52
|
+
old_head_node
|
|
53
|
+
end
|
|
54
|
+
next_node
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def push_node_when_queue_length_is_zero(nodable)
|
|
58
|
+
ActiveRecord::Base.transaction do
|
|
59
|
+
lock!
|
|
60
|
+
nodes.create!(nodable: nodable, kind: :head)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def push_node_when_queue_length_is_one(nodable, in_head)
|
|
65
|
+
if in_head
|
|
66
|
+
push_in_head(nodable)
|
|
67
|
+
else
|
|
68
|
+
ActiveRecord::Base.transaction do
|
|
69
|
+
lock!
|
|
70
|
+
nodes.create!(nodable: nodable, kind: :tail, parent_node: head_node)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def push_in_head(nodable)
|
|
76
|
+
ActiveRecord::Base.transaction do
|
|
77
|
+
lock!
|
|
78
|
+
old_head_node = head_node.lock!
|
|
79
|
+
kind = one_node? ? :tail : :any
|
|
80
|
+
old_head_node.update!(kind: kind)
|
|
81
|
+
new_head_node = nodes.create!(nodable: nodable, kind: :head)
|
|
82
|
+
old_head_node.update!(parent_node: new_head_node)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def push_in_tail(nodable)
|
|
87
|
+
ActiveRecord::Base.transaction do
|
|
88
|
+
lock!
|
|
89
|
+
old_tail_node = tail_node.lock!
|
|
90
|
+
old_tail_node.update!(kind: :any)
|
|
91
|
+
nodes.create!(nodable: nodable, kind: :tail, parent_node: old_tail_node)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# == Schema Information
|
|
98
|
+
#
|
|
99
|
+
# Table name: queue_it_queues
|
|
100
|
+
#
|
|
101
|
+
# id :bigint(8) not null, primary key
|
|
102
|
+
# queable_type :string
|
|
103
|
+
# queable_id :bigint(8)
|
|
104
|
+
# created_at :datetime not null
|
|
105
|
+
# updated_at :datetime not null
|
|
106
|
+
# count_of_nodes :bigint(8) default(0)
|
|
107
|
+
#
|
|
108
|
+
# Indexes
|
|
109
|
+
#
|
|
110
|
+
# index_queue_it_queues_on_queable (queable_type,queable_id)
|
|
111
|
+
#
|
data/bin/rails
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
|
3
|
+
# installed from the root of your application.
|
|
4
|
+
|
|
5
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
|
6
|
+
ENGINE_PATH = File.expand_path('../lib/queue_it/engine', __dir__)
|
|
7
|
+
APP_PATH = File.expand_path('../spec/dummy/config/application', __dir__)
|
|
8
|
+
|
|
9
|
+
# Set up gems listed in the Gemfile.
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
|
11
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
|
12
|
+
|
|
13
|
+
require "rails"
|
|
14
|
+
# Pick the frameworks you want:
|
|
15
|
+
require "active_model/railtie"
|
|
16
|
+
require "active_job/railtie"
|
|
17
|
+
require "active_record/railtie"
|
|
18
|
+
require "active_storage/engine"
|
|
19
|
+
require "action_controller/railtie"
|
|
20
|
+
require "action_mailer/railtie"
|
|
21
|
+
require "action_view/railtie"
|
|
22
|
+
require "action_cable/engine"
|
|
23
|
+
require "sprockets/railtie"
|
|
24
|
+
# require "rails/test_unit/railtie"
|
|
25
|
+
require 'rails/engine/commands'
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class QueueIt::InstallGenerator < Rails::Generators::Base
|
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
3
|
+
|
|
4
|
+
def create_initializer
|
|
5
|
+
template "initializer.rb", "config/initializers/queue_it.rb"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def mount_routes
|
|
9
|
+
line = "Rails.application.routes.draw do\n"
|
|
10
|
+
inject_into_file "config/routes.rb", after: line do <<-"HERE".gsub(/^ {4}/, '')
|
|
11
|
+
mount QueueIt::Engine => "/queue_it"
|
|
12
|
+
HERE
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def copy_engine_migrations
|
|
17
|
+
rake "queue_it:install:migrations"
|
|
18
|
+
end
|
|
19
|
+
end
|