# Schnecke [![CircleCI](https://dl.circleci.com/status-badge/img/gh/prschmid/schnecke/tree/main.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/prschmid/schnecke/tree/main) A very simple gem to enable ActiveRecord models to have slugs. ## Installation Add this line to your application's Gemfile: ```ruby gem 'schnecke' ``` And then execute: $ bundle Or install it yourself as: $ gem install schnecke ## Usage Given a class `A` which has the attribute `name` and has a `slug` column defined, we can do the following: ```ruby class A include Schnecke slug :name end ``` This will take the value in `name` and automatically set the slug based on it. If the slug needs to be based on multiple attributes of a model, simply pass in the array of attributes as follows: ```ruby class A include Schnecke slug [:first_name, :last_name] end ``` ### Slug column By default it is assumed that the generated slug will be assigned to the `slug` attribute of the model. If one needs to place the slug in a different columm, this can be done by defining the `column` attribute: ```ruby class A include Schnecke slug :name, column: :some_other_column end ``` The above will place the generated slug in `some_other_column`. ### Slug Uniquness By default slugs are unique to the object that defines the slug. For example if we have the 2 objects, `A` and `B` as defined as below, then the slugs will be unique for all slugs for all type `A` objcets and all type `B` objects. ```ruby class A include Schnecke slug :name end class B include Schnecke slug :name end ``` This means that the slug `foo` can exists 2 times; once for any object of type `A` and once for any object of type `B`. Currently there is no way to create globally unique slugs. If this is something that is required, then something like [`friendly_id`](https://github.com/norman/friendly_id) might be more appropriate for your use case. ### Handling non-unique slugs If a duplicate slug is to be created, a number is automatically appended to the end of the slug. For example, if there is a slug `foo`, the second one would become `foo-2`, the third `foo-3`, and so forth. ### Defining a custom uniqueness scope There are times when we want slugs not be unique for all objects of type `A`, but rather for a smaller scope. For example, let's say we have a system with multiple `Accounts`, each containing `Record`s. If we want the slug for the `Record` to be unique only within the scope of an `account` we can do by providing the uniqueness scope when setting up the slug. ```ruby class Record include Schnecke slug :name, uniqueness: { scope: :account} belongs_to :account end ``` When we do this, this will let us have the same slug 'foo' for multiple `record` objects as long as they belong todifferent `accounts`. Note, we can also pass an array so that we can define the scope even more narrowly. For example: ```ruby class Tag include Schnecke slug :name, uniqueness: { scope: [:account, :record]} belongs_to :account belongs_to :record end ``` ### Advanced Usage If you need to change how the slug is generated, how duplicates are handled, etc., you can overwrite the methods in your class. For example to change how slugs are generated you can overwrite the `slugify` method. ```ruby class A include Schnecke slug :name # Overwrite the `slugify` method. In this case the slug will always be 'foo' def slugify(str) 'foo' end end ``` Note, by default the library will validate to ensure that the slug only contains lowercase alpphanumeric letters and '-' or '_'. If your new method changes the alloweable set of characters you can either disable this validation, or pass in your own validation pattern. ```ruby class A include Schnecke slug :name, require_format: false def slugify(str) 'This#Would/NormallyF@!l' end end class B include Schnecke slug :name, require_format: /\A[a-z]+\z/ def slugify(str) 'lettersonly' end end ``` The methods that can be overwritten are * `slugify(str)`: This is used to turn a string into a slug * `slugify_blank`: This is used when the slug returned is blank and we need some default slug generated * `slugify_duplicate(slug)`: This will take a slug generated by `slugify` and generate a unique version of it * `slug_concat(parts)`: This takes an array of slugs and combines them into 1 string For details, please see the actual code. ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 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). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/prschmid/schnecke. ## Acknowledgements This work is based on the [`slug`](https://github.com/bkoski/slug) gem. ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).