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