Skip to content

Commit

Permalink
Merge pull request #67 from jcypret/model-level-config
Browse files Browse the repository at this point in the history
Add support for model-level config
  • Loading branch information
jcypret authored Oct 30, 2019
2 parents 6ca4b64 + 4daecf1 commit 98df76e
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 1.3.0 (2019-10-29)
- Add support for model-level config ([#67](https://github.com/jcypret/hashid-rails/pull/67))

## 1.2.2 (2018-07-29)
### Fixed
- Handle exception raised when using a letter-only alphabet and attempting to
Expand Down
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ model.hashid
#=> "yLA6m0oM"
```

Additionally, the `to_param` method is overriden to use hashid instead of id.
Additionally, the `to_param` method is overridden to use hashid instead of id.
This means methods that take advantage of implicit ID will automatically work
with hashids.

Expand Down Expand Up @@ -94,8 +94,9 @@ default options.

```ruby
Hashid::Rails.configure do |config|
# The salt to use for generating hashid. Prepended with table name.
# The salt to use for generating hashid. Prepended with pepper (table name).
config.salt = ""
config.pepper = table_name

# The minimum length of generated hashids
config.min_hash_length = 6
Expand All @@ -113,6 +114,22 @@ Hashid::Rails.configure do |config|
end
```

### Model-Level Config

You can also customize the hashid configuration at the model level.
`hashid_config` supports all the same options as the `Hashid::Rails.configure`
block and allows for each model to have a different config. This can be useful
for setting a custom salt/pepper. For instance, the pepper defaults to the table
name, so if you rename the table, you can keep the same hashids by setting the
pepper to the old table name.

```ruby
class Model < ActiveRecord::Base
include Hashid::Rails
hashid_config pepper: "old_table_name"
end
```

## Upgrading from Pre-1.0

The 1.0 release of this gem introduced hashid signing to prevent
Expand Down
25 changes: 21 additions & 4 deletions lib/hashid/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ def hashid
alias to_param hashid

module ClassMethods
def hashid_config(options = {})
config = Hashid::Rails.configuration.dup
config.pepper = table_name
options.each do |attr, value|
config.public_send("#{attr}=", value)
end
@hashid_configuration = config
end

def hashid_configuration
@hashid_configuration || hashid_config
end

def reset_hashid_config
@hashid_configuration = nil
end

def relation
super.tap { |r| r.extend ClassMethods }
end
Expand Down Expand Up @@ -71,7 +88,7 @@ def find(*ids)
uniq_ids = ids.flatten.compact.uniq
uniq_ids = uniq_ids.first unless expects_array || uniq_ids.size > 1

if Hashid::Rails.configuration.override_find
if hashid_configuration.override_find
super(decode_id(uniq_ids, fallback: true))
else
super
Expand All @@ -89,11 +106,11 @@ def find_by_hashid!(hashid)
private

def hashids
Hashids.new(*Hashid::Rails.configuration.for_table(table_name))
Hashids.new(*hashid_configuration.to_args)
end

def hashid_encode(id)
if Hashid::Rails.configuration.sign_hashids
if hashid_configuration.sign_hashids
hashids.encode(HASHID_TOKEN, id)
else
hashids.encode(id)
Expand All @@ -104,7 +121,7 @@ def hashid_decode(id, fallback:)
fallback_value = fallback ? id : nil
decoded_hashid = hashids.decode(id.to_s)

if Hashid::Rails.configuration.sign_hashids
if hashid_configuration.sign_hashids
valid_hashid?(decoded_hashid) ? decoded_hashid.last : fallback_value
else
decoded_hashid.first || fallback_value
Expand Down
6 changes: 4 additions & 2 deletions lib/hashid/rails/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ module Hashid
module Rails
class Configuration
attr_accessor :salt,
:pepper,
:min_hash_length,
:alphabet,
:override_find,
:sign_hashids

def initialize
@salt = ""
@pepper = ""
@min_hash_length = 6
@alphabet = "abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
Expand All @@ -19,8 +21,8 @@ def initialize
@sign_hashids = true
end

def for_table(table_name)
["#{table_name}#{salt}", min_hash_length, alphabet]
def to_args
["#{pepper}#{salt}", min_hash_length, alphabet]
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/hashid/rails/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

module Hashid
module Rails
VERSION = "1.2.2"
VERSION = "1.3.0"
end
end
70 changes: 68 additions & 2 deletions spec/hashid/rails_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
require "spec_helper"

describe Hashid::Rails do
before(:each) { Hashid::Rails.reset }
before(:each) do
Hashid::Rails.reset
FakeModel.reset_hashid_config
Post.reset_hashid_config
Comment.reset_hashid_config
end

describe "#hashid" do
it "returns model ID encoded as hashid" do
Expand Down Expand Up @@ -284,13 +289,15 @@
Hashid::Rails.configure do |config|
config.override_find = true
end
result = FakeModel.find(model.hashid)
FakeModel.reset_hashid_config

result = FakeModel.find(model.hashid)
expect(result).to eq(model)

Hashid::Rails.configure do |config|
config.override_find = false
end
FakeModel.reset_hashid_config

expect { FakeModel.find(model.hashid) }
.to raise_error(ActiveRecord::RecordNotFound)
Expand Down Expand Up @@ -398,6 +405,65 @@
expect(config.sign_hashids).to eq(false)
end
end

it "inherits default configuration" do
config = FakeModel.hashid_configuration

aggregate_failures "default config" do
expect(config.salt).to eq("")
expect(config.pepper).to eq(FakeModel.table_name)
expect(config.min_hash_length).to eq(6)
expect(config.alphabet).to eq(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
)
expect(config.override_find).to eq(true)
expect(config.sign_hashids).to eq(true)
end
end

it "supports model-specific config" do
FakeModel.hashid_config(
salt: "shhh",
pepper: "achoo",
min_hash_length: 7,
alphabet: "XYZ",
override_find: false,
sign_hashids: false
)

# model-level has new config
config = FakeModel.hashid_configuration
aggregate_failures "model-level config" do
expect(config.salt).to eq("shhh")
expect(config.pepper).to eq("achoo")
expect(config.min_hash_length).to eq(7)
expect(config.alphabet).to eq("XYZ")
expect(config.override_find).to eq(false)
expect(config.sign_hashids).to eq(false)
end

# default config does not change
config = Hashid::Rails.configuration
aggregate_failures "default config" do
expect(config.salt).to eq("")
expect(config.pepper).to eq("")
expect(config.min_hash_length).to eq(6)
expect(config.alphabet).to eq(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
)
expect(config.override_find).to eq(true)
expect(config.sign_hashids).to eq(true)
end
end

it "supports different configs for each model" do
Post.hashid_config(pepper: "achoo")
Comment.hashid_config(pepper: "gazoontite")

expect(FakeModel.hashid_configuration.pepper).to eq(FakeModel.table_name)
expect(Post.hashid_configuration.pepper).to eq("achoo")
expect(Comment.hashid_configuration.pepper).to eq("gazoontite")
end
end

describe ".reset" do
Expand Down

0 comments on commit 98df76e

Please sign in to comment.