Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure localized slug creates index for slug default locale sub-field when "localize: true" is specified #268

Merged
merged 5 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ AllCops:
Style/Documentation:
Enabled: false

Style/ModuleFunction:
EnforcedStyle: extend_self

inherit_from: .rubocop_todo.yml
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
## 6.0.1 (Next)
## 6.0.2 (Next)

* Your contribution here.

## 6.0.1 (2021/08/12)

* [#243](https://github.com/mongoid/mongoid-slug/pull/266): Ensure localized slugs index expected localized slug fields - [@onomated](https://github.com/onomated), [@johnnyshields](https://github.com/johnnyshields).
* [#265](https://github.com/mongoid/mongoid-slug/pull/265): Add Github Actions and remove Travis CI - [@johnnyshields](https://github.com/kailan).
* [#255](https://github.com/mongoid/mongoid-slug/pull/255): Use mongoid::config#models in rake task, resolves #247 - [@kailan](https://github.com/kailan).

## 6.0.0 (2018/09/17)
Expand Down
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Mongoid Slug will attempt to determine whether you want to find using the `slugs
```ruby
Book.fields['_id'].type
=> String

book = Book.find 'a-thousand-plateaus' # Finds by slugs
=> ...

Expand All @@ -61,8 +62,10 @@ end

Post.fields['_id'].type
=> String

post = Post.find 'a-thousand-plateaus' # Finds by slugs
=> ...

post = Post.find '50b1386a0482939864000001' # Finds by bson ids
=> ...
```
Expand Down Expand Up @@ -271,7 +274,8 @@ Specifying an array of custom reserved words will overwrite these defaults.

### Localize Slugs

The slugs can be localized:
The slugs can be localized. This feature is built upon Mongoid localized fields,
so fallbacks and localization works as documented in the Mongoid manual.

```ruby
class PageSlugLocalize
Expand All @@ -283,7 +287,28 @@ class PageSlugLocalize
end
```

This feature is built upon Mongoid localized fields, so fallbacks and localization works as documented in the Mongoid manual.
By specifying `localize: true`, the slug index will be created on the
[I18n.default_locale](http://guides.rubyonrails.org/i18n.html#the-public-i18n-api) field only.
For example, if `I18n.default_locale` is `:en`, the index will be generated as follows:

```ruby
slug :title, localize: true

# The following indexes is auto-generated:
index({ '_slugs.en' => 1 }, { unique: true, sparse: true })
```

If you are supporting multiple locales, you may specify the list of locales on which
to create indexes as an `Array`.

```ruby
slug :title, localize: [:fr, :es, :de]

# The following indexes are auto-generated:
index({ '_slugs.fr' => 1 }, { unique: true, sparse: true })
index({ '_slugs.es' => 1 }, { unique: true, sparse: true })
index({ '_slugs.de' => 1 }, { unique: true, sparse: true })
```

### Custom Find Strategies

Expand Down
6 changes: 3 additions & 3 deletions lib/mongoid/slug.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'mongoid'
require 'stringex'
require 'mongoid/slug/criteria'
require 'mongoid/slug/index'
require 'mongoid/slug/index_builder'
require 'mongoid/slug/unique_slug'
require 'mongoid/slug/slug_id_strategy'
require 'mongoid-compatibility'
Expand Down Expand Up @@ -84,8 +84,8 @@ def slug(*fields, &block)
field :_slugs, type: Array, localize: options[:localize]
alias_attribute :slugs, :_slugs

# Set index
index(*Mongoid::Slug::Index.build_index(slug_scope_key, slug_by_model_type)) unless embedded?
# Set indexes
Mongoid::Slug::IndexBuilder.build_indexes(self, slug_scope_key, slug_by_model_type, options[:localize]) unless embedded?

self.slug_url_builder = block_given? ? block : default_slug_url_builder

Expand Down
36 changes: 29 additions & 7 deletions lib/mongoid/slug/index.rb → lib/mongoid/slug/index_builder.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
module Mongoid
module Slug
module Index
# @param [ String or Symbol ] scope_key The optional scope key for the index
# @param [ Boolean ] by_model_type Whether or not
module IndexBuilder
extend self

# Creates indexes on a document for a given slug scope
#
# @param [ Mongoid::Document ] doc The document on which to create the index(es)
# @param [ String or Symbol ] scope_key The optional scope key for the index(es)
# @param [ Boolean ] by_model_type Whether or not to use single table inheritance
# @param [ Boolean or Array ] localize The locale for localized index field
#
# @return [ Array(Hash, Hash) ] the indexable fields and index options.
def self.build_index(scope_key = nil, by_model_type = false)
def build_indexes(doc, scope_key = nil, by_model_type = false, locales = nil)
if locales.is_a?(Array)
locales.each { |locale| build_index(doc, scope_key, by_model_type, locale) }
else
build_index(doc, scope_key, by_model_type, locales)
end
end

private

def build_index(doc, scope_key = nil, by_model_type = false, locale = nil)
# The order of field keys is intentional.
# See: http://docs.mongodb.org/manual/core/index-compound/
fields = {}
fields[:_type] = 1 if by_model_type
fields[:_type] = 1 if by_model_type
fields[scope_key] = 1 if scope_key
fields[:_slugs] = 1

locale = ::I18n.default_locale if locale.is_a?(TrueClass)
if locale
fields[:"_slugs.#{locale}"] = 1
else
fields[:_slugs] = 1
end

# By design, we use the unique index constraint when possible to enforce slug uniqueness.
# When migrating legacy data to Mongoid slug, the _slugs field may be null on many records,
Expand All @@ -38,7 +60,7 @@ def self.build_index(scope_key = nil, by_model_type = false)
options[:sparse] = true
end

[fields, options]
doc.index(fields, options)
end
end
end
Expand Down
105 changes: 105 additions & 0 deletions spec/mongoid/index_builder_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
require 'spec_helper'

describe Mongoid::Slug::IndexBuilder do
let(:doc) { Class.new.include(Mongoid::Document) }
let(:scope_key) { nil }
let(:by_model_type) { false }
let(:default_locale) { :en }
let(:locales) { nil }

subject do
if Mongoid::Compatibility::Version.mongoid3?
doc.index_options.to_a
else
doc.index_specifications.map { |spec| [spec.key, spec.options] }
end
end

before do
allow(I18n).to receive(:default_locale).and_return(default_locale)
Mongoid::Slug::IndexBuilder.build_indexes(doc, scope_key, by_model_type, locales)
end

context 'when scope_key is set' do
let(:scope_key) { :foo }

context 'when by_model_type is true' do
let(:by_model_type) { true }

it { is_expected.to eq [[{ _slugs: 1, foo: 1, _type: 1 }, {}]] }
end

context 'when by_model_type is false' do
it { is_expected.to eq [[{ _slugs: 1, foo: 1 }, {}]] }
end

context 'when locale is set' do
let(:default_locale) { :de }
let(:locales) { true }

it { is_expected.to eq [[{ :'_slugs.de' => 1, foo: 1 }, {}]] }
end

context 'when locale is not set' do
it { is_expected.to eq [[{ _slugs: 1, foo: 1 }, {}]] }
end

context 'when locales is an Array' do
let(:locales) { %i[es de fr] }

it do
is_expected.to eq [[{ :'_slugs.es' => 1, foo: 1 }, {}],
[{ :'_slugs.de' => 1, foo: 1 }, {}],
[{ :'_slugs.fr' => 1, foo: 1 }, {}]]
end
end
end

context 'when scope_key is not set' do
context 'when by_model_type is true' do
let(:by_model_type) { true }

it { is_expected.to eq [[{ _slugs: 1, _type: 1 }, {}]] }
end

context 'when by_model_type is false' do
it { is_expected.to eq [[{ _slugs: 1 }, { unique: true, sparse: true }]] }
end

context 'when locales is true' do
let(:locales) { true }

it { is_expected.to eq [[{ :'_slugs.en' => 1 }, { unique: true, sparse: true }]] }
end

context 'when locales is a String' do
let(:locales) { 'de' }

it { is_expected.to eq [[{ :'_slugs.de' => 1 }, { unique: true, sparse: true }]] }
end

context 'when locales is a Symbol' do
let(:locales) { :de }

it { is_expected.to eq [[{ :'_slugs.de' => 1 }, { unique: true, sparse: true }]] }
end

context 'when locales is an Array' do
let(:locales) { %i[es de fr] }

it do
is_expected.to eq [[{ :'_slugs.es' => 1 }, { unique: true, sparse: true }],
[{ :'_slugs.de' => 1 }, { unique: true, sparse: true }],
[{ :'_slugs.fr' => 1 }, { unique: true, sparse: true }]]
end
end

context 'when locale is set and by_model_type is true' do
let(:locales) { true }
let(:default_locale) { :fr }
let(:by_model_type) { true }

it { is_expected.to eq [[{ :'_slugs.fr' => 1, _type: 1 }, {}]] }
end
end
end
33 changes: 0 additions & 33 deletions spec/mongoid/index_spec.rb

This file was deleted.