{"id":30600388,"url":"https://github.com/eliav-lavi/priora","last_synced_at":"2025-08-29T23:15:14.067Z","repository":{"id":56888778,"uuid":"148376787","full_name":"eliav-lavi/priora","owner":"eliav-lavi","description":"An Object Prioritization Utility for Ruby","archived":false,"fork":false,"pushed_at":"2018-09-12T08:25:48.000Z","size":22,"stargazers_count":29,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-13T09:31:27.886Z","etag":null,"topics":["gem","prioritization","ruby","sorting"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eliav-lavi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-09-11T20:33:54.000Z","updated_at":"2025-04-16T19:51:56.000Z","dependencies_parsed_at":"2022-08-20T23:40:56.738Z","dependency_job_id":null,"html_url":"https://github.com/eliav-lavi/priora","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/eliav-lavi/priora","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliav-lavi%2Fpriora","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliav-lavi%2Fpriora/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliav-lavi%2Fpriora/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliav-lavi%2Fpriora/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eliav-lavi","download_url":"https://codeload.github.com/eliav-lavi/priora/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliav-lavi%2Fpriora/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272779027,"owners_count":24991509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-29T02:00:10.610Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gem","prioritization","ruby","sorting"],"created_at":"2025-08-29T23:15:12.864Z","updated_at":"2025-08-29T23:15:14.057Z","avatar_url":"https://github.com/eliav-lavi.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Priora: An Object Prioritization Utility for Ruby\n[![Gem Version](https://badge.fury.io/rb/priora.svg)](https://badge.fury.io/rb/priora)\n[![Maintainability](https://api.codeclimate.com/v1/badges/7a45f13797375a92b558/maintainability)](https://codeclimate.com/github/eliav-lavi/priora/maintainability)\n[![Build Status](https://travis-ci.org/eliav-lavi/priora.svg?branch=master)](https://travis-ci.org/eliav-lavi/priora)\n\nPriora supplies an easy and intuitive way to prioritize a collection of objects in Ruby.\nIt serves as a useful utility for working with a collection of several instances of some data class.\nOften, we would like to get that collection arranged according to some prioritization logic.  \nInstead of writing custom sorting blocks or implementing the spaceship operator (`\u003c=\u003e`) in your class,\nPriora offers a declarative style in order to obtain ready-for-consumption collections.\n\nFor example, let's assume we have a simple `Post` class, holding data regarding the author name,\nhow many likes did it receive and whether this post is sponsored:\n\n```ruby\nclass Post\n  attr_reader :author, :like_count, :is_sponsored\n\n  def initialize(author:, like_count:, is_sponsored:)\n    @author = author\n    @like_count = like_count\n    @is_sponsored = is_sponsored\n  end\nend\n``` \n\nThen, in a given scenario, we have three instances at hand (these examples will be used throughout this README):\n\n```ruby\nlow_like_count_sponsored = Post.new(author: 'Jay C.', like_count: 10, is_sponsored: true)\nhigh_like_count_unsponsored = Post.new(author: 'Aaron R.', like_count: 90, is_sponsored: false)\nhigh_like_count_sponsored = Post.new(author: 'Don Y.', like_count: 90, is_sponsored: true)\n```\n\nUsing Priora, we can easily get the collection prioritized according to our needs:\n\n```ruby\nunprioritized_array = [high_like_count_unsponsored, low_like_count_sponsored, high_like_count_sponsored]\nprioritized_array =  [high_like_count_sponsored, high_like_count_unsponsored, low_like_count_sponsored]\nPriora.prioritize(unprioritized_array, by: [:like_count, :is_sponsored]) == prioritized_array\n=\u003e true\n```\n\nIn case we can commit to the prioritization between `Post` objects - i.e. we do not need the flexibility of\nchanging the priorities each time - we can include the `Priora` module in our class and\ndeclare the priorities using the `prioritize_by` class macro and gain shorter invocation.\nOur class would then read like this:\n \n```ruby\nclass Post\n  include Priora\n  prioritize_by :like_count, :is_sponsored\n\n  attr_reader :author, :like_count, :is_sponsored\n\n  def initialize(author:, like_count:, is_sponsored:)\n    @author = author\n    @like_count = like_count\n    @is_sponsored = is_sponsored\n  end\nend\n``` \n\nAnd getting the prioritized array would read like this:\n\n```ruby\nPriora.prioritize(unprioritized_array) == prioritized_array\n=\u003e true\n```\n\nUsing the `prioritize_by` class macro increases the readability of your code for the cost of flexibility.\nBy adopting this usage, priorities are declared in-class and Priora can fetch it implicitly.\nFor some cases this might be the right choice while for others the explicit style is more suitable.\n\n### Advantages Over Using Custom `sort` Or Implementing `\u003c=\u003e` \nOne might come up with the following snippet as an equivalent solution:\n\n```ruby\nunprioritized_array.sort { |a, b| [a.like_count, a.is_sponsored ? 1 : 0 ] \u003c=\u003e [b.like_count, b.is_sponsored ? 1 : 0] }.reverse == prioritized_array\n=\u003e true\n```\n\nWhich is, of course, correct. However, I find several issues with this code:\n* It is more verbose and prone to errors.\n* It declares the prioritization logic twice.\n* It handles the conversion of a boolean value (`true` / `false`) into a sortable value (`1` / `0`) inline,\nthus mixing levels of abstractions and confusing the potential reader.\n\nAnother possible alternative is implementing the spaceship operator (`\u003c=\u003e`) for `Post` instances,\nand then simply employ reverse sorting.\nI regard this approach as somewhat more elegant, but its main problem is that it assumes our sorting logic is always the same\nfor a given class, which is not always true.\nPriora solves this problem by supporting the explicit `by` parameter.\n\nI created Priora after having encountered a few scenarios in which I needed to get some collections prioritized in some\nspecific manner, and having to supply these explicit blocks again and again was quite annoying.\nI figured out a modest library solving this problem could be nice to have.  \n\n### Reverse Sorting, Extended: An Agenda\n\nPriora is based on the presumption that when we talk about a prioritized collection,\nwe often refer to the outcome of sorting it and then reversing the result.\nThis is because we naturally think about sorting in an ascending fashion, from small to large,\nwhile when we talk about \"top priorities\" we usually think of the largest items first.\n\n#### Directional Priorities\n\nObviously, this is not always true and some prioritization processes should give precedence to smaller items first;\nPriora supports this scenario as well.\nYou may change the prioritization direction for a specific priority:\n\n```ruby\nPriora.prioritize(unprioritized_array, by: [[like_count: :asc], :is_sponsored])\n=\u003e [low_like_count_sponsored, high_like_count_sponsored, high_like_count_unsponsored]\n```\n\nWe can see that the `Post` with the low `like_count` comes up first,\nhowever the two high `like_count` posts are prioritized by `is_sponsored`, so the sponsored `Post` comes up first.\n\nIf you have several priorities for which you wish to specify direction, you need to do so for each separately:\n```ruby\nPriora.prioritize(unprioritized_array, by: [[like_count: :asc], [is_sponsored: :asc]])\n=\u003e [low_like_count_sponsored, high_like_count_unsponsored, high_like_count_sponsored]\n```\n\n### Implicit Conversions\n\nAs you might have noted, Priora also takes care of converting non-sortable values,\nsuch as `true`, `false` or `nil`, into sortable values.\nBy default, it assumes that `true` is larger than `false` and that `nil` evaluates to `0`.  \n\nYou may override these implicit conversions with your own lambdas,\nas well as supply your own custom lambdas for other classes (and perhaps override their sorting logic!).\n\nFor example, if we wished to sort attributes of class `String` by their length,\nwe could configure `Priora` accordingly beforehand:\n\n```ruby\nPriora.configuration.add_conversion_lambda(String, lambda { |value| value.length })\n```\n\nConversion lambdas are also removable, should that need arise:\n\n```ruby\nPriora.configuration.remove_conversion_lambda(String)\n```\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'priora'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install priora\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo 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).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/eliav-lavi/priora.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feliav-lavi%2Fpriora","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feliav-lavi%2Fpriora","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feliav-lavi%2Fpriora/lists"}