Skip to content

Commit

Permalink
Correctly use singular/plural versions of model names
Browse files Browse the repository at this point in the history
  • Loading branch information
spohlenz committed Sep 25, 2017
1 parent 9687f44 commit 370d01f
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 20 deletions.
45 changes: 32 additions & 13 deletions lib/trestle/model_name.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,57 @@ class ModelName

def initialize(klass)
@klass = klass

if klass.respond_to?(:model_name)
@name = klass.model_name
else
@name = ActiveModel::Name.new(klass)
end
@name = klass.respond_to?(:model_name) ? klass.model_name : ActiveModel::Name.new(klass)
end

def ==(other)
other.is_a?(self.class) && klass == other.klass
end

def to_s
human
singular
end

def singular(options={})
human(options)
human(default_singular, options)
end
alias_method :singularize, :singular

def plural(options={})
if klass.respond_to?(:lookup_ancestors) && klass.respond_to?(:i18n_scope)
human({ count: :many, default: human.pluralize }.merge(options))
if i18n_supported? && i18n_pluralizations_available?
human(default_plural, { count: :many }.merge(options))
else
human.pluralize
default_plural
end
end
alias_method :pluralize, :plural

def human(options={})
@name.human(options)
protected
# Default singular version if it cannot be determined from i18n
def default_singular
@name.name.demodulize.titleize
end

# Default plural version if it cannot be determined from i18n
def default_plural
singular.pluralize(I18n.locale)
end

# Safely delegates to ActiveModel::Name#human, catching exceptions caused by missing pluralizations
def human(default, options={})
@name.human(options.merge(default: default))
rescue I18n::InvalidPluralizationData
default
end

# Checks if the model can be translated by ActiveModel
def i18n_supported?
klass.respond_to?(:lookup_ancestors) && klass.respond_to?(:i18n_scope)
end

# Checks if multiple pluralization forms (e.g. zero/one/few/many/other) are available from i18n
def i18n_pluralizations_available?
@name.human(count: nil).is_a?(Hash)
end
end
end
8 changes: 8 additions & 0 deletions spec/support/i18n_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module I18nHelper
def with_translations(locale, translations)
I18n.backend.store_translations(locale, translations)
yield
ensure
I18n.reload!
end
end
52 changes: 45 additions & 7 deletions spec/trestle/model_name_spec.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
require 'spec_helper'

describe Trestle::ModelName do
let(:klass) { stub_const("TestPost", Class.new(ActiveRecord::Base)) }
include I18nHelper

subject(:name) { Trestle::ModelName.new(klass) }

let(:klass) { stub_const("TestPost", Class.new(ActiveRecord::Base)) }

it "converts to string" do
expect(name.to_s).to eq("Test post")
expect(name.to_s).to eq("Test Post")
end

it "delegates string methods to to_s" do
Expand All @@ -16,21 +19,56 @@
end

it "has a singular form" do
expect(name.singular).to eq("Test post")
expect(name.singularize).to eq("Test post")
expect(name.human).to eq("Test post")
expect(name.singular).to eq("Test Post")
expect(name.singularize).to eq("Test Post")
end

it "has a plural form" do
expect(name.plural).to eq("Test posts")
expect(name.pluralize).to eq("Test posts")
expect(name.plural).to eq("Test Posts")
expect(name.pluralize).to eq("Test Posts")
end

it "is equal to another ModelName with the same class" do
expect(name).to eq(Trestle::ModelName.new(klass))
expect(name).not_to eq(Trestle::ModelName.new(String))
end

context "both singular and plural i18n translations provided" do
it "uses the translations from i18n" do
with_translations(:en, activerecord: { models: { test_post: { one: "One Test Post", other: "Many Test Posts" } } }) do
expect(name.singular).to eq("One Test Post")
expect(name.plural).to eq("Many Test Posts")
end
end
end

context "model i18n translation provided" do
it "pluralizes based on singular version and inflection rules" do
with_translations(:en, activerecord: { models: { test_post: "One Test Post" } }) do
expect(name.singular).to eq("One Test Post")
expect(name.plural).to eq("One Test Posts")
end
end
end

context "plural i18n translation missing" do
it "pluralizes based on singular version and inflection rules" do
with_translations(:en, activerecord: { models: { test_post: { one: "One Test Post" } } }) do
expect(name.singular).to eq("One Test Post")
expect(name.plural).to eq("One Test Posts")
end
end
end

context "singular i18n translation missing" do
it "falls back to non-i18n singular version" do
with_translations(:en, activerecord: { models: { test_post: { other: "Many Test Posts" } } }) do
expect(name.singular).to eq("Test Post")
expect(name.plural).to eq("Many Test Posts")
end
end
end

context "plain ruby class" do
let(:klass) { stub_const("Article", Class.new) }

Expand Down

0 comments on commit 370d01f

Please sign in to comment.