Stop initialization of ruby object - ruby

Which is the best practice to not save an object because does not pass certain validations?
I have done this class:
class Articles
attr_reader :doi, :title, :issn
def initialize data, doi_validator: Validators::Doi.new, title_validator: Validators::Title.new, issn_validator: Validators::Issn.new
#doi, #title, #issn = data
#doi_validator = doi_validator
#title_validator = title_validator
#issn_validator = issn_validator
raise_error unless valid?
end
private
def valid?
(
doi_validator.call(doi) &&
title_validator.call(title) &&
issn_validator.call(issn)
)
end
attr_reader :doi_validator, :title_validator, :issn_validator
end
The thing is that instead of raising an error I would like the process of instantiation to be stopped without affecting the flow of the app.
Probably the best way is to check it before initializing the instantiation, but that would complicate pretty much the application. Is there any way you recommend?

If you have something like
article = Articles.new(data)
then there are only two possibilities:
an exception is raised; article = assignment does not happen. You need to catch the exception.
an exception is not raised; article now contains an Article object (possibly invalid).
You could walk the middle ground and make a new class method:
class Article
def self.new_if_valid(*args)
self.new(*args)
rescue InitializationInvalidError
nil
end
end
class InitializationInvalidError < StandardError; end
EDIT: Actually, you could even make it a mixin, it is general enough:
module NilIfInitializationFails
def self.new_if_valid(*args)
self.new(*args)
rescue InitializationInvalidError
nil
end
end
class InitializationInvalidError < StandardError; end
class Article
include NilIfInitializationFails
# ....
end

Related

ruby / Passing arguments to a dynamically created class

