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

User super: true to access overriden accessors #62

Merged
merged 5 commits into from
Jul 30, 2017
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 lib/mobility/attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,21 @@ def define_backend(attribute)

def define_reader(attribute)
define_method attribute do |locale: Mobility.locale, **options|
return super() if options.delete(:super)
Mobility.enforce_available_locales!(locale)
mobility_backend_for(attribute).read(locale.to_sym, options)
end

define_method "#{attribute}?" do |locale: Mobility.locale, **options|
return super() if options.delete(:super)
Mobility.enforce_available_locales!(locale)
mobility_backend_for(attribute).read(locale.to_sym, options).present?
end
end

def define_writer(attribute)
define_method "#{attribute}=" do |value, locale: Mobility.locale, **options|
return super(value) if options.delete(:super)
Mobility.enforce_available_locales!(locale)
mobility_backend_for(attribute).write(locale.to_sym, value, options)
end
Expand Down
6 changes: 3 additions & 3 deletions lib/mobility/backend/sequel/hash_valued.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ def write(locale, value, **_)

# @!group Cache Methods
def translations
model.send("#{attribute}_before_mobility")
model[attribute.to_sym]
end

setup do |attributes|
method_overrides = Module.new do
define_method :initialize_set do |values|
attributes.each { |attribute| send(:"#{attribute}_before_mobility=", {}) }
attributes.each { |attribute| self[attribute.to_sym] = {} }
super(values)
end
define_method :before_validation do
attributes.each do |attribute|
send("#{attribute}_before_mobility").delete_if { |_, v| v.blank? }
self[attribute.to_sym].delete_if { |_, v| v.blank? }
end
super()
end
Expand Down
6 changes: 3 additions & 3 deletions lib/mobility/backend/sequel/serialized.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Post < Sequel::Model
post.save
post.deserialized_values[:title] # get deserialized value
#=> {:en=>"foo", :ja=>"あああ"}
post.title_before_mobility # get serialized value
post.title(super: true) # get serialized value
#=> "---\n:en: foo\n:ja: \"あああ\"\n"

=end
Expand Down Expand Up @@ -67,7 +67,7 @@ def self.configure(options)

method_overrides = Module.new do
define_method :initialize_set do |values|
attributes.each { |attribute| send(:"#{attribute}_before_mobility=", {}.send(:"to_#{format}")) }
attributes.each { |attribute| self[attribute.to_sym] = {}.send(:"to_#{format}") }
super(values)
end
end
Expand Down Expand Up @@ -108,7 +108,7 @@ def deserialize_value(column, value)
end

def serialized_value
model.send("#{attribute}_before_mobility")
model[attribute.to_sym]
end
end
end
Expand Down
47 changes: 32 additions & 15 deletions lib/mobility/locale_accessors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,42 @@ def self.apply(attributes, option)
# @param [String] One or more attribute names
# @param [Array<Symbol>] Locales
def initialize(*attribute_names, locales: I18n.available_locales)
warning_message = "locale passed as option to locale accessor will be ignored".freeze

attribute_names.each do |name|
locales.each do |locale|
normalized_locale = Mobility.normalize_locale(locale)
define_method "#{name}_#{normalized_locale}" do |**options|
warn warning_message if options.delete(:locale)
Mobility.with_locale(locale) { send(name, options) }
end
define_method "#{name}_#{normalized_locale}?" do |**options|
warn warning_message if options.delete(:locale)
Mobility.with_locale(locale) { send("#{name}?", options) }
end
define_method "#{name}_#{normalized_locale}=" do |value, **options|
warn warning_message if options.delete(:locale)
Mobility.with_locale(locale) { send("#{name}=", value, options) }
end
define_reader(name, locale)
define_writer(name, locale)
end
end
end

private

def define_reader(name, locale)
warning_message = "locale passed as option to locale accessor will be ignored".freeze
normalized_locale = Mobility.normalize_locale(locale)

define_method "#{name}_#{normalized_locale}" do |**options|
return super() if options.delete(:super)
warn warning_message if options.delete(:locale)
Mobility.with_locale(locale) { send(name, options) }
end

define_method "#{name}_#{normalized_locale}?" do |**options|
return super() if options.delete(:super)
warn warning_message if options.delete(:locale)
Mobility.with_locale(locale) { send("#{name}?", options) }
end
end

