-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
192 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
class Evil::Client | ||
# | ||
# Data structure with validators and memoizers | ||
# | ||
class Model | ||
extend Dry::Initializer | ||
|
||
@policy = Policy | ||
|
||
class << self | ||
# @!method options(key, type = nil, opts = {}) | ||
# Creates or updates the settings' initializer | ||
# | ||
# @see [http://dry-rb.org/gems/dry-initializer] | ||
# | ||
# @param [#to_sym] key Symbolic name of the option | ||
# @param [#call] type (nil) Type coercer for the option | ||
# @option opts [#call] :type Another way to assign type coercer | ||
# @option opts [#call] :default Proc containing default value | ||
# @option opts [Boolean] :optional Whether it can be missed | ||
# @option opts [#to_sym] :as The name of settings variable | ||
# @option opts [false, :private, :protected] :reader Reader method type | ||
# @return [self] | ||
# | ||
def option(key, type = nil, as: key.to_sym, **opts) | ||
NameError.check!(as) | ||
super | ||
self | ||
end | ||
undef_method :param # model initializes with [#options] only | ||
|
||
# Creates or reloads memoized attribute | ||
# | ||
# @param [#to_sym] key The name of the attribute | ||
# @param [Proc] block The body of new attribute | ||
# @return [self] | ||
# | ||
def let(key, &block) | ||
NameError.check!(key) | ||
define_method(key) do | ||
instance_variable_get(:"@#{key}") || | ||
instance_variable_set(:"@#{key}", instance_exec(&block)) | ||
end | ||
self | ||
end | ||
|
||
def policy | ||
@policy ||= superclass.policy.for(self) | ||
end | ||
|
||
# Add validation rule to the [#policy] | ||
# | ||
# @param [Proc] block The body of new attribute | ||
# @return [self] | ||
# | ||
def validate(&block) | ||
policy.validate(&block) | ||
self | ||
end | ||
|
||
def new(op = {}) | ||
op = Hash(op).each_with_object({}) { |(k, v), obj| obj[k.to_sym] = v } | ||
super(op).tap { |item| in_english { policy[item].validate! } } | ||
rescue => error | ||
raise ValidationError, error.message | ||
This comment has been minimized.
Sorry, something went wrong. |
||
end | ||
|
||
private | ||
|
||
def in_english(&block) | ||
available_locales = I18n.available_locales | ||
I18n.available_locales = %i[en] | ||
I18n.with_locale(:en, &block) | ||
ensure | ||
I18n.available_locales = available_locales | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
RSpec.describe Evil::Client::Model do | ||
before { class Test::Model < described_class; end } | ||
|
||
let(:model) { klass.new(options) } | ||
let(:klass) { Test::Model } | ||
let(:options) { { "id" => 42, "name" => "Andrew" } } | ||
let(:dsl_methods) do | ||
%i[options datetime logger scope basic_auth key_auth token_auth] | ||
end | ||
|
||
describe ".policy" do | ||
subject { klass.policy } | ||
|
||
it "subclasses Evil::Client::Policy" do | ||
expect(subject.superclass).to eq described_class.policy | ||
expect(described_class.policy.superclass).to eq Tram::Policy | ||
end | ||
|
||
it "refers back to the model" do | ||
expect(subject.model).to eq klass | ||
end | ||
end | ||
|
||
describe ".option" do | ||
it "is defined by Dry::Initializer DSL" do | ||
expect(klass).to be_a Dry::Initializer | ||
end | ||
|
||
it "fails when method name is reserved for DSL" do | ||
dsl_methods.each do |name| | ||
expect { klass.option name } | ||
.to raise_error Evil::Client::NameError | ||
end | ||
end | ||
|
||
it "allows the option to be renamed" do | ||
expect { klass.option :basic_auth, as: :something }.not_to raise_error | ||
end | ||
end | ||
|
||
describe ".let" do | ||
before do | ||
klass.option :id | ||
klass.let(:square_id) { id**2 } | ||
end | ||
|
||
subject { model.square_id } | ||
|
||
it "adds the corresponding memoizer to the instance" do | ||
expect(subject).to eq(42**2) | ||
end | ||
|
||
it "fails when method name is reserved for DSL" do | ||
dsl_methods.each do |name| | ||
expect { klass.let(name) { 0 } } | ||
.to raise_error Evil::Client::NameError | ||
end | ||
end | ||
end | ||
|
||
describe ".validate" do | ||
before do | ||
klass.option :name | ||
klass.validate { errors.add :name_present if name.to_s == "" } | ||
end | ||
|
||
let(:options) { { "name" => "" } } | ||
|
||
it "adds validation for an instance" do | ||
# see spec/fixtures/locale/en.yml | ||
expect { model } | ||
.to raise_error(Evil::Client::ValidationError, /The user has no name/) | ||
end | ||
end | ||
|
||
describe ".new" do | ||
subject { model } | ||
|
||
context "with wrong options" do | ||
before { klass.option :user, as: :customer } | ||
|
||
it "raises Evil::Client::ValidationError" do | ||
expect { subject }.to raise_error Evil::Client::ValidationError, /user/ | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is very nasty thing. Any exception occured inside method will be thrown out and it will be hard to find where it was raised and why. Happy debugging bitches!