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

WIP: Allow to pass a proc as fallback for conditional fallbacks #328

Closed
wants to merge 6 commits into from
Closed
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
31 changes: 24 additions & 7 deletions lib/mobility/plugins/fallbacks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,30 @@ def initialize(fallbacks_option)

def define_read(fallbacks)
define_method :read do |locale, fallback: true, **options|
return super(locale, options) if !fallback || options[:locale]

locales = fallback == true ? fallbacks[locale] : [locale, *fallback]
locales.each do |fallback_locale|
value = super(fallback_locale, options)
return value if Util.present?(value)
value = super(locale, options)
return value if !fallback || options[:locale] || Util.present?(value)

fallback_locales =
Util.array_wrap(
if fallback == true
if fallbacks.is_a?(Proc)
model.instance_exec(&fallbacks)
else
fallbacks[locale]
end
elsif fallback.is_a?(Proc)
model.instance_exec(&fallback)
else
fallback
end
)

fallback_locales.each do |fallback_locale|
fallback_value = super(fallback_locale.to_sym, options)
return fallback_value if Util.present?(fallback_value)
end

super(locale, options)
value
end
end

Expand All @@ -156,6 +171,8 @@ def convert_option_to_fallbacks(option)
Mobility.new_fallbacks(option)
elsif option == true
Mobility.new_fallbacks
elsif option.is_a?(Proc)
option
else
Hash.new { [] }
end
Expand Down
11 changes: 11 additions & 0 deletions lib/mobility/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ def presence(object)
object if present?(object)
end

# taken from https://github.com/rails/rails/blob/6-0-stable/activesupport/lib/active_support/core_ext/array/wrap.rb
def array_wrap(object)
if object.nil?
[]
elsif object.respond_to?(:to_ary)
object.to_ary || [object]
else
[object]
end
end

private

# Calls caller method on object if defined, otherwise yields to block
Expand Down
167 changes: 165 additions & 2 deletions spec/mobility/plugins/fallbacks_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,25 @@ def read(locale, **options)
"title" => {
:'de-DE' => "foo",
:ja => "フー",
:'pt' => ""
:'pt' => "",
:'cz' => "cz-foo"
}
}[attribute][locale]
end
end
Class.new(backend_subclass).include(described_class.new(fallbacks))
end
let(:object) { (stub_const 'MobilityModel', Class.new).include(Mobility).new }
let(:object) do
(stub_const 'MobilityModel', Class.new)
.include(Mobility)
.include(
Module.new do
def fallback_locale_method
'de-DE'
end
end
).new
end
subject { backend_class.new(object, "title") }

context "fallbacks is a hash" do
Expand Down Expand Up @@ -55,6 +66,158 @@ def read(locale, **options)
expect(subject.read(:"en-US", fallback: :ja)).to eq("フー")
end

it "uses array of locales passed in as value of fallback options when present" do
expect(subject.read(:"en-US", fallback: [:pl, :'cz'])).to eq("cz-foo")
end

it "passes options to getter in fallback locale" do
expect(subject.read(:'en-US', bar: true)).to eq("bar")
end

it "does not modify options passed in" do
options = { fallback: false }
subject.read(:"en-US", options)
expect(options).to eq({ fallback: false })
end
end

context "fallbacks is a proc returning a locale" do
let(:fallbacks) { proc { 'de-DE' } }

it "returns value when value is not nil" do
expect(subject.read(:ja)).to eq("フー")
end

it "falls through to fallback locale when value is nil" do
expect(subject.read(:"en-US")).to eq("foo")
end

it "falls through to fallback locale when value is blank" do
expect(subject.read(:pt)).to eq("foo")
end

it "falls through to fallback locale when fallback: true option is passed" do
expect(subject.read(:"en-US", fallback: true)).to eq("foo")
end

it "uses locale passed in as value of fallback option when present" do
expect(subject.read(:"en-US", fallback: :ja)).to eq("フー")
end

it "uses array of locales passed in as value of fallback options when present" do
expect(subject.read(:"en-US", fallback: [:pl, :'de-DE'])).to eq("foo")
end

it "passes options to getter in fallback locale" do
expect(subject.read(:'en-US', bar: true)).to eq("bar")
end

it "does not modify options passed in" do
options = { fallback: false }
subject.read(:"en-US", options)
expect(options).to eq({ fallback: false })
end
end

context "fallbacks is a proc returning nothing" do
let(:fallbacks) { proc {} }

it "returns value when value is not nil" do
expect(subject.read(:ja)).to eq("フー")
end

it "returns original value when value is nil" do
expect(subject.read(:"en-US")).to eq(nil)
end

it "returns original value when value is blank" do
expect(subject.read(:pt)).to eq("")
end

it "returns original value when value when fallback: true option is passed" do
expect(subject.read(:"en-US", fallback: true)).to eq(nil)
end

it "uses locale passed in as value of fallback option when present" do
expect(subject.read(:"en-US", fallback: :ja)).to eq("フー")
end

it "uses array of locales passed in as value of fallback options when present" do
expect(subject.read(:"en-US", fallback: [:pl, :'de-DE'])).to eq("foo")
end

it "passes options to getter in fallback locale" do
expect(subject.read(:'en-US', bar: true)).to eq("bar")
end

it "does not modify options passed in" do
options = { fallback: false }
subject.read(:"en-US", options)
expect(options).to eq({ fallback: false })
end
end

context "fallbacks is a proc returning an array" do
let(:fallbacks) { proc { [:pl, :'de-DE'] } }

it "returns value when value is not nil" do
expect(subject.read(:ja)).to eq("フー")
end

it "returns fallback value when value is nil" do
expect(subject.read(:"en-US")).to eq("foo")
end

it "returns fallback value when value is blank" do
expect(subject.read(:pt)).to eq("foo")
end

it "returns fallback value when value when fallback: true option is passed" do
expect(subject.read(:"en-US", fallback: true)).to eq("foo")
end

it "uses locale passed in as value of fallback option when present" do
expect(subject.read(:"en-US", fallback: :ja)).to eq("フー")
end

it "uses array of locales passed in as value of fallback options when present" do
expect(subject.read(:"en-US", fallback: [:pl, :'de-DE'])).to eq("foo")
end

it "passes options to getter in fallback locale" do
expect(subject.read(:'en-US', bar: true)).to eq("bar")
end

it "does not modify options passed in" do
options = { fallback: false }
subject.read(:"en-US", options)
expect(options).to eq({ fallback: false })
end
end

context "fallbacks is a proc calling a model method" do
let(:fallbacks) { proc { fallback_locale_method } }

it "returns value when value is not nil" do
expect(subject.read(:ja)).to eq("フー")
end

it "falls through to fallback locale when value is nil" do
expect(subject.read(:"en-US")).to eq("foo")
end

it "falls through to fallback locale when value is blank" do
expect(subject.read(:pt)).to eq("foo")
end

it "falls through to fallback locale when fallback: true option is passed" do
expect(subject.read(:"en-US", fallback: true)).to eq("foo")
end

it "uses locale passed in as value of fallback option when present" do
expect(subject.read(:"en-US", fallback: :ja)).to eq("フー")
end

it "uses array of locales passed in as value of fallback options when present" do
expect(subject.read(:"en-US", fallback: [:pl, :'de-DE'])).to eq("foo")
end
Expand Down