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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +116 -0
  3. data/.circleci/setup-rubygems.sh +3 -0
  4. data/.editorconfig +24 -0
  5. data/.gitignore +9 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +503 -0
  8. data/.ruby-version +1 -0
  9. data/CHANGELOG.md +7 -0
  10. data/Gemfile +15 -0
  11. data/Gemfile.lock +277 -0
  12. data/Guardfile +12 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +202 -0
  15. data/Rakefile +10 -0
  16. data/app/assets/config/queue_it_manifest.js +1 -0
  17. data/app/assets/images/queue_it/.keep +0 -0
  18. data/app/assets/stylesheets/queue_it/application.css +15 -0
  19. data/app/controllers/queue_it/application_controller.rb +5 -0
  20. data/app/helpers/queue_it/application_helper.rb +4 -0
  21. data/app/jobs/queue_it/application_job.rb +4 -0
  22. data/app/mailers/queue_it/application_mailer.rb +6 -0
  23. data/app/models/concerns/queue_it/queable.rb +89 -0
  24. data/app/models/queue_it/application_record.rb +5 -0
  25. data/app/models/queue_it/node.rb +68 -0
  26. data/app/models/queue_it/queue.rb +111 -0
  27. data/app/views/layouts/queue_it/application.html.erb +15 -0
  28. data/bin/rails +25 -0
  29. data/config/routes.rb +2 -0
  30. data/db/migrate/20210723140008_create_queue_it_queues.rb +10 -0
  31. data/db/migrate/20210723140434_create_queue_it_nodes.rb +12 -0
  32. data/lib/generators/queue_it/install/USAGE +5 -0
  33. data/lib/generators/queue_it/install/install_generator.rb +19 -0
  34. data/lib/generators/queue_it/install/templates/initializer.rb +2 -0
  35. data/lib/queue_it.rb +25 -0
  36. data/lib/queue_it/engine.rb +15 -0
  37. data/lib/queue_it/example_class.rb +7 -0
  38. data/lib/queue_it/version.rb +3 -0
  39. data/lib/tasks/auto_annotate_models.rake +67 -0
  40. data/lib/tasks/queue_it_tasks.rake +4 -0
  41. data/queue_it.gemspec +38 -0
  42. data/spec/concerns/queue_it/queable_spec.rb +492 -0
  43. data/spec/dummy/Rakefile +6 -0
  44. data/spec/dummy/app/assets/config/manifest.js +3 -0
  45. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  46. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  47. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  48. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  49. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  50. data/spec/dummy/app/javascript/packs/application.js +15 -0
  51. data/spec/dummy/app/jobs/application_job.rb +7 -0
  52. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  53. data/spec/dummy/app/models/application_record.rb +3 -0
  54. data/spec/dummy/app/models/task.rb +3 -0
  55. data/spec/dummy/app/models/user.rb +2 -0
  56. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  57. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  58. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  59. data/spec/dummy/bin/rails +4 -0
  60. data/spec/dummy/bin/rake +4 -0
  61. data/spec/dummy/bin/setup +33 -0
  62. data/spec/dummy/config.ru +5 -0
  63. data/spec/dummy/config/application.rb +30 -0
  64. data/spec/dummy/config/boot.rb +5 -0
  65. data/spec/dummy/config/cable.yml +10 -0
  66. data/spec/dummy/config/database.yml +27 -0
  67. data/spec/dummy/config/environment.rb +5 -0
  68. data/spec/dummy/config/environments/development.rb +81 -0
  69. data/spec/dummy/config/environments/production.rb +112 -0
  70. data/spec/dummy/config/environments/test.rb +49 -0
  71. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  72. data/spec/dummy/config/initializers/assets.rb +12 -0
  73. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  74. data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
  75. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  76. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  77. data/spec/dummy/config/initializers/inflections.rb +16 -0
  78. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  79. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  80. data/spec/dummy/config/locales/en.yml +33 -0
  81. data/spec/dummy/config/puma.rb +38 -0
  82. data/spec/dummy/config/routes.rb +3 -0
  83. data/spec/dummy/config/spring.rb +6 -0
  84. data/spec/dummy/config/storage.yml +34 -0
  85. data/spec/dummy/db/migrate/20210723142357_create_users.rb +9 -0
  86. data/spec/dummy/db/migrate/20210723172002_create_tasks.rb +9 -0
  87. data/spec/dummy/db/schema.rb +52 -0
  88. data/spec/dummy/public/404.html +67 -0
  89. data/spec/dummy/public/422.html +67 -0
  90. data/spec/dummy/public/500.html +66 -0
  91. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  92. data/spec/dummy/public/apple-touch-icon.png +0 -0
  93. data/spec/dummy/public/favicon.ico +0 -0
  94. data/spec/dummy/spec/models/task_spec.rb +7 -0
  95. data/spec/dummy/spec/models/user_spec.rb +7 -0
  96. data/spec/factories/queue_it/nodes.rb +18 -0
  97. data/spec/factories/queue_it/queues.rb +26 -0
  98. data/spec/factories/tasks.rb +11 -0
  99. data/spec/factories/users.rb +5 -0
  100. data/spec/fixtures/files/image.png +0 -0
  101. data/spec/fixtures/files/video.mp4 +0 -0
  102. data/spec/models/queue_it/node_spec.rb +43 -0
  103. data/spec/models/queue_it/queue_spec.rb +116 -0
  104. data/spec/queue_it_spec.rb +16 -0
  105. data/spec/rails_helper.rb +52 -0
  106. data/spec/spec_helper.rb +9 -0
  107. data/spec/support/test_helpers.rb +5 -0
  108. 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,5 @@
1
+ module QueueIt
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module QueueIt
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module QueueIt
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module QueueIt
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -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,5 @@
1
+ module QueueIt
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ 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
+ #
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Queue it</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "queue_it/application", media: "all" %>
9
+ </head>
10
+ <body>
11
+
12
+ <%= yield %>
13
+
14
+ </body>
15
+ </html>
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,2 @@
1
+ QueueIt::Engine.routes.draw do
2
+ end
@@ -0,0 +1,10 @@
1
+ class CreateQueueItQueues < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :queue_it_queues do |t|
4
+ t.references :queable, polymorphic: true
5
+ t.bigint :count_of_nodes, default: 0
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ class CreateQueueItNodes < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :queue_it_nodes do |t|
4
+ t.references :nodable, polymorphic: true
5
+ t.references :queue
6
+ t.references :parent_node
7
+ t.integer :kind
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Install the engine in host app.
3
+
4
+ Example:
5
+ rails generate queue_it:install
@@ -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
@@ -0,0 +1,2 @@
1
+ QueueIt.setup do |config|
2
+ end