There's probably many other better ways; but having the following piece of code :
class ApplicationService
def self.build(*args, &block)
new(*args, &block).build
end
end
class BaseClass; end
class Fetcher < ApplicationService
attr_reader :resource_name
def initialize(resource_name)
#resource_name = resource_name
end
def build
resource_name = #resource_name
Class.new(BaseClass) do
##resource_name = resource_name
class << self
def all
"http://some.remote.resource/#{##resource_name}/all"
end
end
end
end
end
in order to have the initial resource_name in the self.all method, i came up with defining ##resource_name = resource_name. I'm totally unsure if that's the good way to go.
I'd like to be able to use such 'generator', in order to provide the following interface :
## In some kind of initializers :
Xyz = Fetcher.build('xyz')
## Final use :
Xyz.all
Would there be a better pattern to have the class created dynamically, while passing arguments when creating this class ?
It is unclear why you want to create the class in the first place. If there are good reasons for it, my answer is kind of invalid.
You can have the desired behaviour using "standard" OOP techniques and working with instances
class Fetcher
def initialize(resource_name)
#resource_name = resource_name
end
def all
"http://some.remote.resource/#{#resource_name}/all"
end
end
xyz_fetcher = Fetcher.new('xyz')
xyz_fetcher.all
Otherwise, your code is more or less what you would/should do, I guess. Just, I would let the Fetcher class act as a singleton (not use an instance of Fetcher):
class Fetcher < ApplicationService
# make a singleton by privatizing initialize (read this up somewhere else)
def self.build(resource_name)
Class.new(BaseClass) do
##resource_name = resource_name
class << self
def all
"http://some.remote.resource/#{##resource_name}/all"
end
end
end
end
end
Then
Xyz = Fetcher.build('xyz')
Xyz.all
Now, you have the stuff with ApplicationService which more or less achieves that (and passes a block), so probably we readers miss some parts of the bigger picture ... please clarify if that is the case.
Besides singletonization, you could also work with modules instead (thanks #max for the comment).

Testing an abstracted if conditional

I'm trying to figure out the best way to test find_communities here without resorting to using polymorphism here to defeat the if statement staring at me.
class CommunityFinder
def initialize(filters={})
#filters = filters
end
def find_communities
return my_communities if #filters[:my_communities]
visible_communities
end
def my_communities
# [...]
end
def visibile_communities
# [...]
end
end
I have both my_communities and visible_communities well tested, but I have concerns about testing find_communities.
I don't want to duplicate the test setup for both my_communities and visible_communities, because there's likely going to be
I would prefer for the class API to contain all 3 public methods because the conditions for find_communities won't ever change.
I'm writing this with the expectation that the class is going to change by someone other than me in the near future, and that there's going to be more methods
Should I:
make find_communities live in the caller
make find_communities be it's own strategy
duplicate the tests into find_communities
pick your own 4th option.
This example is a case where you really should have two subclasses, each of which implements its own communities method:
class CommunityFinder::Base
def initialize(**options)
#options = options
end
end
class CommunityFinder::Mine < CommunityFinder::Base
def communities
end
end
class CommunityFinder::Visible < CommunityFinder::Base
def communities
end
end
You can use a factory method to instantiate the correct subclass:
module CommunityFinder
def self.filter(**options)
if (options[:my_communities])
CommunityFinder::Mine.new(options)
else
CommunityFinder::Visible.new(options)
end
end
end

How to test a class method that modifies an attribute of another class in a containerised way rspec

I have an issue I have been whacking my head against for hours now, and neither I nor anyone I have asked has been able to come up with a suitable answer.
Essentially, I am writing a method that allows me to edit an instance variable of another method. I have multiple ways of doing this, however my issue is with writing the test for this method. I have tried many different double types, however as they are immutable and do not store states, I did not manage to find a way to make it work.
Here is the class whose working variable is changed:
class MyClass
attr_writer :working
def working?
#working
end
end
Here is the class and method that change it:
class OtherClass
def makes_work
#ary_of_instances_of_MyClass_to_fix.map do |x|
x.working = true
#ary_of_fixed_objects << x
end
end
end
(The actual class is much larger, but I have only included a generalised version of the method in question. I can put all of the specific code up in a gist if it would help)
So I need a way to test that makes_work does in fact accept the array of objects to be changed, changes them and appends them to array_of_fixed_objects. What would be the best way of testing this in a containerised way, without requiring MyClass?
My last attempt was using spies to see what methods were called on my dummy instance, however a range of failures, depending on what I did. Here is the most recent test I wrote:
describe '#make_work' do
it 'returns array of working instances' do
test_obj = spy('test_obj')
subject.ary_of_instances_of_MyClass_to_fix = [test_obj]
subject.makes_work
expect(test_obj).to have_received(working = true)
end
end
This currently throws the error:
undefined method to_sym for true:TrueClass
Many thanks for any help! I apologise if some formatting/ info is a little bit messed up, I am still pretty new to this whole stackoverflow thing!
I think the problem is have_received(working = true), it should be have_received(:working=).with(true)
Edit:
Examples of using have_received
https://github.com/rspec/rspec-mocks#test-spies
https://relishapp.com/rspec/rspec-mocks/v/3-5/docs/setting-constraints/matching-arguments
This works for me
class MyClass
attr_writer :working
def working?
#working
end
end
class OtherClass
attr_writer :ary_of_instances_of_MyClass_to_fix
def initialize
#ary_of_fixed_objects = []
end
def makes_work
#ary_of_instances_of_MyClass_to_fix.map do |x|
x.working = true
#ary_of_fixed_objects << x
end
end
end
describe '#make_work' do
subject { OtherClass.new }
it 'returns array of working instances' do
test_obj = spy('test_obj')
subject.ary_of_instances_of_MyClass_to_fix = [test_obj]
subject.makes_work
expect(test_obj).to have_received(:working=).with(true)
end
end
If you'd rather just avoid stubbing, you could use an instance of OpenStruct instead of a double:
class OtherClass
attr_writer :ary_of_instances_of_MyClass_to_fix
def initialize
#ary_of_instances_of_MyClass_to_fix, #ary_of_fixed_objects = [], []
end
def makes_work
#ary_of_instances_of_MyClass_to_fix.map do |x|
x.working = true
#ary_of_fixed_objects << x
end
#ary_of_fixed_objects
end
end
require 'ostruct'
RSpec.describe "#makes_work" do
describe "given an array" do
let(:array) { [OpenStruct.new(working: nil)] }
subject { OtherClass.new }
before do
subject.ary_of_instances_of_MyClass_to_fix = array
end
it "sets the 'working' attribute for each element" do
expect(array.map(&:working)).to eq [nil]
subject.makes_work
expect(array.map(&:working)).to eq [true]
end
end
end

Ruby: How to catch custom exception in parent class when it is defined in a sub class

I am relatively new to ruby and I am stuck with this problem which is rather hard to solve.
What I want to achieve is that I could catch custom errors which I throw from the sub class in a parent class. Given an example below, how could I make the parent class to understand the RequestTimeout class? Because, now when I run the code it results to a following output:
test_raise.rb:5:in `rescue in handle_errors': uninitialized constant BaseService::RequestTimeout (NameError)
from test_raise.rb:4:in `handle_errors'
from test_raise.rb:14:in `first_service_method'
from test_raise.rb:31:in `<main>'
The code:
class BaseService
def handle_errors
yield
rescue RequestTimeout => e # <-- the problem
p e.message
end
end
class FirstService < BaseService
class RequestTimeout < StandardError; end
def first_service_method
handle_errors do
raise RequestTimeout, "FirstService RequestTimeout"
end
end
end
class SecondService < BaseService
class RequestTimeout < StandardError; end
def second_service_method
handle_errors do
raise RequestTimeout, "SecondService RequestTimeout"
end
end
end
a = FirstService.new
a.first_service_method
Ofc. I could solve the problem by changing:
rescue RequestTimeout => e
to:
rescue => e
But I dont want to do that because I wan't to catch multiple exceptions (more than RequestTimeout) which are defined and raised by me. Any help would be awesome!
There is an option to move
class RequestTimeout < StandardError; end
declaration into the base class. It hence will become available to all children.
Whether you have different exceptions for different child classes, you are forced to use namespaces, as #Marek said in sibling comment.
The problem is about namespaces - RequestTimeout is defined in different namespace than BaseService. You should have:
rescue FirstService::RequestTimeout => e

Abstract Method in Ruby

How can I force a subclass to implement a method in Ruby. There doesn't seem to be an abstract keyword in Ruby, which is the approach I would take in Java. Is there another more Ruby-like way to enforce abstract?
Abstract methods are supposed to be less useful in Ruby because it's not strongly statically typed.
However, this is what I do:
class AbstractThing
MESS = "SYSTEM ERROR: method missing"
def method_one; raise MESS; end
def method_two; raise MESS; end
end
class ConcreteThing < AbstractThing
def method_one
puts "hi"
end
end
a = ConcreteThing.new
a.method_two # -> raises error.
It rarely seems to be necessary, however.
I like the answer by pvandenberk, but I would improve it as follows:
module Canine # in Ruby, abstract classes are known as modules
def bark
fail NotImplementedError, "A canine class must be able to #bark!"
end
end
Now if you make a class belonging to Canine "abstract class" (ie. a class that has Canine module in its ancestors), it will complain if it is found that #bark method is not implemented:
class Dog
include Canine # make dog belong to Canine "abstract class"
end
Dog.new.bark # complains about #bark not being implemented
class Dog
def bark; "Bow wow!" end
end
# Now it's OK:
Dog.new.bark #=> "Bow wow!"
Note that since Ruby classes are not static, but always open to changes, Dog class itself cannot enforce existence of #bark methods, since it doesn't know when is it supposed to be finished. If you as a programmer do, it is up to you to test it at such time.
My preferred approach is similar but slightly different... I prefer it as follows, because it makes the code self-documenting, giving you something very similar to Smalltalk:
class AbstractThing
def method_one; raise "SubclassResponsibility" ; end
def method_two; raise "SubclassResponsibility" ; end
def non_abstract_method; method_one || method_two ; end
end
Some people will complain that this is less DRY, and insist on creating an exception subclass and/or put the "SubclassResponsibility" string in a constant, but IMHO you can dry things up to the point of being chafed, and that is not usually a good thing. E.g. if you have multiple abstract classes across your code base, where would you define the MESS string constant?!?
I like the use of a gem like abstract_method which gives a dsl rails style syntax abstract methods:
class AbstractClass
abstract_method :foo
end
class AbstractModule
abstract_method :bar
end
class ConcreteClass < AbstractClass
def foo
42
end
end
This code will not let you load the class if the methods 'foo', 'bar' and 'mate' are not defined in the inherited class.
It does not account for classes being defined across many files, but lets get honest do many of us actually define class methods across many files? I mean if you don't count mix-ins. (which this does account for)
def self.abstract(*methods_array)
##must_abstract ||= []
##must_abstract = Array(methods_array)
end
def self.inherited(child)
trace = TracePoint.new(:end) do |tp|
if tp.self == child #modules also trace end we only care about the class end
trace.disable
missing = ( Array(##must_abstract) - child.instance_methods(false) )
raise NotImplementedError, "#{child} must implement the following method(s) #{missing}" if missing.present?
end
end
trace.enable
end
abstract :foo
abstract :bar, :mate
If you want to have an error thrown when you create an instance of the class you could do the following
class AbstractClass
def self.new(args)
instance = allocate # make memory space for a new object
instance.send(:default_initialize, args)
instance.send(:initialize, args)
instance
end
#This is called whenever object created, regardless of whether 'initialize' is overridden
def default_initialize(args)
self.abstract_method #This will raise error upon object creation
end
private :default_initialize
def initialize(args)
# This can be overridden by new class
end
end
class NewClass < AbstractClass
end
NewClass.new #Throw error
Because the question is (focus on) "How can I force a subclass to implement a method in Ruby", so i think we can use TDD :D, for example: rspec shared example
shared_examples "MUST implement abstract method" do |method_sym|
it { is_expected.to respond_to(method_sym) }
end
describe Stack do
it_behaves_like "MUST implement abstract method", :push
it_behaves_like "MUST implement abstract method", :pop
end
Maybe Tests are better than Abstract :D , reference: http://morningcoffee.io/interfaces-in-ruby.html

Resources