biovision 0.0.200518.1

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 (170) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +214 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/config/biovision_manifest.js +1 -0
  6. data/app/assets/images/biovision/components/carousel/arrow-left.svg +4 -0
  7. data/app/assets/images/biovision/components/carousel/arrow-right.svg +4 -0
  8. data/app/assets/images/biovision/icons/breadcrumb-hover.svg +1 -0
  9. data/app/assets/images/biovision/icons/breadcrumb.svg +1 -0
  10. data/app/assets/images/biovision/icons/components/contact.svg +26 -0
  11. data/app/assets/images/biovision/icons/components/content.svg +20 -0
  12. data/app/assets/images/biovision/icons/components/users.svg +21 -0
  13. data/app/assets/images/biovision/icons/key.svg +16 -0
  14. data/app/assets/images/biovision/icons/log_in.svg +15 -0
  15. data/app/assets/images/biovision/icons/log_out.svg +15 -0
  16. data/app/assets/images/biovision/icons/messages/error.svg +14 -0
  17. data/app/assets/images/biovision/icons/messages/info.svg +8 -0
  18. data/app/assets/images/biovision/icons/messages/success.svg +12 -0
  19. data/app/assets/images/biovision/icons/messages/warning.svg +13 -0
  20. data/app/assets/images/biovision/icons/settings.svg +41 -0
  21. data/app/assets/images/biovision/placeholders/16x9.svg +11 -0
  22. data/app/assets/images/biovision/placeholders/1x1.svg +11 -0
  23. data/app/assets/images/biovision/placeholders/3x2.svg +11 -0
  24. data/app/assets/images/biovision/placeholders/user.svg +15 -0
  25. data/app/assets/stylesheets/biovision/admin.scss +6 -0
  26. data/app/assets/stylesheets/biovision/admin/components.scss +59 -0
  27. data/app/assets/stylesheets/biovision/admin/default.scss +57 -0
  28. data/app/assets/stylesheets/biovision/admin/layout.scss +95 -0
  29. data/app/assets/stylesheets/biovision/admin/vars.scss +56 -0
  30. data/app/assets/stylesheets/biovision/biovision.scss +134 -0
  31. data/app/assets/stylesheets/biovision/biovision/messages.scss +41 -0
  32. data/app/assets/stylesheets/biovision/components/carousel.scss +53 -0
  33. data/app/assets/stylesheets/biovision/components/forms.scss +85 -0
  34. data/app/assets/stylesheets/biovision/components/hamburger.scss +56 -0
  35. data/app/assets/stylesheets/biovision/components/lists.scss +91 -0
  36. data/app/assets/stylesheets/biovision/default.scss +57 -0
  37. data/app/assets/stylesheets/biovision/themes/default_theme.scss +2 -0
  38. data/app/assets/stylesheets/biovision/themes/default_theme/components.scss +3 -0
  39. data/app/assets/stylesheets/biovision/themes/default_theme/layout.scss +44 -0
  40. data/app/assets/stylesheets/biovision/themes/default_theme/layout/footer.scss +0 -0
  41. data/app/assets/stylesheets/biovision/themes/default_theme/layout/header.scss +39 -0
  42. data/app/assets/stylesheets/biovision/vars.scss +53 -0
  43. data/app/controllers/admin/agents_controller.rb +15 -0
  44. data/app/controllers/admin/components_controller.rb +174 -0
  45. data/app/controllers/admin/index_controller.rb +11 -0
  46. data/app/controllers/admin/ip_addresses_controller.rb +15 -0
  47. data/app/controllers/admin_controller.rb +14 -0
  48. data/app/controllers/authentication_controller.rb +79 -0
  49. data/app/controllers/concerns/authentication.rb +45 -0
  50. data/app/controllers/index_controller.rb +8 -0
  51. data/app/controllers/my/confirmations_controller.rb +2 -0
  52. data/app/controllers/my/index_controller.rb +8 -0
  53. data/app/controllers/my/profiles_controller.rb +128 -0
  54. data/app/controllers/my/recoveries_controller.rb +2 -0
  55. data/app/helpers/biovision_components_helper.rb +14 -0
  56. data/app/helpers/users_helper.rb +11 -0
  57. data/app/lib/biovision/components/base_component.rb +177 -0
  58. data/app/lib/biovision/components/component_settings.rb +30 -0
  59. data/app/lib/biovision/components/privilege_handler.rb +77 -0
  60. data/app/lib/biovision/components/track_component.rb +9 -0
  61. data/app/lib/biovision/components/users/authentication.rb +47 -0
  62. data/app/lib/biovision/components/users/code_handler.rb +23 -0
  63. data/app/lib/biovision/components/users/profile_handler.rb +12 -0
  64. data/app/lib/biovision/components/users/registration_handler.rb +99 -0
  65. data/app/lib/biovision/components/users_component.rb +47 -0
  66. data/app/lib/biovision/notifiers/base_notifier.rb +69 -0
  67. data/app/lib/biovision/notifiers/socialization_notifier.rb +31 -0
  68. data/app/lib/biovision/notifiers/users_notifier.rb +15 -0
  69. data/app/models/agent.rb +35 -0
  70. data/app/models/biovision_component.rb +45 -0
  71. data/app/models/biovision_component_user.rb +21 -0
  72. data/app/models/browser.rb +34 -0
  73. data/app/models/code.rb +71 -0
  74. data/app/models/concerns/checkable.rb +22 -0
  75. data/app/models/concerns/flat_priority.rb +50 -0
  76. data/app/models/concerns/has_language.rb +10 -0
  77. data/app/models/concerns/has_owner.rb +22 -0
  78. data/app/models/concerns/has_simple_image.rb +18 -0
  79. data/app/models/concerns/has_track.rb +10 -0
  80. data/app/models/concerns/has_uuid.rb +12 -0
  81. data/app/models/concerns/meta_texts.rb +16 -0
  82. data/app/models/concerns/nested_priority.rb +58 -0
  83. data/app/models/concerns/required_unique_name.rb +16 -0
  84. data/app/models/concerns/required_unique_slug.rb +15 -0
  85. data/app/models/concerns/toggleable.rb +33 -0
  86. data/app/models/foreign_site.rb +34 -0
  87. data/app/models/foreign_user.rb +21 -0
  88. data/app/models/ip_address.rb +31 -0
  89. data/app/models/language.rb +31 -0
  90. data/app/models/login_attempt.rb +35 -0
  91. data/app/models/metric.rb +48 -0
  92. data/app/models/metric_value.rb +11 -0
  93. data/app/models/notification.rb +37 -0
  94. data/app/models/simple_image.rb +47 -0
  95. data/app/models/simple_image_tag.rb +30 -0
  96. data/app/models/simple_image_tag_image.rb +13 -0
  97. data/app/models/token.rb +96 -0
  98. data/app/models/user.rb +136 -0
  99. data/app/models/user_language.rb +15 -0
  100. data/app/uploaders/simple_image_uploader.rb +96 -0
  101. data/app/uploaders/user_image_uploader.rb +58 -0
  102. data/app/views/admin/agents/_nav_item.html.erb +6 -0
  103. data/app/views/admin/agents/entity/_in_list.html.erb +6 -0
  104. data/app/views/admin/agents/index.html.erb +13 -0
  105. data/app/views/admin/components/_list.html.erb +12 -0
  106. data/app/views/admin/components/entity/_links.html.erb +35 -0
  107. data/app/views/admin/components/entity/_section.html.erb +12 -0
  108. data/app/views/admin/components/index.html.erb +12 -0
  109. data/app/views/admin/components/links/_track.html.erb +2 -0
  110. data/app/views/admin/components/links/_users.html.erb +11 -0
  111. data/app/views/admin/components/privileges.html.erb +20 -0
  112. data/app/views/admin/components/privileges/_component_user.html.erb +17 -0
  113. data/app/views/admin/components/privileges/_links.html.erb +35 -0
  114. data/app/views/admin/components/privileges/_privilege_flag.html.erb +28 -0
  115. data/app/views/admin/components/privileges/_users.html.erb +23 -0
  116. data/app/views/admin/components/settings.html.erb +39 -0
  117. data/app/views/admin/components/settings/_new_parameter.html.erb +46 -0
  118. data/app/views/admin/components/settings/_parameters.html.erb +20 -0
  119. data/app/views/admin/components/settings/_setting.html.erb +18 -0
  120. data/app/views/admin/components/settings/_settings.html.erb +31 -0
  121. data/app/views/admin/components/show.html.erb +14 -0
  122. data/app/views/admin/index/index.html.erb +14 -0
  123. data/app/views/admin/ip_addresses/_nav_item.html.erb +6 -0
  124. data/app/views/admin/ip_addresses/entity/_in_list.html.erb +6 -0
  125. data/app/views/admin/ip_addresses/index.html.erb +13 -0
  126. data/app/views/admin/unauthorized.html.erb +19 -0
  127. data/app/views/authentication/_form.html.erb +40 -0
  128. data/app/views/authentication/failed.js.erb +3 -0
  129. data/app/views/authentication/new.html.erb +21 -0
  130. data/app/views/index/index.html.erb +0 -0
  131. data/app/views/layouts/admin.html.erb +23 -0
  132. data/app/views/layouts/admin/_breadcrumbs.html.erb +8 -0
  133. data/app/views/layouts/admin/_footer.html.erb +10 -0
  134. data/app/views/layouts/admin/_header.html.erb +18 -0
  135. data/app/views/layouts/admin/header/_logo.html.erb +3 -0
  136. data/app/views/layouts/application/_footer.html.erb +8 -0
  137. data/app/views/layouts/application/_header.html.erb +9 -0
  138. data/app/views/layouts/application/header/_authentication.html.erb +13 -0
  139. data/app/views/my/index/index.html.erb +0 -0
  140. data/app/views/my/profiles/closed.html.erb +12 -0
  141. data/app/views/my/profiles/new.html.erb +17 -0
  142. data/app/views/my/profiles/new/_form.html.erb +147 -0
  143. data/app/views/shared/_breadcrumbs.html.erb +8 -0
  144. data/app/views/shared/_cookie_notification.html.erb +4 -0
  145. data/app/views/shared/_flash_messages.html.erb +7 -0
  146. data/app/views/shared/_meta_texts.html.erb +31 -0
  147. data/app/views/shared/_nothing_found.html.erb +1 -0
  148. data/app/views/shared/_pagination.jbuilder +15 -0
  149. data/app/views/shared/admin/_list.html.erb +19 -0
  150. data/app/views/shared/admin/_list_with_priority.html.erb +19 -0
  151. data/app/views/shared/admin/_toggleable.html.erb +8 -0
  152. data/app/views/shared/entity/_list_of_errors.html.erb +7 -0
  153. data/app/views/shared/forms/errors.js.erb +5 -0
  154. data/config/initializers/carrierwave.rb +27 -0
  155. data/config/initializers/pluralization.rb +2 -0
  156. data/config/locales/biovision-ru.yml +90 -0
  157. data/config/locales/components-ru.yml +81 -0
  158. data/config/locales/track-ru.yml +40 -0
  159. data/config/locales/users-ru.yml +130 -0
  160. data/config/routes.rb +75 -0
  161. data/db/migrate/20191228000000_create_biovision_components.rb +72 -0
  162. data/db/migrate/20200224000000_create_track_component.rb +59 -0
  163. data/db/migrate/20200224000010_create_users_component.rb +189 -0
  164. data/db/migrate/20200404000000_create_simple_images.rb +54 -0
  165. data/lib/biovision.rb +9 -0
  166. data/lib/biovision/base_methods.rb +167 -0
  167. data/lib/biovision/engine.rb +40 -0
  168. data/lib/biovision/version.rb +5 -0
  169. data/lib/tasks/biovision_tasks.rake +4 -0
  170. metadata +392 -0
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Biovision
4
+ module Components
5
+ # Handling users
6
+ class UsersComponent < BaseComponent
7
+ include Users::Authentication
8
+
9
+ METRIC_NEW_USER = 'users.new_user.hit'
10
+
11
+ def self.settings_flags
12
+ %w[
13
+ registration_open email_as_login confirm_email require_email
14
+ invite_only use_invites
15
+ ]
16
+ end
17
+
18
+ def self.settings_numbers
19
+ %w[invite_count bounce_count bounce_timeout]
20
+ end
21
+
22
+ # @param [Hash] parameters
23
+ # @param [Code] code
24
+ def register_user(parameters, code)
25
+ handler = Users::RegistrationHandler.new(self)
26
+ handler.handle(parameters, code)
27
+ end
28
+
29
+ # @param [User] entity
30
+ def valid?(entity)
31
+ entity.valid? && valid_slug?(entity)
32
+ end
33
+
34
+ def valid_slug?(entity)
35
+ if settings['email_as_login']
36
+ entity.slug =~ /\A[-_a-z0-9.]+@[-a-z0-9.]+\.[a-z]{2,24}\z/
37
+ elsif entity.slug =~ /\A[_a-z0-9]{1,30}\z/
38
+ true
39
+ else
40
+ key = 'activerecord.errors.models.user.attributes.slug.invalid'
41
+ entity.errors[:slug] = t(key)
42
+ false
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Biovision
4
+ module Notifiers
5
+ # Base notifier class for dispatching component notifications
6
+ class BaseNotifier
7
+ attr_accessor :notification, :user
8
+
9
+ # @param [User] user
10
+ def initialize(user)
11
+ @user = user
12
+ end
13
+
14
+ # @param [Notification] notification
15
+ def self.[](notification)
16
+ slug = notification.component_slug
17
+ instance = handler_class(slug).new(notification.user)
18
+ instance.notification = notification
19
+ instance
20
+ end
21
+
22
+ def self.slug
23
+ to_s.demodulize.underscore.gsub('_notifier', '')
24
+ end
25
+
26
+ # @param [String] slug
27
+ def self.handler_class(slug)
28
+ handler_name = "biovision/notifiers/#{slug}_notifier".classify
29
+ handler_name.safe_constantize || BaseNotifier
30
+ end
31
+
32
+ def component
33
+ @component ||= BiovisionComponent[self.class.slug]
34
+ end
35
+
36
+ def entity
37
+ nil
38
+ end
39
+
40
+ def view
41
+ return if notification.nil?
42
+
43
+ "my/notifications/#{self.class.slug}/#{notification.data['type']}"
44
+ end
45
+
46
+ # @param [Hash] data
47
+ def notify(data)
48
+ attributes = { user: user, biovision_component: component, data: data }
49
+ Notification.create(attributes)
50
+ end
51
+
52
+ protected
53
+
54
+ # @param [Integer] id
55
+ # @param [String] type
56
+ def check_and_notify(id, type)
57
+ return if check_chain(id, type).exists?
58
+
59
+ notify({id: id, type: type})
60
+ end
61
+
62
+ # @param [Integer] id
63
+ # @param [String] type
64
+ def check_chain(id, type)
65
+ Notification.owned_by(user).unread.data_id(id).data_type(type)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Biovision
4
+ module Notifiers
5
+ # Notification mapper for socialization component
6
+ class SocializationNotifier < BaseNotifier
7
+ TYPE_FOLLOWER = 'follower'
8
+ TYPE_MESSAGE = 'message'
9
+
10
+ def entity
11
+ data = notification&.data.to_h
12
+ case data['type']
13
+ when TYPE_MESSAGE, TYPE_FOLLOWER
14
+ User.find_by(id: data['id'])
15
+ else
16
+ nil
17
+ end
18
+ end
19
+
20
+ # @param [Integer] sender_id
21
+ def new_message(sender_id)
22
+ check_and_notify(sender_id, TYPE_MESSAGE)
23
+ end
24
+
25
+ # @param [Integer] follower_id
26
+ def new_follower(follower_id)
27
+ check_and_notify(follower_id, TYPE_FOLLOWER)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Biovision
4
+ module Notifiers
5
+ # Notification mapper for socialization component
6
+ class UsersNotifier < BaseNotifier
7
+ TYPE_LOGIN_ATTEMPT = 'login_attempt'
8
+
9
+ # @param [Hash] track
10
+ def new_login_attempt(track)
11
+ check_and_notify(track[:ip_address]&.id, TYPE_LOGIN_ATTEMPT)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # User agent
4
+ #
5
+ # Attributes:
6
+ # banned [boolean]
7
+ # browser_id [Browser], optional
8
+ # created_at [DateTime]
9
+ # object_count [integer]
10
+ # name [string]
11
+ # updated_at [DateTime]
12
+ class Agent < ApplicationRecord
13
+ include RequiredUniqueName
14
+ include Toggleable
15
+
16
+ toggleable :banned
17
+
18
+ belongs_to :browser, optional: true, counter_cache: true
19
+
20
+ before_validation { self.name = name.to_s[0..254] }
21
+
22
+ scope :list_for_administration, -> { ordered_by_name }
23
+
24
+ # @param [Integer] page
25
+ def self.page_for_administration(page = 1)
26
+ list_for_administration.page(page)
27
+ end
28
+
29
+ # @param [String] name
30
+ def self.[](name)
31
+ return if name.blank?
32
+
33
+ find_or_create_by(name: name[0..254])
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Biovision component entity, settings and parameters
4
+ #
5
+ # Attributes:
6
+ # active [Boolean]
7
+ # created_at [DateTime]
8
+ # parameters [JSON]
9
+ # priority [Integer]
10
+ # settings [JSON]
11
+ # slug [String]
12
+ # updated_at [DateTime]
13
+ class BiovisionComponent < ApplicationRecord
14
+ include FlatPriority
15
+ include RequiredUniqueSlug
16
+
17
+ has_many :biovision_component_users, dependent: :delete_all
18
+ has_many :simple_images, dependent: :destroy
19
+
20
+ scope :list_for_administration, -> { ordered_by_priority }
21
+
22
+ # Find component by slug
23
+ #
24
+ # @param [String] slug
25
+ def self.[](slug)
26
+ find_by(slug: slug)
27
+ end
28
+
29
+ # @param [String] slug
30
+ # @param [String] default_value
31
+ def get(slug, default_value = '')
32
+ parameters.fetch(slug.to_s) { default_value }
33
+ end
34
+
35
+ # @param [String] slug
36
+ # @param value
37
+ def []=(slug, value)
38
+ parameters[slug.to_s] = value
39
+ save!
40
+ end
41
+
42
+ def privileges
43
+ biovision_component_users.recent
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # User data in biovision component
4
+ #
5
+ # Attributes:
6
+ # administrator [boolean]
7
+ # biovision_component_id [BiovisionComponent]
8
+ # created_at [DateTime]
9
+ # data [jsonb]
10
+ # updated_at [DateTime]
11
+ # user_id [User]
12
+ class BiovisionComponentUser < ApplicationRecord
13
+ include HasOwner
14
+
15
+ belongs_to :biovision_component
16
+ belongs_to :user
17
+
18
+ validates_uniqueness_of :user_id, scope: :biovision_component_id
19
+
20
+ scope :recent, -> { order('updated_at desc') }
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Browser
4
+ #
5
+ # Attributes:
6
+ # agents_count [integer]
7
+ # banned [boolean]
8
+ # created_at [DateTime]
9
+ # mobile [boolean]
10
+ # name [string]
11
+ # updated_at [DateTime]
12
+ # version [string]
13
+ class Browser < ApplicationRecord
14
+ include Checkable
15
+ include Toggleable
16
+
17
+ NAME_LIMIT = 50
18
+ VERSION_LIMIT = 10
19
+
20
+ toggleable :banned
21
+
22
+ has_many :agents, dependent: :nullify
23
+
24
+ validates_presence_of :name, :version
25
+ validates_uniqueness_of :version, scope: :name
26
+ validates_length_of :name, maximum: NAME_LIMIT
27
+ validates_length_of :version, maximum: VERSION_LIMIT
28
+
29
+ scope :list_for_administration, -> { order('name asc, version asc') }
30
+
31
+ def self.entity_parameters
32
+ %i[banned mobile name version]
33
+ end
34
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Code for users
4
+ #
5
+ # Attributes:
6
+ # agent_id [Agent], optional
7
+ # biovision_component_id [BiovisionComponent]
8
+ # body [string]
9
+ # created_at [DateTime]
10
+ # data [jsonb]
11
+ # ip_address_id [IpAddress], optional
12
+ # quantity [integer]
13
+ # updated_at [DateTime]
14
+ # user_id [User]
15
+ class Code < ApplicationRecord
16
+ include HasOwner
17
+ include HasTrack
18
+
19
+ BODY_LIMIT = 50
20
+ QUANTITY_RANGE = (0..32_767).freeze
21
+
22
+ belongs_to :biovision_component
23
+ belongs_to :user
24
+
25
+ after_initialize :generate_body
26
+
27
+ before_validation :sanitize_quantity
28
+
29
+ validates_presence_of :body
30
+ validates_uniqueness_of :body
31
+ validates_length_of :body, maximum: BODY_LIMIT
32
+
33
+ scope :recent, -> { order('id desc') }
34
+ scope :active, -> { where('quantity > 0') }
35
+ scope :list_for_administration, -> { recent }
36
+
37
+ # @param [Integer] page
38
+ def self.page_for_administration(page = 1)
39
+ list_for_administration.page(page)
40
+ end
41
+
42
+ def self.entity_parameters
43
+ %i[body payload quantity]
44
+ end
45
+
46
+ def self.creation_parameters
47
+ entity_parameters + %i[user_id code_type_id]
48
+ end
49
+
50
+ def activated?
51
+ quantity < 1
52
+ end
53
+
54
+ def active?
55
+ quantity.positive?
56
+ end
57
+
58
+ private
59
+
60
+ def generate_body
61
+ return unless body.nil?
62
+
63
+ number = SecureRandom.random_number(0xffff_ffff_ffff_ffff)
64
+ self.body = number.to_s(36).scan(/.{4}/).join('-').upcase
65
+ end
66
+
67
+ def sanitize_quantity
68
+ self.quantity = QUANTITY_RANGE.first if quantity < QUANTITY_RANGE.first
69
+ self.quantity = QUANTITY_RANGE.last if quantity > QUANTITY_RANGE.last
70
+ end
71
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Adds method for validating model in controllers
4
+ #
5
+ # @author Maxim Khan-Magomedov <maxim.km@gmail.com>
6
+ module Checkable
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # @param id
11
+ # @param [Hash] parameters
12
+ def self.instance_for_check(id, parameters)
13
+ if id.blank?
14
+ entity = new(parameters)
15
+ else
16
+ entity = find_by(id: id)
17
+ entity.assign_attributes(parameters)
18
+ end
19
+ entity
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Model has flat priority field
4
+ #
5
+ # Adds sorting items by priority
6
+ # @author Maxim Khan-Magomedov <maxim.km@gmail.com>
7
+ module FlatPriority
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ after_initialize :set_next_priority
12
+ before_validation :normalize_priority
13
+
14
+ scope :ordered_by_priority, -> { order('priority asc, id asc') }
15
+
16
+ def self.priority_range
17
+ (1..32_767)
18
+ end
19
+ end
20
+
21
+ # @param [Integer] delta
22
+ def change_priority(delta)
23
+ swap_priority_with_adjacent(priority + delta)
24
+
25
+ self.class.ordered_by_priority.map { |e| [e.id, e.priority] }.to_h
26
+ end
27
+
28
+ protected
29
+
30
+ # @param [Integer] new_priority
31
+ def swap_priority_with_adjacent(new_priority)
32
+ adjacent = self.class.find_by(priority: new_priority)
33
+ if adjacent.is_a?(self.class) && (adjacent.id != id)
34
+ adjacent.update!(priority: priority)
35
+ end
36
+ update!(priority: new_priority)
37
+ end
38
+
39
+ def set_next_priority
40
+ return unless id.nil? && priority == 1
41
+
42
+ self.priority = self.class.maximum(:priority).to_i + 1
43
+ end
44
+
45
+ def normalize_priority
46
+ range = self.class.priority_range
47
+ self.priority = range.first if priority < range.first
48
+ self.priority = range.last if priority > range.last
49
+ end
50
+ end