prioritize 0.1.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 396834d7951d62b0cc6d2b49704e85ca61b360cd673d19173db5ef7766f26f8f
4
- data.tar.gz: 16391da63996a8867e26db9336e6617f1669df344b3f91d111a63f95174f8c6d
3
+ metadata.gz: 84063454a547cceae27c20081b6ad0f833cd352a2393a8a1670f2eabeca59ae2
4
+ data.tar.gz: 5ac4d9025cd9eb0f0de80cd1e89bb011e73df2a2886cf97fde2a801ab6221f14
5
5
  SHA512:
6
- metadata.gz: 857fec8198e0c39d35b529a9d5b42ab3109ca7c0f902aded72b8a5b6579e0a31802d9e1e18274c874ec02272596f866247c04fb6b8341b690280dfb938b37d57
7
- data.tar.gz: 8bc036e52af40fec4c6e75aada313bdbc5ca26a997bf358bf9da558aa98a45245880ea7d6176fe77efa217b2c53bca223545a4c516f1f3e554da5626a1856c71
6
+ metadata.gz: d7a789c449df198afe50c8d72de196fd2ada1b82451945901b725ebb9dbb4dd8558efa60dbc4e588815575fccc307a14d2f15ff48656d1a3aefde9f6dc3227f9
7
+ data.tar.gz: b2dff65570402d3618231ac5dc9cd70d20d2bd759ec749600692b63f523d7b1d8d86aa51122fde3025d138993197a8a8c2db73b2c26bae51dc9f253461858ef8
data/.rubocop.yml ADDED
@@ -0,0 +1,61 @@
1
+ # Игнорировать отсутствие магического комментария:
2
+ # frozen_string_literal: true
3
+ #
4
+ # Надо понимать что мы теряем производительность если не используем замороженные
5
+ # строки там где это возможно, применяйте!: ` -"неизменяемая строка" `
6
+ Style/FrozenStringLiteralComment:
7
+ Enabled: false
8
+
9
+ # Надо понимать что есть качественное различие между
10
+ #
11
+ # module Foo
12
+ # class Bar
13
+ # end
14
+ # end
15
+ #
16
+ # и
17
+ #
18
+ # class Foo::Bar
19
+ # end
20
+ #
21
+ # Если где то в модуль вынесен код:
22
+ #
23
+ # module Foo
24
+ # X = 42
25
+ # end
26
+ #
27
+ # то следует инициализировать модуль До класса-потомка, иначе ошибка. Как выход,
28
+ # рубокоп предлагает везде использовать первый синтаксис, а это некрасиво)
29
+ Style/ClassAndModuleChildren:
30
+ Enabled: false
31
+
32
+ # Корячить пустые методы с точка-запятой, нет уж извините!
33
+ Style/EmptyMethod:
34
+ Enabled: false
35
+
36
+ # Не даёт сравнимать с нуль (... == 0), заставляет переписывать на `.zero?`, как-то вообще не согласен!
37
+ Style/NumericPredicate:
38
+ Enabled: false
39
+
40
+ Metrics/MethodLength:
41
+ Max: 30
42
+
43
+ Metrics/ClassLength:
44
+ Max: 300
45
+
46
+ # fail обычно бросает исключение, raise пребрасывает после отлова при необходимости.
47
+ Style/SignalException:
48
+ Enabled: false
49
+
50
+ # Что может быть более привычным чем простой if
51
+ Style/NegatedIf:
52
+ Enabled: false
53
+
54
+ # Assignment - присвоения, в том числе +=...
55
+ # Branch - явное прямое ветвление программы (вызов методов).
56
+ # Condition - логические выражения
57
+ # AbcSize = Math.sqrt(A * A + B * B + C * C)
58
+ Metrics/AbcSize:
59
+ # Enabled: false
60
+ Max: 30
61
+ CountRepeatedAttributes: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- prioritize (0.1.0)
4
+ prioritize (1.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README-ru.md ADDED
@@ -0,0 +1,175 @@
1
+ # Prioritize
2
+
3
+ Позволяет сортировать как простые так и вложенные списки (например списки на основе гема closure_tree).
4
+
5
+ __Выбрать язык README.md__
6
+
7
+ - en [English](README.md)
8
+ - ru [Русский](README-ru.md)
9
+
10
+ __Важно!__ Работает только с PostgreSQL бд. Обновление данных происходит за 1
11
+ запрос, Пока что нет "ActiveRecord" алгоритма, на подобии: выбрать данные ->
12
+ перебрать-преобразовать -> сохранить. Только SQL, только хардкор.
13
+
14
+ _Prioritize_ добавляет следующие возможности:
15
+
16
+ 1. Добавляет в класс и экземпляр класса метод
17
+ `.priority_after(prev_id, moved_id)`, который обновит значения колонки
18
+ `my_column` (указанной в настройках) так, чтобы при
19
+ `Section.order(my_column: :asc)` элемент `moved` следавал за `prev`.
20
+
21
+ 2. Если при сохранении экземпляра модели в дополнительном поле `priority_prev`
22
+ указать идентификатор предыдущего элемента или строку '^' (при перемещении в
23
+ начало), тогда сработает поведение и значения колонки `my_column` обновятся в
24
+ соответствии с перемещением.
25
+
26
+
27
+
28
+
29
+ ## Установка
30
+
31
+ Добавьте эту строку в Gemfile вашего приложения:
32
+
33
+ ```ruby
34
+ gem 'prioritize', '~> 1.0'
35
+ ```
36
+
37
+ Затем выполните:
38
+
39
+ ```sh
40
+ bundle install
41
+ ```
42
+
43
+ Или установить его самостоятельно как:
44
+
45
+ ```sh
46
+ gem install prioritize
47
+ ```
48
+
49
+
50
+
51
+
52
+ ## Использование
53
+
54
+ Выполните подобную миграцию для вашей модели, если это необходимо:
55
+
56
+ ```rb
57
+ add_column :sections, "order", :integer , null: false, default: 0
58
+ add_index :sections, "order"
59
+ ```
60
+
61
+ Добавте настроечный метод в код класса:
62
+
63
+ ```rb
64
+ class Section < ApplicationRecord
65
+ prioritize_column(:order)
66
+ end
67
+ ```
68
+
69
+ Настройка окончена, наблюдаем результат в консоли:
70
+
71
+ ```sh
72
+ # Создадим несколько экземпляров
73
+ Section.create
74
+ => #<Section... id: 1, order: 0...>
75
+ Section.create
76
+ => #<Section... id: 2, order: 0...>
77
+
78
+ # Выведем идентификаторы с необходимым порядком.
79
+ Section.order(order: :asc).pluck(:id)
80
+ => [1, 2]
81
+
82
+ # Приоритет: после 2 следует 1
83
+ Section.priority_after(2, 1)
84
+ Section.order(order: :asc).pluck(:id)
85
+ => [2, 1]
86
+
87
+ # Переместим элемент 1 в начало
88
+ Section.find(1).priority_after(nil)
89
+ Section.order(order: :asc).pluck(:id)
90
+ => [1, 2]
91
+ ```
92
+
93
+ При перемещении элементов на фронтэнде (например с jquery-ui) нам необходимо отправить на контроллер
94
+ PATCH запрос примерно следующего характера с дополнительным полем `priority_prev`:
95
+
96
+ ```js
97
+ $.ajax({
98
+ url: "/sections/1.json",
99
+ type: "PATCH",
100
+ data: {
101
+ section: {
102
+ priority_prev: 2
103
+ }
104
+ }
105
+ })
106
+ ```
107
+
108
+ В контроллере необходимо разрешить параметр `priority_prev`:
109
+
110
+ ```rb
111
+ def section_params
112
+ params.require(:section).permit(
113
+ ...
114
+ :order,
115
+ :priority_prev
116
+ )
117
+ end
118
+ ```
119
+
120
+ При присутствии значения в поле `priority_prev` будет срабатывать обратный вызов, в котором
121
+ вызывается `.priority_after` метод.
122
+
123
+
124
+
125
+
126
+ ### Использование при сортировки древовидных структур
127
+
128
+ Модель
129
+
130
+ ```rb
131
+ class Catalog < ApplicationRecord
132
+ prioritize_column(:order, nested: true)
133
+ end
134
+ ```
135
+
136
+ При обновлении значений колонки :order используются только элементы с тем же
137
+ предком что и перемещаемый элемент.
138
+
139
+
140
+
141
+
142
+ ## Разработка
143
+
144
+ При желании протестировать изменения гема с вашим приложением необходимо перенастроить Gemfile:
145
+
146
+ ```ruby
147
+ # Сортировка
148
+ # gem 'prioritize', '~> 1.0'
149
+ gem 'prioritize', path: '/home/username/path/to/cloned/prioritize'
150
+ ```
151
+
152
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
153
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
154
+ prompt that will allow you to experiment.
155
+
156
+ To install this gem onto your local machine, run `bundle exec rake install`. To
157
+ release a new version, update the version number in `version.rb`, and then run
158
+ `bundle exec rake release`, which will create a git tag for the version, push
159
+ git commits and tags, and push the `.gem` file to [rubygems.org]
160
+ (https://rubygems.org).
161
+
162
+ ```sh
163
+ bundle
164
+ rake spec
165
+ ```
166
+
167
+ ## Содействие
168
+
169
+ Отчеты об ошибках и запросы на включение приветствуются на GitHub https://github.com/Zlatov/prioritize.
170
+
171
+
172
+ ## Лицензия
173
+
174
+ Гем доступен с открытым исходным кодом в соответствии с условиями
175
+ [MIT License](https://opensource.org/licenses/MIT).
data/README.md CHANGED
@@ -1,18 +1,38 @@
1
1
  # Prioritize
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be
4
- able to package up your Ruby library into a gem. Put your Ruby code in the file
5
- `lib/prioritize`. To experiment with that code, run `bin/console` for an
6
- interactive prompt.
3
+ Allows you to sort both simple and nested lists (for example, lists based on the
4
+ closure_tree gem).
5
+
6
+ __Select language README.md__
7
+
8
+ - en [English](README.md)
9
+ - ru [Русский](README-ru.md)
10
+
11
+ __Important!__ Only works with PostgreSQL databases. Updating data occurs in 1
12
+ request, So far there is no "ActiveRecord" algorithm, like: select data ->
13
+ sort-transform data -> save. Only SQL, only hardcore.
14
+
15
+ _Prioritize_ adds the following features:
16
+
17
+ 1. Adds a method `.priority_after(prev_id, moved_id)` to the class and class
18
+ instance, which will update the values of the `my_column` column (specified in
19
+ the settings) so that when `Section.order(my_column: :asc)` the `moved` element
20
+ followed the `prev`.
21
+
22
+ 2. If, when saving a model instance, in the additional field `priority_prev`
23
+ specify the identifier of the previous element or the string '^' (when moving
24
+ to the beginning), then the behavior will work and the values of the
25
+ `my_column` column will be updated in accordance with the movement.
26
+
27
+
7
28
 
8
- TODO: Delete this and the text above, and describe your gem
9
29
 
10
30
  ## Installation
11
31
 
12
32
  Add this line to your application's Gemfile:
13
33
 
14
34
  ```ruby
15
- gem 'prioritize'
35
+ gem 'prioritize', '~> 1.0'
16
36
  ```
17
37
 
18
38
  And then execute:
@@ -23,9 +43,83 @@ Or install it yourself as:
23
43
 
24
44
  $ gem install prioritize
25
45
 
46
+
47
+
48
+
26
49
  ## Usage
27
50
 
28
- TODO: Write usage instructions here
51
+ Perform a similar migration for your model if necessary:
52
+
53
+ ```rb
54
+ add_column :sections, "order", :integer , null: false, default: 0
55
+ add_index :sections, "order"
56
+ ```
57
+
58
+ Add a setup method to the class code:
59
+
60
+ ```rb
61
+ class Section < ApplicationRecord
62
+ prioritize_column(:order)
63
+ end
64
+ ```
65
+
66
+ The setup is over, we observe the result in the console:
67
+
68
+ ```sh
69
+ # Let's create multiple instances
70
+ Section.create
71
+ => #<Section... id: 1, order: 0...>
72
+ Section.create
73
+ => #<Section... id: 2, order: 0...>
74
+
75
+ # Let's derive the identifiers with the required order.
76
+ Section.order(order: :asc).pluck(:id)
77
+ => [1, 2]
78
+
79
+ # Priority: 2 followed by 1
80
+ Section.priority_after(2, 1)
81
+ Section.order(order: :asc).pluck(:id)
82
+ => [2, 1]
83
+
84
+ # Move element 1 to the beginning
85
+ Section.find(1).priority_after(nil)
86
+ Section.order(order: :asc).pluck(:id)
87
+ => [1, 2]
88
+ ```
89
+
90
+ When moving elements on the frontend (for example, with jquery-ui), we need to
91
+ send a PATCH request to the controller of the following nature with an
92
+ additional `priority_prev` field:
93
+
94
+ ```js
95
+ $.ajax({
96
+ url: "/sections/1.json",
97
+ type: "PATCH",
98
+ data: {
99
+ section: {
100
+ priority_prev: 2
101
+ }
102
+ }
103
+ })
104
+ ```
105
+
106
+ In the controller, the `priority_prev` parameter must be enabled:
107
+
108
+ ```rb
109
+ def section_params
110
+ params.require(:section).permit(
111
+ ...
112
+ :order,
113
+ :priority_prev
114
+ )
115
+ end
116
+ ```
117
+
118
+ If there is a value in the `priority_prev` field, a callback will be fired in
119
+ which the `.priority_after` method is called.
120
+
121
+
122
+
29
123
 
30
124
  ## Development
31
125
 
@@ -46,7 +140,7 @@ rake spec
46
140
 
47
141
  ## Contributing
48
142
 
49
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/prioritize.
143
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Zlatov/prioritize.
50
144
 
51
145
 
52
146
  ## License
@@ -1,3 +1,3 @@
1
1
  module Prioritize
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/prioritize.rb CHANGED
@@ -1,89 +1,150 @@
1
- require "prioritize/version"
1
+ require 'prioritize/version'
2
2
 
3
+ # Корневой модуль отделяющий пространство имён Классов и Модулей гема от
4
+ # приложения.
3
5
  module Prioritize
4
- class Error < StandardError; end
6
+ # Собственные ошибки TODO: разработать список частых ошибок при использовании
7
+ # гема с описанием исправления.
8
+ class Error < StandardError
9
+ end
5
10
 
6
- def self.included base
11
+ # Можно сказать точка входа, включаем гем в базовый класс ActiveRecord::Base,
12
+ # после чего в любой модели унаследованной от этого класса появится метод
13
+ # prioritize_column(...).
14
+ # Метод расширяет конкретный класс дополнительными методами гема и
15
+ # настройками.
16
+ def self.included(base)
7
17
  base.extend ClassMethods
8
18
  end
9
19
 
20
+ # Все Модели унаследованные от ActiveRecord::Base буду обладать указанным
21
+ # настроечным методом класса prioritize_column(...)
10
22
  module ClassMethods
11
- def prioritize_column
12
- self.include PriorityAfter
23
+ # При вызове настроечного метода
24
+ def prioritize_column(column, nested: false, parent_column: :parent_id)
25
+ # Класс расширяется модулем PriorityAfter
26
+ include PriorityAfter
27
+
28
+ # fail Error.new('') if column.blank?
29
+
30
+ # Настраиваем конкретную колонку для текущей модели.
31
+ self.priority_column = column.to_s
32
+ self.priority_nested = nested
33
+ self.priority_parent = parent_column.to_s
13
34
  end
14
35
  end
15
36
 
37
+ # Модуль которым расширяем Модели использовавшие в себе метод
38
+ # prioritize_column(...)
16
39
  module PriorityAfter
17
- def self.included base
40
+ def self.included(base)
18
41
  base.extend ClassMethods
42
+ # К экземпляру класса добавим поле, в которое необходимо записать значение
43
+ # если необходимо выполнить сортировку.
44
+ # После обновления экземпляра если в поле установлено значение - сработает
45
+ # поведение.
46
+ attr_accessor :priority_prev
47
+ # Навесим поведение модели.
48
+ base.after_update :priority_callback
19
49
  end
20
- def priority_after before_id
21
- self.class.priority_after before_id, self.id
50
+
51
+ # При заполннном поле priority_prev запустим запрос на обновление
52
+ # сортировочного поля. В данном поле должен быть записан идентификатор
53
+ # элемента ЗА которым должен слеовать перемещаемый элемент. Если такого
54
+ # элемента нет (тоесть перемещаемый элемент переносится в начало списка),
55
+ # тогда в поле необходимо записать строку '^'.
56
+ def priority_callback
57
+ if priority_prev.present?
58
+ prev_id = priority_prev == '^' ? nil : priority_prev
59
+ priority_after prev_id
60
+ end
22
61
  end
62
+
63
+ def priority_after(prev_id)
64
+ self.class.priority_after prev_id, id
65
+ end
66
+
67
+ # Методы класса после расширения модулем PriorityAfter перечислены в этом
68
+ # подмодуле.
23
69
  module ClassMethods
24
- def priority_after before_id, moved_id
70
+ def priority_after(prev_id, moved_id)
25
71
  connection.exec_query(
26
- <<-SQL,
27
- UPDATE #{table_name} o
28
- SET #{priority_column} = ordered.rn
29
- FROM (
30
- SELECT *, ROW_NUMBER() OVER() AS rn
31
- FROM (
32
- (
33
- SELECT o.*
34
- FROM #{table_name} o
35
- LEFT JOIN #{table_name} ai ON ai.id = $1
36
- WHERE
37
- (o.#{priority_column} < ai.#{priority_column} OR ai.#{priority_column} IS NULL) AND
38
- o.id <> $2
39
- ORDER BY o.#{priority_column} ASC
40
- )
41
- UNION ALL
42
- (
43
- SELECT o.*
44
- FROM #{table_name} o
45
- WHERE o.id = $2
46
- )
47
- UNION ALL
48
- (
49
- SELECT o.*
50
- FROM #{table_name} o
51
- LEFT JOIN #{table_name} ai ON ai.id = $1
52
- WHERE
53
- o.#{priority_column} >= ai.#{priority_column} AND
54
- o.id <> $2
55
- ORDER BY o.#{priority_column} ASC
56
- )
57
- ) numbered
58
- ) ordered
59
- WHERE o.id = ordered.id
60
- SQL
72
+ priority_sql,
61
73
  'priority_after',
62
- [[nil,before_id], [nil,moved_id]]
74
+ [[nil, prev_id], [nil, moved_id]]
63
75
  )
64
76
  end
65
- attr_accessor :priority_column
66
- def priority_column
67
- @priority_column.to_s
77
+
78
+ def priority_sql
79
+ sql = <<-SQL
80
+ UPDATE #{table_name} o
81
+ SET "#{priority_column}" = ordered.rn
82
+ FROM (
83
+ SELECT *, ROW_NUMBER() OVER() AS rn
84
+ FROM (
85
+ -- Выбрать записи от начала и до предыдущего (включая его),
86
+ -- конечно исключая перемещаемую запись.
87
+ (
88
+ SELECT o.*
89
+ FROM #{table_name} o
90
+ LEFT JOIN #{table_name} prev ON prev.id IS NOT DISTINCT FROM $1
91
+ LEFT JOIN #{table_name} moved ON moved.id IS NOT DISTINCT FROM $2
92
+ WHERE
93
+ o."#{priority_column}" <= prev."#{priority_column}"
94
+ AND o.id <> $2
95
+ nested_condition
96
+ ORDER BY o."#{priority_column}" ASC
97
+ )
98
+ UNION ALL
99
+ -- Выбрать перемещаемую запись.
100
+ (
101
+ SELECT o.*
102
+ FROM #{table_name} o
103
+ WHERE o.id = $2
104
+ )
105
+ UNION ALL
106
+ -- Выбрать от предыдущего (не включая его) и до конца, так же
107
+ -- исключая перемещаемую запись.
108
+ (
109
+ SELECT o.*
110
+ FROM #{table_name} o
111
+ LEFT JOIN #{table_name} prev ON prev.id IS NOT DISTINCT FROM $1
112
+ LEFT JOIN #{table_name} moved ON moved.id IS NOT DISTINCT FROM $2
113
+ WHERE
114
+ (o."#{priority_column}" > prev."#{priority_column}" OR prev."#{priority_column}" IS NULL)
115
+ AND o.id <> $2
116
+ nested_condition
117
+ ORDER BY o."#{priority_column}" ASC
118
+ )
119
+ ) numbered
120
+ ) ordered
121
+ WHERE o.id = ordered.id
122
+ SQL
123
+ nested_condition = priority_nested ?
124
+ %Q(AND o."#{priority_parent}" IS NOT DISTINCT FROM moved."#{priority_parent}") :
125
+ ''
126
+ sql.gsub! 'nested_condition', nested_condition
127
+ sql
68
128
  end
69
- def self.extended base
129
+
130
+ # def priority_column
131
+ # @priority_column.to_s
132
+ # end
133
+
134
+ # def priority_column=(value)
135
+ # @priority_column = value
136
+ # end
137
+
138
+ attr_accessor :priority_column, :priority_nested, :priority_parent
139
+
140
+ def self.extended(base)
70
141
  base.class_eval do
71
- @priority_column = :priority
142
+ # @priority_column = :priority
72
143
  end
73
144
  end
74
145
  end
75
146
  end
76
147
  end
77
148
 
78
- ActiveRecord::Base.send(:include, Prioritize)
79
-
80
-
81
- # byebug
82
-
83
- # Кто после кого
84
- # PostSection.priority_after 1, 2
85
- # post_section.priority_after 2
86
- #
87
- #
88
- # self.implicit_order_column = "updated_at"
89
- #
149
+ # ActiveRecord::Base.send(:include, Prioritize)
150
+ ActiveRecord::Base.include Prioritize
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prioritize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zlatov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-25 00:00:00.000000000 Z
11
+ date: 2022-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
@@ -68,11 +68,13 @@ files:
68
68
  - ".byebug_history"
69
69
  - ".gitignore"
70
70
  - ".rspec"
71
+ - ".rubocop.yml"
71
72
  - ".ruby-version"
72
73
  - ".travis.yml"
73
74
  - Gemfile
74
75
  - Gemfile.lock
75
76
  - LICENSE.txt
77
+ - README-ru.md
76
78
  - README.md
77
79
  - Rakefile
78
80
  - bin/console