diff --git a/lib/mobility.rb b/lib/mobility.rb index 83466c559..3bb90b29b 100644 --- a/lib/mobility.rb +++ b/lib/mobility.rb @@ -165,12 +165,16 @@ def config # (see Mobility::Configuration#default_accessor_locales) # @!method default_accessor_locales - %w[accessor_method default_fallbacks default_backend default_accessor_locales].each do |method_name| + %w[accessor_method default_backend default_accessor_locales].each do |method_name| define_method method_name do config.public_send(method_name) end end + define_method :default_fallbacks do |*args| + config.public_send(:default_fallbacks, *args) + end + # Configure Mobility # @yield [Mobility::Configuration] Mobility configuration def configure diff --git a/lib/mobility/attributes.rb b/lib/mobility/attributes.rb index 5e2e38cf8..2f997623b 100644 --- a/lib/mobility/attributes.rb +++ b/lib/mobility/attributes.rb @@ -166,13 +166,6 @@ def initialize(method, *attributes_, **options_) end end - def include_backend_modules(backend_class, options) - backend_class.include(Backend::Cache) unless options[:cache] == false - backend_class.include(Backend::Dirty.for(options[:model_class])) if options[:dirty] - backend_class.include(Backend::Fallbacks) if options[:fallbacks] - backend_class.include(FallthroughAccessors.new(attributes)) if options[:fallthrough_accessors] - end - # Add this attributes module to shared {Mobility::Wrapper} and setup model # with backend setup block (see {Mobility::Backend::Setup#setup_model}). # @param model_class [Class] Class of model @@ -189,6 +182,14 @@ def each &block private + # Include backend modules depending on value of options. + def include_backend_modules(backend_class, options) + backend_class.include(Backend::Cache) unless options[:cache] == false + backend_class.include(Backend::Dirty.for(options[:model_class])) if options[:dirty] + backend_class.include(Backend::Fallbacks) unless options[:fallbacks] == false + backend_class.include(FallthroughAccessors.new(attributes)) if options[:fallthrough_accessors] + end + def define_backend(attribute) _backend_class, _options = backend_class, options define_method Backend.method_name(attribute) do diff --git a/lib/mobility/backend.rb b/lib/mobility/backend.rb index 3a42fdca0..65b98928a 100644 --- a/lib/mobility/backend.rb +++ b/lib/mobility/backend.rb @@ -73,12 +73,9 @@ module Backend # @!macro [new] backend_constructor # @param model Model on which backend is defined # @param [String] attribute Backend attribute - # @option backend_options [Hash] fallbacks Fallbacks hash - def initialize(model, attribute, **backend_options) + def initialize(model, attribute, **_) @model = model @attribute = attribute - fallbacks = backend_options[:fallbacks] - @fallbacks = I18n::Locale::Fallbacks.new(fallbacks) if fallbacks.is_a?(Hash) end # @!macro [new] backend_reader diff --git a/lib/mobility/backend/fallbacks.rb b/lib/mobility/backend/fallbacks.rb index c1fdf564a..9dadbdda1 100644 --- a/lib/mobility/backend/fallbacks.rb +++ b/lib/mobility/backend/fallbacks.rb @@ -76,6 +76,20 @@ class Post #=> "Mobilité" =end module Fallbacks + # @!macro [new] backend_constructor + # @param model Model on which backend is defined + # @param [String] attribute Backend attribute + # @option backend_options [Hash] fallbacks Fallbacks hash + def initialize(model, attributes, **backend_options) + super + @fallbacks = + if (fallbacks = backend_options[:fallbacks]).is_a?(Hash) + Mobility.default_fallbacks(fallbacks) + elsif fallbacks == true + Mobility.default_fallbacks + end + end + # @!group Backend Accessors # @!macro backend_reader # @param [Boolean,Symbol,Array] fallback @@ -86,18 +100,15 @@ def read(locale, fallback: nil, **options) warn "You passed an option with key 'fallbacks', which will be ignored. Did you mean 'fallback'?" end - return super if fallback == false - (fallback ? [locale, *fallback] : fallbacks[locale]).detect do |locale| - value = super(locale, **options) + return super if fallback == false || fallbacks.nil? + (fallback ? [locale, *fallback] : fallbacks[locale]).detect do |fallback_locale| + value = super(fallback_locale, **options) break value if value.present? end end private - - def fallbacks - @fallbacks ||= Mobility.default_fallbacks - end + attr_reader :fallbacks end end end diff --git a/lib/mobility/configuration.rb b/lib/mobility/configuration.rb index 145d80f8b..44430f725 100644 --- a/lib/mobility/configuration.rb +++ b/lib/mobility/configuration.rb @@ -11,7 +11,10 @@ class Configuration # Default fallbacks instance # @return [I18n::Locale::Fallbacks] - attr_accessor :default_fallbacks + def default_fallbacks(fallbacks = {}) + @default_fallbacks.call(fallbacks) + end + attr_writer :default_fallbacks # Default backend to use (can be symbol or actual backend class) # @return [Symbol,Class] @@ -31,7 +34,7 @@ def default_accessor_locales def initialize @accessor_method = :translates - @default_fallbacks = I18n::Locale::Fallbacks.new + @default_fallbacks = lambda { |fallbacks| I18n::Locale::Fallbacks.new(fallbacks) } @default_accessor_locales = lambda { I18n.available_locales } end end diff --git a/spec/mobility/attributes_spec.rb b/spec/mobility/attributes_spec.rb index 25a0131a1..8352fde2d 100644 --- a/spec/mobility/attributes_spec.rb +++ b/spec/mobility/attributes_spec.rb @@ -45,12 +45,12 @@ describe "cache" do it "includes Backend::Cache into backend when options[:cache] is not false" do expect(backend_klass).to receive(:include).with(Mobility::Backend::Cache) - Article.include described_class.new(:accessor, "title", { backend: backend_klass }) + Article.include described_class.new(:accessor, "title", { backend: backend_klass, fallbacks: false }) end it "does not include Backend::Cache into backend when options[:cache] is false" do expect(backend_klass).not_to receive(:include).with(Mobility::Backend::Cache) - Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false }) + Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false, fallbacks: false }) end end @@ -62,6 +62,7 @@ Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false, + fallbacks: false, dirty: true, fallthrough_accessors: false, model_class: Article @@ -70,7 +71,7 @@ it "does not include Backend::Model::Dirty into backend when options[:dirty] is falsey" do expect(backend_klass).not_to receive(:include).with(Mobility::Backend::ActiveModel::Dirty) - Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false, model_class: Article }) + Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false, fallbacks: false, model_class: Article }) end end @@ -85,6 +86,7 @@ Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false, + fallbacks: false, dirty: true, fallthrough_accessors: false, model_class: Article @@ -93,20 +95,20 @@ it "does not include Backend::Sequel::Dirty into backend when options[:dirty] is falsey" do expect(backend_klass).not_to receive(:include).with(Mobility::Backend::Sequel::Dirty) - Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false, model_class: Article }) + Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false, fallbacks: false, model_class: Article }) end end end describe "fallbacks" do - it "includes Backend::Fallbacks into backend when options[:fallbacks] is truthy" do + it "includes Backend::Fallbacks into backend when options[:fallbacks] is not false" do expect(backend_klass).to receive(:include).with(Mobility::Backend::Fallbacks) - Article.include described_class.new(:accessor, "title", { backend: backend_klass, fallbacks: true, cache: false }) + Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false }) end - it "does not include Backend::Fallbacks into backend when options[:fallbacks] is falsey" do + it "does not include Backend::Fallbacks into backend when options[:fallbacks] is false" do expect(backend_klass).not_to receive(:include).with(Mobility::Backend::Fallbacks) - Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false }) + Article.include described_class.new(:accessor, "title", { backend: backend_klass, cache: false, fallbacks: false }) end end diff --git a/spec/mobility/backend/fallbacks_spec.rb b/spec/mobility/backend/fallbacks_spec.rb index 8dec7fa4f..5aed51be3 100644 --- a/spec/mobility/backend/fallbacks_spec.rb +++ b/spec/mobility/backend/fallbacks_spec.rb @@ -10,7 +10,7 @@ def read(locale, **options) { "title" => { :'de-DE' => "foo", - :'jp' => "フー", + :ja => "フー", :'pt' => "" } }[attribute][locale] @@ -21,39 +21,65 @@ def read(locale, **options) end let(:object) { (stub_const 'MobilityModel', Class.new).include(Mobility).new } - subject do - backend_class.new(object, "title", fallbacks: { :'en-US' => 'de-DE', :pt => 'de-DE' }) - end + context "fallbacks is a hash" do + subject do + backend_class.new(object, "title", fallbacks: { :'en-US' => 'de-DE', :pt => 'de-DE' }) + end - it "returns value when value is not nil" do - expect(subject.read(:"jp")).to eq("フー") - end + 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 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 value is blank" do + expect(subject.read(:pt)).to eq("foo") + end - it "returns nil when no fallback is found" do - expect(subject.read(:"fr")).to eq(nil) - end + it "returns nil when no fallback is found" do + expect(subject.read(:"fr")).to eq(nil) + end - it "returns nil when fallbacks: false option is passed" do - expect(subject.read(:"en-US", fallback: false)).to eq(nil) - end + it "returns nil when fallbacks: false option is passed" do + expect(subject.read(:"en-US", fallback: false)).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 locale passed in as value of fallback option when present" do - expect(subject.read(:"en-US", fallback: :jp)).to eq("フー") + it "uses array of locales passed in as value of fallback options when present" do + expect(subject.read(:"en-US", fallback: [:es, :'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 end - it "uses array of locales passed in as value of fallback options when present" do - expect(subject.read(:"en-US", fallback: [:es, :'de-DE'])).to eq("foo") + context "fallbacks is true" do + subject do + backend_class.new(object, "title", fallbacks: true) + end + + it "uses default fallbacks" do + original_default_locale = I18n.default_locale + I18n.default_locale = :ja + expect(subject.read(:"en-US")).to eq("フー") + I18n.default_locale = original_default_locale + end end - it "passes options to getter in fallback locale" do - expect(subject.read(:'en-US', bar: true)).to eq("bar") + context "fallbacks is falsey" do + subject { backend_class.new(object, "title") } + + it "does not use fallbacks" do + original_default_locale = I18n.default_locale + I18n.default_locale = :ja + expect(subject.read(:"en-US")).to eq(nil) + I18n.default_locale = original_default_locale + end end end diff --git a/spec/mobility/backend_spec.rb b/spec/mobility/backend_spec.rb index 41640db30..444d6855b 100644 --- a/spec/mobility/backend_spec.rb +++ b/spec/mobility/backend_spec.rb @@ -21,19 +21,6 @@ end end - context "with options" do - subject { MyBackend.new(model, attribute, options) } - let(:options) { { foo: "bar" } } - - context "with fallbacks" do - let(:options) { { fallbacks: { :'en-US' => 'de-DE' } } } - - it "sets @fallbacks variable" do - expect(subject.instance_variable_get(:'@fallbacks')).to eq(I18n::Locale::Fallbacks.new(:'en-US' => 'de-DE')) - end - end - end - describe ".setup" do before do MyBackend.class_eval do