def define_writer(name, locale)
warning_message = "locale passed as option to locale accessor will be ignored".freeze
normalized_locale = Mobility.normalize_locale(locale)

define_method "#{name}_#{normalized_locale}=" do |value, **options|
return super(value) if options.delete(:super)
warn warning_message if options.delete(:locale)
Mobility.with_locale(locale) { send("#{name}=", value, options) }
end
end
end
end
8 changes: 4 additions & 4 deletions lib/mobility/sequel/column_changes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module Mobility
module Sequel
=begin

Internal class used to force Sequel model to notice changes when +mobility_set+
is called.
Internal class used to force Sequel model to notice changes when mobility
setter method is called.

=end
class ColumnChanges < Module
Expand All @@ -12,8 +12,8 @@ def initialize(attributes)
@attributes = attributes

attributes.each do |attribute|
define_method "#{attribute}=" do |value, **options|
if send(attribute) != value
define_method "#{attribute}=".freeze do |value, **options|
if !options[:super] && send(attribute) != value
locale = options[:locale] || Mobility.locale
column = attribute.to_sym
column_with_locale = :"#{attribute}_#{Mobility.normalize_locale(locale)}"
Expand Down
4 changes: 0 additions & 4 deletions lib/mobility/translates.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ module Translates
def mobility_#{method}(*args, **options, &block)
attributes = Attributes.new(:#{method}, *args, options)
attributes.backend.instance_eval(&block) if block_given?
attributes.each do |attribute|
alias_method "\#{attribute}_before_mobility", attribute if method_defined?(attribute) && #{%w[accessor reader].include? method}
alias_method "\#{attribute}_before_mobility=", "\#{attribute}=" if method_defined?("\#{attribute}=") && #{%w[accessor writer].include? method}
end
include attributes
end
EOM
Expand Down
41 changes: 38 additions & 3 deletions spec/mobility/attributes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@
end

describe "defining getters and setters" do
let(:model) { double("model") }
before do
model_double = model
mod = Module.new do
define_method :title do
model_double.title
end

define_method :title? do
model_double.title?
end

define_method :title= do |value|
model_double.title = value
end
end
Article.include mod
end
let(:article) { Article.new }

shared_examples_for "reader" do
Expand Down Expand Up @@ -128,6 +146,16 @@
expect(backend).to receive(:read).with(:de, someopt: "someval").and_return("foo")
expect(article.title(someopt: "someval")).to eq("foo")
end

it "calls original getter when super: true passed as option" do
expect(model).to receive("title").and_return("foo")
expect(article.title(super: true)).to eq("foo")
end

it "calls original presence method when super: true passed as option" do
expect(model).to receive("title?").and_return(true)
expect(article.title?(super: true)).to eq(true)
end
end

shared_examples_for "writer" do
Expand All @@ -142,6 +170,11 @@
expect(backend).to receive(:write).with(:de, "foo", someopt: "someval").and_return("foo")
expect(article.send(:title=, "foo", someopt: "someval")).to eq("foo")
end

it "calls original setter when super: true passed as option" do
expect(model).to receive("title=").with("foo")
article.send(:title=, "foo", super: true)
end
end

describe "method = :accessor" do
Expand All @@ -156,8 +189,9 @@

it_behaves_like "reader"

it "does not define writer" do
expect { article.title = "foo" }.to raise_error(NoMethodError)
it "calls original method" do
expect(model).to receive(:title=).once.with("foo")
article.title = "foo"
end
end

Expand All @@ -167,7 +201,8 @@
it_behaves_like "writer"

it "does not define reader" do
expect { article.title }.to raise_error(NoMethodError)
expect(model).to receive(:title).once.and_return("model foo")
expect(article.title).to eq("model foo")
end
end

Expand Down
2 changes: 1 addition & 1 deletion spec/mobility/backend/sequel/hstore_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
backend = post.mobility_backend_for("title")
backend.write(:en, { foo: :bar } )
post.save
expect(post.title_before_mobility.to_hash).to eq({ "en" => "{:foo=>:bar}" })
expect(post[:title].to_hash).to eq({ "en" => "{:foo=>:bar}" })
end
end
end if Mobility::Loaded::Sequel
2 changes: 1 addition & 1 deletion spec/mobility/backend/sequel/jsonb_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
backend = post.mobility_backend_for("title")
backend.write(:en, { foo: :bar } )
post.save
expect(post.title_before_mobility).to eq({ "en" => { "foo" => "bar" }})
expect(post[:title]).to eq({ "en" => { "foo" => "bar" }})
end

it "stores integer values" do
Expand Down
4 changes: 2 additions & 2 deletions spec/mobility/backend/sequel/serialized_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
backend = post.mobility_backend_for("title")
backend.write(:en, { foo: :bar } )
post.save
expect(post.title_before_mobility).to eq({ en: "{:foo=>:bar}" }.to_yaml)
expect(post[:title]).to eq({ en: "{:foo=>:bar}" }.to_yaml)
end
end

Expand Down Expand Up @@ -51,7 +51,7 @@
backend = post.mobility_backend_for("title")
backend.write(:en, { foo: :bar } )
post.save
expect(post.title_before_mobility).to eq({ en: "{:foo=>:bar}" }.to_json)
expect(post[:title]).to eq({ en: "{:foo=>:bar}" }.to_json)
end
end

Expand Down
32 changes: 32 additions & 0 deletions spec/mobility/locale_accessors_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,38 @@ def title=(_value, **_); end
end
end
end

describe "super: true" do
it "calls super of locale accessor method" do
spy = double("model")
mod = Module.new do
define_method :title_en do
spy.title_en
end
define_method :title_en? do
spy.title_en?
end
define_method :title_en= do |value|
spy.title_en = value
end
end
base_model_class.include mod
base_model_class.include described_class.new(:title)

instance = base_model_class.new

aggregate_failures do
expect(spy).to receive(:title_en).and_return("model foo")
instance.title_en(super: true)

expect(spy).to receive(:title_en?).and_return(true)
instance.title_en?(super: true)

expect(spy).to receive(:title_en=).with("model foo")
instance.send(:title_en=, "model foo", super: true)
end
end
end
end

describe ".apply" do
Expand Down
73 changes: 0 additions & 73 deletions spec/mobility/translates_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,78 +28,5 @@ def self.each &block; end
foo("bar")
end
end

describe "aliasing attribute getter/setter methods" do
let(:attributes) do
_attribute_names = attribute_names
Module.new do
define_singleton_method :each do |&block|
_attribute_names.each &block
end
end
end
before { allow(Mobility::Attributes).to receive(:new).and_return(attributes) }

describe ".mobility_accessor" do
it "aliases getter and setter methods if defined to <attribute>_before_mobility and <attribute_before_mobility=" do
MyClass.class_eval do
def title
"foo"
end

def title=(value)
value
end
end
MyClass.mobility_accessor *attribute_names
expect(MyClass.new.title).to eq("foo")
expect(MyClass.new.title_before_mobility).to eq("foo")
expect(MyClass.new.title="baz").to eq("baz")
expect(MyClass.new.title_before_mobility="baz").to eq("baz")
expect { MyClass.new.content }.to raise_error(NoMethodError)
expect { MyClass.new.content_before_mobility }.to raise_error(NoMethodError)
expect { MyClass.new.content=("foo") }.to raise_error(NoMethodError)
expect { MyClass.new.content_before_mobility=("foo") }.to raise_error(NoMethodError)
end
end

describe ".mobility_reader" do
it "aliases getter methods only if defined to <attribute>_before_mobility" do
MyClass.class_eval do
def title
"foo"
end

def title=(value)
value
end
end
MyClass.mobility_reader *attribute_names
expect(MyClass.new.title).to eq("foo")
expect(MyClass.new.title_before_mobility).to eq("foo")
expect(MyClass.new.title="baz").to eq("baz")
expect { MyClass.new.title_before_mobility=("baz") }.to raise_error(NoMethodError)
end
end

describe ".mobility_writer" do
it "aliases getter methods only if defined to <attribute>_before_mobility=" do
MyClass.class_eval do
def title
"foo"
end

def title=(value)
value
end
end
MyClass.mobility_writer *attribute_names
expect(MyClass.new.title).to eq("foo")
expect { MyClass.new.title_before_mobility }.to raise_error(NoMethodError)
expect(MyClass.new.title="baz").to eq("baz")
expect(MyClass.new.title_before_mobility=("baz")).to eq("baz")
end
end
end
end
end
Loading