My single table inheritance system creates it's subclasses based on a input string from my service. I am wondering if there is a better way to write this code as it seems clunky.
The client should not know, nor should it care, what my sub-classing structure is. When a request comes in the parameters I get are:
{calculator: {course: 'science'}}
The class:
class Calculator < ActiveRecord::Base
before_save :subclass, on: :create
def subclass
case course.downcase
when "science"
self.type = "Calculator::ScientificCalc"
when "standard"
self.type = "Calculator::StandardCalc"
end
end
end
It seems smelly to have a callback on the object and then set the 'self.type'. Any body have a better solution to this?
Im not sure if I get what you want to do there, but you should create a type column on your calculator table, and your classes will inherit it from Calculator.
Related
I'm trying to implement a Facade in idiomatic Ruby while coming from Java. I can see that Rails' ActiveRecord is fond of using class methods for things like find_by(criteria) and does not use Repository pattern for that task.
My Facade wraps a specific webservice with several methods. My original idea was to make it's API similar to ActiveRecord (learning by imitation):
class MyEntity
# ....
def get_name
#loaded_name + #loaded_surname
end
def delete
#entity_access_service.delete(#id)
end
def save
#entity_access_service.save(#id, #loaded_name , #loaded_surname)
end
def self.find(id)
data = #entity_access_service.get_data_for(id)
MyEntity.new(data) #Or whatever way to populate my entity
end
end
This, in theory, would work great:
e = MyEntity.find(10)
p e.get_name
e.delete
Or:
e = MyEntity.new(some stuff)
e.save
Question:
For save and delete instance methods to work, I need to somehow get an instance of EntityAccessService. This instance should be mockable to test it in isolated environment. What is the correct way to do it?
I'm expecting my tests to look as simple as possible and without some weird hacks, as what I'm trying to implement seems fairly trivial.
I have thought of several options to do that:
Having a class-level variable holding entity_access_service used by all of the entities created in application. In this case, where should I initialize this field? For example:
class MyEntity
##entity_access_service = nil
end
# Somewhere else (where?):
MyEntity.entity_access_service = MyEntityService.new(some_params_from_env)
This way, in my tests I would have to initialize/mock it at start.
Similar to 1 but initialize it in the class. This looks weird, especially if I know that my tests do not have required ENV params populated at all.
Have an extra constructor/attribute to set the entity_service. This won't work, as save would not have this field initialized.
Create a Repository class. This would work pretty ok, but seems to be not what Ruby people do.
Following ActiveRecord's example, you can create a method on your class itself, or on the base class from which your other classes are derived.
ActiveRecord provides a method ActiveRecord::Base.connection which returns the connection object which all models use to access the database. You can do something similar:
class MyEntity
....
def self.entity_access_service
# return your service object
end
def self.find(id)
MyEntity.entity_access_service.get_data_for(id)
MyEntity.new(data) # Or whatever way to populate my entity
end
def save()
MyEntity.entity_access_service.save(#id, #loadedName, #loadedSurname)
end
end
As far as initialization goes, you either have to have a initialization step in your app (and test suite) where service credentials are read from config files and passed into your MyEntity object, or your entity_access_service method can lazily create the object it returns on first access using a very common Ruby idiom:
def self.entity_access_service
#entity_access_service || = # build entity_access_service object
end
Note that, by wrapping your class-level instance variables in class-level accessor methods, you can avoid the use of ## which is a recommended best practice.
I am extending an existing library by creating a child class which extends to the library class.
In the child class, I was able to test most of functionality in initialize method, but was not able to mock super call. The child class looks like something like below.
class Child < SomeLibrary
def initialize(arg)
validate_arg(arg)
do_something
super(arg)
end
def validate_arg(arg)
# do the validation
end
def do_something
#setup = true
end
end
How can I write rspec test (with mocha) such that I can mock super call? Note that I am testing functionality of initialize method in the Child class. Do I have to create separate code path which does not call super when it is provided with extra argument?
You can't mock super, and you shouldn't. When you mock something, you are verifying that a particular message is received, and super is not a message -- it's a keyword.
Instead, figure out what behavior of this class will change if the super call is missing, and write an example that exercises and verifies that behavior.
As #myron suggested you probably want to test the behavior happening in super.
But if you really want to do this, you could do:
expect_any_instance_of(A).to receive(:instance_method).and_call_original
Assuming
class B < A
def instance_method
super
end
end
class A
def instance_method
#
end
end
Disclaimer expect_any_instance_of are a mark of weak test (see):
This feature is sometimes useful when working with legacy code, though
in general we discourage its use for a number of reasons:
The rspec-mocks API is designed for individual object instances, but
this feature operates on entire classes of objects. As a result there
are some semantically confusing edge cases. For example, in
expect_any_instance_of(Widget).to receive(:name).twice it isn't clear
whether a specific instance is expected to receive name twice, or if
two receives total are expected. (It's the former.)
Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too
complex.
It is the most complicated feature of rspec-mocks, and has historically received the most bug reports. (None of the core team
actively use it, which doesn't help.)
A good way to test this is to set an expectation of some action taken by the superclass - example :
class Some::Thing < Some
def instance_method
super
end
end
and the super class:
class Some
def instance_method
another_method
end
def self.another_method # not private!
'does a thing'
end
end
now test :
describe '#instance_method' do
it 'appropriately triggers the super class method' do
sawm = Some::Thing.new
expect(sawm).to receive(:another_method)
sawm.instance_method
end
end
All This Determines Is That Super Was Called On the Superclass
This pattern's usefulness is dependent on how you structure your tests/what expectations you have of the child/derivative class' mutation by way of the super method being applied.
Also - pay close attention to class and instance methods, you will need to adjust allows and expects accordingly
YMMV
A bit late to this party, but what you can also do is forego using the super keyword and instead do
class Parent
def m(*args)
end
end
class Child < Parent
alias super_m m
def m(*args)
super_m(*args)
end
end
That way your super method is accessible like any other method and can e.g. be stubbed like any other method. The main downside is that you have to explicitly pass arguments to the call to the super method.
I'm using Sinatra (1.2) and RSpec (2.5) and would like to create a new object with an attribute TDD style. This is how the end result should look like:
class User
def initialize(name)
#name = name
end
end
I know I have to write the example before the implementation but I'm trying to explain my question here. :) Here is the not working spec I have so far:
describe User
it "creates a new user object" do
name = mock("A name")
user = mock(User) # shouldn't do this, see the reply's
user.should_receive(:name=).with(name)
User.new(name)
end
end
When I run RSpec I get the "expected: 1 time, received 0 times" error. Any idea how I can explain RSpec I would like to assign the name attribute?
Note: I'm not using Rails, not using ActiveRecord or anything, just Ruby.
First of all, let me explain why the spec you have written doesn't work:
You set an expectation that the mock object returned by mock(User) should receive name=. There are two problems with this. First of all the mock will receive nothing, because it is never called. mock(User) returns a mock object, and it cannot be used to set expectations for what the User class object will receive (to do that simply do User.should_receive(...)). Secondly, even if you had set the expectation on the User class object, that object will never receive name=. There are two reasons for this, too: firstly because name= (had it existed) would be an instance method, and as such not called on the class object, and secondly, you declare no name= instance method. What your code does is that it sets an instance variable.
Now, how should you write a test for this? You shouldn't. Tests are to define and assert the behaviour, not the implementation. Setting an instance variable is pure implementation. There is in your example code no way to get the value of the #name instance variable from outside of the class, therefore there is no reason to write a test for it.
Obviously your code is just an example, anything useful would do something with the #name variable, and that is what you should test. Start by writing a test for what a User object will be used for, then write all the implementation needed to fulfill that test (but no more). Write a test that reflects how the object will be used in actual production code.
I'd really recommend you don't approach this using mocks. It's not what they're for. In fact, specifying getters/setters like this is not really what TDD is for. The idea is to let a requirement drive the setters/getters into existence. For example, there might be a requirement that the user's name appear in a welcome message when he/she logs in. Then you might do something like this:
describe 'login process' do
it "displays user's name after successful login" do
user = User.new("Cimm", "cimm#somewhere.com", "secret")
post "/login", :email => "cimm#somewhere.com", :password => "secret"
last_response.body.should =~ /Welcome Cimm/m
end
end
This specifies behavior, and forces you to implement a means of setting the name attribute (via the constructor, in this case) and a means of accessing it. No need to specify the constructor directly.
Do you really want to mock the very object you are developing?
require 'rspec'
class User
attr_accessor :name
def initialize(name)
#name = name
end
end
describe User do
subject {User.new "other name"}
it "creates a new user object" do
subject.should respond_to :name=
end
end
I want to extend the Nokohiri::XML::Node object into my own unique object with custom behaviors (methods).
I have the following object:
class RDFNode < Nokogiri::XML::Node
def get_tag
self.at_xpath("Path/to/tag")
end
end
and the Node Factory:
class RDFNodeFactory
#doc = Nokogiri::XML.parse('rdf_file.xml')
def self.get_node(id)
#doc.xpath_at("Path/to/rdf/node[#id=#{id}]")
end
end
My question is about best Ruby practices and basic OOP in Ruby.
How can I get RDFNodeFactory.get_node("someid") to return an RDFNode instead of a Nokogiri::XML::Node? I used to use type casting in Java but we don't have that in Ruby.
Should I just modify Nokogiri::XML::Node class instead of extending it to a custom class? What is a more acceptable practice?
Instead of extending the Nokogiri::XML::Node class just to add one method, you should move the get_tag method and add it to the already existing Nokogiri::XML::Node using the concept of Open Classes. This would look like:
class Nokogiri::XML::Node
def get_tag
self.at_xpath("Path/to/tag")
end
end
This is completely fine in terms of Ruby Standards, just be sure there aren't any side affects when adding this method to Nokogiri::XML::Node, such as get_tag already exists.
With regards to open class(under the assumption that there are no current conflicts) vs inheriting 3rd party libraries:
This is a valid fear, this is why you have to be very careful when you utilize open classes and updating 3rd party libraries. However if you think of it, if a library changes their code in such a way that it messes up your code... This will happen both when you use open classes or inherit from their code. When it all boils down, you have a dependency, and you must be careful no matter what.
Instance methods tied to object, not class, so there is no way to "cast" object to another class. But in ruby you can add methods to existing class:
class Nokogiri::XML::Node
def get_tag
self.at_xpath("Path/to/tag")
end
end
Or even add method to single object:
def #doc.get_tag
self.at_xpath("Path/to/tag")
end
A (sort of) solution I have is containment
class RDFNode
attr_reader :node
def initialize(node)
#node = node
end
def get_tag(id)
node.at_xpath("Path/to/tag")
end
end
Now I have preserved modularity but lost all the awesomeness of inheritance! Not perfect but it's a start. Perhaps there is some way improve on this (extend RDFNode with Nokogiri::XML::Node and make self = node for Nokogiri methods)?
I have an instance of a master class which generates instances of a subclass, these subclasses need to forward some method calls back to the master instance.
At the moment I have code looking something like this, but it feels like I should be able to do the same thing more efficiently (maybe with method_missing?)
class Master
def initalize(mynum)
#mynum = mynum
end
def one_thing(subinstance)
"One thing with #{subinstance.var} from #{#mynum}"
end
def four_things(subinstance)
"Four things with #{subinstance.var} from #{#mynum}"
end
def many_things(times,subinstance)
"#{times} things with #{subinstance.var} from #{#mynum}"
end
def make_a_sub(uniqueness)
Subthing.new(uniqueness,self)
end
class Subthing
def initialize(uniqueness,master)
#u = uniqueness
#master = master
end
# Here I'm forwarding method calls
def one_thing
master.one_thing(self)
end
def four_things
master.four_things(self)
end
def many_things(times)
master.many_things(times,self)
end
end
end
m = Master.new(42)
s = m.make_a_sub("very")
s.one_thing === m.one_thing(s)
s.many_things(8) === m.many_things(8,s)
I hope you can see what's going on here. I would use method_missing, but I'm not sure how to cope with the possibility of some calls having arguments and some not (I can't really rearrange the order of the arguments to the Master methods either)
Thanks for reading!
Support for the Delegation Pattern
Delegate
Does the Delegate help here? It allows you to delegate methods to a second class
This library provides three different ways to delegate method calls to an object. The easiest to use is SimpleDelegator. Pass an object to the constructor and all methods supported by the object will be delegated. This object can be changed later.
Going a step further, the top level DelegateClass method allows you to easily setup delegation through class inheritance. This is considerably more flexible and thus probably the most common use for this library.
Finally, if you need full control over the delegation scheme, you can inherit from the abstract class Delegator and customize as needed. (If you find yourself needing this control, have a look at forwardable, also in the standard library. It may suit your needs better.)
Forwardable
There's also the forwardable library
This library allows you delegate method calls to an object, on a method by method basis. You can use Forwardable to setup this delegation at the class level, or SingleForwardable to handle it at the object level.