rails-queue-it 0.1.0

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