lexoranking 0.1.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 72f006a273afafbc992d2d23b520f21f18064004be7272d1292e8c9091d07a03
4
+ data.tar.gz: 1a5717d7d63036d9d378c91e03429d2975c78d9c9bb9d3698b8eed9b2ce4573b
5
+ SHA512:
6
+ metadata.gz: 2ec1c8201943d37b376bb37a5ec2294a5f5f1e9ccb58b4dcbe047433bd0b846f5d9be513bf54bbe33eebde80b64fee7c0efca69783ae490a55ae2fe3ac483c06
7
+ data.tar.gz: 7ccf361f0888868024efb36b0f122bd5c1e92480f5c975118cd2233311200a409023acba711f9ff299b0e7882ca3442d92d49d29c902ed6f0c6cd0064856df51
@@ -0,0 +1,24 @@
1
+ name: Ruby
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ os: [ ubuntu-latest ]
15
+ ruby: [ 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', head, truffleruby, truffleruby-head, jruby, jruby-head ]
16
+ runs-on: ${{ matrix.os }}
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: ${{ matrix.ruby }}
22
+ bundler-cache: true
23
+ - run: bundle exec rake
24
+ continue-on-error: ${{ endsWith(matrix.ruby, 'head') }}
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.gem
10
+ Gemfile.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,68 @@
1
+ require: rubocop-performance
2
+ require: rubocop-md
3
+
4
+ AllCops:
5
+ TargetRubyVersion: 2.6
6
+ Exclude:
7
+ - "Rakefile"
8
+
9
+ Style/MultilineBlockChain:
10
+ Enable: false
11
+
12
+ Security/YAMLLoad:
13
+ Enabled: false
14
+
15
+ Style/StringLiterals:
16
+ EnforcedStyle: double_quotes
17
+ ConsistentQuotesInMultiline: true
18
+
19
+ Style/StringLiteralsInInterpolation:
20
+ EnforcedStyle: double_quotes
21
+
22
+
23
+ Style/Lambda:
24
+ EnforcedStyle: literal
25
+
26
+ Layout/ParameterAlignment:
27
+ EnforcedStyle: with_fixed_indentation
28
+
29
+
30
+ Layout/MultilineMethodCallIndentation:
31
+ EnforcedStyle: indented
32
+
33
+ Metrics/BlockLength:
34
+ Exclude:
35
+ - "spec/**/*.rb"
36
+ - "db/migrate/*.rb"
37
+ - "*.gemspec"
38
+ - "lib/tasks/**/*.rake"
39
+
40
+ Metrics/LineLength:
41
+ Max: 120
42
+
43
+ Lint/AssignmentInCondition:
44
+ Enabled: false
45
+
46
+ Layout/FirstHashElementIndentation:
47
+ EnforcedStyle: consistent
48
+
49
+
50
+ Layout/SpaceAroundEqualsInParameterDefault:
51
+ EnforcedStyle: no_space
52
+
53
+ Style/BracesAroundHashParameters:
54
+ EnforcedStyle: no_braces
55
+
56
+ Metrics/AbcSize:
57
+ Max: 60
58
+
59
+ Metrics/MethodLength:
60
+ Max: 16
61
+ Exclude:
62
+ - "spec/**/*.rb"
63
+ - "migrate/*.rb"
64
+ - "*.gemspec"
65
+
66
+ Lint/AmbiguousBlockAssociation:
67
+ Exclude:
68
+ - "spec/**/*"
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at yair.facio11@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in lexoranking.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 12.0"
9
+ gem "rspec", "~> 3.0"
10
+ gem "rubocop", "0.79.0"
11
+ gem "rubocop-md"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 yairfacio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # Lexoranking
2
+
3
+ Lexicographical sorting for ActiveRecord Models.
4
+
5
+ ## How does it work?
6
+ Allows your ActiveRecord models to sort its elements using lexographical sorting.
7
+
8
+ This is inpired by how Jira handles sorting of kanban board.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem "lexoranking", "~> 1.3"
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle install
21
+
22
+ To generate the require migration for your models execute:
23
+
24
+ ```ruby
25
+ rails generate lexoranking:install --model=your_model
26
+ ```
27
+
28
+ This will generate a migration file adding a `rank` column to your model, which is used to perform sorting.
29
+
30
+ ```ruby
31
+ class AddRankToProjects < ActiveRecord::Migration[6.1]
32
+ def change
33
+ add_column :projects, :rank, :text
34
+ add_index :projects, :rank
35
+ end
36
+ end
37
+ ```
38
+
39
+ Then run the migration by executing
40
+ ```ruby
41
+ rails db:migrate
42
+ ```
43
+ ## Usage
44
+
45
+ Declare your model as a lexoranking model
46
+ ```ruby
47
+ class Project < ApplicationRecord
48
+ include Lexoranking::Model
49
+ end
50
+ ```
51
+
52
+ Now when you create a new `Project` the sorting value for the `rank` column will be calculated automatically when saving the record to the data base.
53
+
54
+ ```ruby
55
+ project = Project.new(name: 'My Project', description: 'Random description')
56
+ project.save
57
+
58
+ #<Project:0x00007f9f6b4a2a38
59
+ id: 1,
60
+ name: 'My Project',
61
+ description: 'Random description',
62
+ rank: 'y'
63
+ >
64
+ ```
65
+
66
+ ## Class Methods
67
+
68
+ The model will have access to the following class method.
69
+ ```ruby
70
+ # Retrieve the collection of Projects sorted by their ranking in ascending order
71
+ Project.ranked
72
+ ```
73
+
74
+ ## Instance Methods
75
+
76
+ You will have access to the following instance methods.
77
+ ```ruby
78
+ project = Project.find(3)
79
+
80
+ # Rank the element to the last position of the list
81
+ project.rank_last
82
+
83
+ # Rank the record to the first position of the list
84
+ project.rank_first
85
+
86
+ # Rank the record to a specific position of the list
87
+ project.rank_to(4)
88
+ ```
89
+
90
+ ## Working with Associations
91
+ If your model belongs to another model and you want to sort the elements scope to the association, you can simply add a class attribute that will allow your models to be sorted based on the scope they belong to.
92
+
93
+ For example, in an application where a `Project` model has many tasks. We can sort tasks using the project they belong to as the scope.
94
+
95
+ ```ruby
96
+ class Project < ApplicationRecord
97
+ has_many :tasks
98
+ end
99
+
100
+ class Task < ApplicationRecord
101
+ include Lexoranking::Model
102
+ self.ranking_scope = :project_id
103
+
104
+ belongs_to :project
105
+ end
106
+ ```
107
+
108
+ Adding this class attribute to the model, allows your records to be sorted in the scope of the project they belong to.
109
+
110
+ You can also define a default scope for your models if you want to retreive the elements in the lexicographical order.
111
+
112
+ ```ruby
113
+ class Task < ApplicationRecord
114
+ include Lexoranking::Model
115
+ self.ranking_scope = :project_id
116
+ default_scope { self.ranked }
117
+
118
+ belongs_to :project
119
+ end
120
+ ```
121
+
122
+ With this you can chain ActiveRecord methods and get all the elements in their right lexicographical order.
123
+
124
+ ```ruby
125
+ Project.last.tasks
126
+ ```
127
+
128
+ ## Development
129
+
130
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
131
+
132
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
133
+
134
+ ## Contributing
135
+
136
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/lexoranking. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/lexoranking/blob/master/CODE_OF_CONDUCT.md).
137
+
138
+
139
+ ## License
140
+
141
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
142
+
143
+ ## Code of Conduct
144
+
145
+ Everyone interacting in the Lexoranking project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/lexoranking/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "lexoranking"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/lexoranking/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "lexoranking"
7
+ spec.version = Lexoranking::VERSION
8
+ spec.authors = ["yairfacio"]
9
+ spec.email = ["yair.facio11@gmail.com"]
10
+
11
+ spec.summary = "Lexoranking gem to reorder elements in kanban board using lexicographic sorting techinque."
12
+ spec.description = "Allow your models to order and reorder elements using lexoranking sorting."
13
+ spec.homepage = "https://rubygems.org"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://rubygems.org"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_dependency "activerecord"
32
+ spec.add_dependency "activesupport", "~>6.0"
33
+ spec.add_dependency "pry", "~> 0.14.1"
34
+ spec.add_dependency "zeitwerk", "~> 2.4", ">= 2.4.2"
35
+
36
+ spec.add_development_dependency "rake"
37
+ spec.add_development_dependency "rubocop", "0.77.0"
38
+ spec.add_development_dependency 'generator_spec'
39
+ end
@@ -0,0 +1,51 @@
1
+ module Lexoranking
2
+ module Generators
3
+ class InstallGenerator < ::Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+ source_root File.expand_path('../templates', __FILE__)
6
+ desc "Add migration for Lexoranking models"
7
+ class_option :model, type: :string, desc: 'Model name where to add rank column'
8
+ RANKING_COLUMN = :rank
9
+
10
+ def self.next_migration_number(path)
11
+ next_migration_number = current_migration_number(path) + 1
12
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
13
+ end
14
+
15
+ def setup
16
+ @model_name = options.model.pluralize
17
+ end
18
+
19
+ def copy_migrations
20
+ validates_ranking_column!
21
+
22
+ migration_template "migration.rb", "db/migrate/add_#{column}_to_#{model_name}.rb"
23
+ end
24
+
25
+
26
+ private
27
+ attr_reader :model_name, :column
28
+
29
+ def validates_ranking_column!
30
+ model_exists! || _raise_model_do_not_exists
31
+
32
+ @column = load_ranking_column(model_name.classify.constantize) || RANKING_COLUMN
33
+ end
34
+
35
+ def model_exists!
36
+ ActiveRecord::Base.connection.tables.map(&:classify).include?(model_name.classify)
37
+ end
38
+
39
+ def load_ranking_column(model)
40
+ model.ranking_column if model.respond_to? :ranking_column
41
+ end
42
+
43
+ def _raise_model_do_not_exists
44
+ raise ::Lexoranking::ModelDoNotExists.new(
45
+ "The model #{model_name.classify} do not exists",
46
+ self
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ class Add<%= column.to_s.camelize %>To<%= model_name.capitalize %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ add_column :<%= model_name %>, :<%= column %>, :text
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lexoranking/version"
4
+ require "lexoranking/model"
5
+ require "lexoranking/errors"
6
+ require "lexoranking/main"
7
+ require "active_support/concern"
8
+ require 'active_support/inflector'
9
+ require 'rails/generators'
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexoranking
4
+ class LexorankingError < StandardError
5
+ end
6
+
7
+ # RankingScopeNotValid class
8
+ class RankingScopeNotValid < LexorankingError
9
+ attr_reader :record
10
+
11
+ def initialize(message=nil, record=nil)
12
+ @record = record
13
+ super(message)
14
+ end
15
+ end
16
+
17
+ # ModelDoNotExists class
18
+ class ModelDoNotExists < LexorankingError
19
+ attr_reader :record
20
+
21
+ def initialize(message=nil, record=nil)
22
+ @record = record
23
+ super(message)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexoranking
4
+ # Main class that calculates the ranking value based on
5
+ # some previous and next elements.
6
+ class Main
7
+ class InvalidRankError < StandardError; end
8
+
9
+ MIN_CHAR = "a"
10
+ MAX_CHAR = "z"
11
+
12
+ class << self
13
+ def perform(prev, after)
14
+ new(prev, after).calculate_ranking
15
+ end
16
+ end
17
+
18
+ def initialize(prev, after)
19
+ @prev = prev || MIN_CHAR
20
+ @after = after || MAX_CHAR
21
+ end
22
+
23
+ def calculate_ranking
24
+ rank = ""
25
+ i = 0
26
+
27
+ loop do
28
+ prev_char = get_char(prev, i, MIN_CHAR)
29
+ after_char = get_char(after, i, MAX_CHAR)
30
+
31
+ if prev_char == after_char
32
+ rank += prev_char
33
+ i += 1
34
+ next
35
+ end
36
+
37
+ mid_char = mid(prev_char, after_char)
38
+ if mid_char == prev_char || mid_char == after_char
39
+ rank += prev_char
40
+ i += 1
41
+ next
42
+ end
43
+
44
+ rank += mid_char
45
+ break
46
+ end
47
+
48
+ rank
49
+ end
50
+
51
+ def mid(prev, after)
52
+ middle_ascii = ((prev.ord + after.ord) / 2).round
53
+ middle_ascii.chr
54
+ end
55
+
56
+ def get_char(str, idx, default_char)
57
+ idx >= str.length ? default_char : str[idx]
58
+ end
59
+
60
+ attr_accessor :prev, :after
61
+ end
62
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexoranking
4
+ # Allows your models to sort elements using lexographical sorting
5
+ #
6
+ # Options:
7
+ # self.ranking_scope - Determine if the elements should be scoped to a specific
8
+ # association before sorting.
9
+ module Model
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ class_attribute :ranking_column, :ranking_scope
14
+ self.ranking_scope = nil
15
+ self.ranking_column = :rank
16
+
17
+ scope :ranked, -> { where.not(ranking_column => nil).order(ranking_column => :asc) }
18
+ end
19
+
20
+ # Rank the record to the last position of the list before saving it.
21
+ def save
22
+ rank_to(:last) if rank.nil?
23
+ super
24
+ end
25
+
26
+ # Ranks the record to the last position of the list and saves it.
27
+ def rank_last
28
+ rank_to(:last)
29
+ end
30
+
31
+ # Ranks the record to the first postiion of the list and saves it.
32
+ def rank_first
33
+ rank_to(0)
34
+ end
35
+
36
+ # Ranks the record to a specific position in the list and saves it.
37
+ def rank_to(position)
38
+ model_collection = get_collection
39
+ # This return statement handle the case of when there is only one
40
+ # element in the scoped collection and we call this method. Since
41
+ # there is only one element in the collection and the rank column is
42
+ # present there is no reason to calculate the ranking value again.
43
+ return if model_collection.size == 1 && rank.present?
44
+ position = position == :last ? model_collection.size-1 : position.to_i
45
+ previous, nextt = get_prev_next_elements(position, model_collection)
46
+ ranking = calculate_ranking(previous, nextt)
47
+
48
+ send("#{self.class.ranking_column}=", ranking)
49
+ save
50
+ end
51
+
52
+ private
53
+
54
+ # Returns the model collection scoped to the ranking scope column in the model
55
+ #
56
+ # If the ranking scope colunm does not exists in the model or it is not an
57
+ # association it will raise an exception error.
58
+ #
59
+ # raise {Lexoranking::RankingScopeNotValid}
60
+ def collection_by_ranking_scope
61
+ model_scope = ranking_model_scope
62
+ validate_ranking_scope_column!(model_scope) || _raise_ranking_scope_not_valid
63
+
64
+ self.class.ranked.where("#{model_scope}": send(model_scope))
65
+ end
66
+
67
+ # Returns a ranked model collection of elements if the ranking model scope is not present
68
+ # otherwise it will return a models collection scoped to the ranking scope column.
69
+ def get_collection
70
+ @get_collection ||= ranking_model_scope.present? ? collection_by_ranking_scope : self.class.ranked
71
+ end
72
+
73
+ # Returns the ranking scope column
74
+ def ranking_model_scope
75
+ @ranking_model_scope ||= self.class.ranking_scope
76
+ end
77
+
78
+ # Returns the previous and next elements where the new element will be position inside the list.
79
+ #
80
+ # If the position we want to sort the element is position zero, it will return nil for the previous
81
+ # element and the the first element of the list.
82
+ #
83
+ # If the position is less than the index of the current element(self) that means that we try to
84
+ # move the element from a higher position to a lower position in the list and in this case we
85
+ # decrement the position by 1 so that we keep the zero base logic, otherwise we are trying to move
86
+ # the element from a lower position of the list to a higher position and in this case we keep the
87
+ # position value as it is.
88
+ # We use the position to offset those number of elements in the list and then take the next two. That
89
+ # way we will have the two element where we want to position the record in between.
90
+ def get_prev_next_elements(position, collection)
91
+ return [collection[0], nil] if position == 0 && collection.size == 1
92
+ return [nil, collection[0]] if position <= 0
93
+ position -= 1 if (collection.map(&:id).index(id) || 0) > position
94
+ collection.offset(position).limit(2)
95
+ end
96
+
97
+ # Calculated the ranking value for the current record(self) based on the previous and nextt elements in
98
+ # the list.
99
+ def calculate_ranking(prev, nextt)
100
+ Lexoranking::Main.perform(
101
+ prev&.send(self.class.ranking_column),
102
+ nextt&.send(self.class.ranking_column)
103
+ )
104
+ end
105
+
106
+ # Validates that the ranking scope column exists in the model or is an association for the model.
107
+ def validate_ranking_scope_column!(model_scope)
108
+ self.class.columns.map(&:name).include?(model_scope.to_s) || self.class.reflect_on_association(model_scope)
109
+ end
110
+
111
+ def _raise_ranking_scope_not_valid
112
+ raise ::Lexoranking::RankingScopeNotValid.new(
113
+ "The #{ranking_model_scope} column does not exists in this model #{self.class.name}",
114
+ self
115
+ )
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lexoranking
4
+ VERSION = "0.1.3"
5
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lexoranking
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - yairfacio
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-06-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.14.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.14.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: zeitwerk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.4'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.4.2
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '2.4'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.4.2
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rubocop
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - '='
94
+ - !ruby/object:Gem::Version
95
+ version: 0.77.0
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '='
101
+ - !ruby/object:Gem::Version
102
+ version: 0.77.0
103
+ - !ruby/object:Gem::Dependency
104
+ name: generator_spec
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ description: Allow your models to order and reorder elements using lexoranking sorting.
118
+ email:
119
+ - yair.facio11@gmail.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - ".github/workflows/ruby.yml"
125
+ - ".gitignore"
126
+ - ".rubocop.yml"
127
+ - CODE_OF_CONDUCT.md
128
+ - Gemfile
129
+ - LICENSE.txt
130
+ - README.md
131
+ - Rakefile
132
+ - bin/console
133
+ - bin/setup
134
+ - lexoranking.gemspec
135
+ - lib/generators/lexoranking/install_generator.rb
136
+ - lib/generators/lexoranking/templates/migration.rb.tt
137
+ - lib/lexoranking.rb
138
+ - lib/lexoranking/errors.rb
139
+ - lib/lexoranking/main.rb
140
+ - lib/lexoranking/model.rb
141
+ - lib/lexoranking/version.rb
142
+ homepage: https://rubygems.org
143
+ licenses:
144
+ - MIT
145
+ metadata:
146
+ allowed_push_host: https://rubygems.org
147
+ homepage_uri: https://rubygems.org
148
+ source_code_uri: https://rubygems.org
149
+ post_install_message:
150
+ rdoc_options: []
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: 2.3.0
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubygems_version: 3.1.2
165
+ signing_key:
166
+ specification_version: 4
167
+ summary: Lexoranking gem to reorder elements in kanban board using lexicographic sorting
168
+ techinque.
169
+ test_files: []