For testing and administration purposes I am looking to build a class to communicate with an API. I've got the connection and authentication down but am struggling with the base structure and size of the class.
My main goal is to keep each application domain split, but still easy to access by one class/connection.
I've made an simpler example of what I'm looking for. In reality each domain has its own set of business rules to follow, which is why I want to keep them separate, whilst the API connection stays the same.
For instance, on CLI level I want to invoke:
$ client_one = Api.new("one")
$ client_two = Api.new("two")
$ client_one.Bikes.delete(1)
> deleted bike 1 from one
$ client_two.Phones.new(phone)
> posted phone iPhone to two
My thought proces was to nest modules inside an Api class but I can't get it to work or find the right syntax.
class Api
def initialize(client)
#client = client
#connection = Authentication.get_connection(#client)
end
#preferable put each submodule in a separate file
module Authentication
def get_connection(client)
#code to get Faraday connection
end
end
module Bikes
def new(object)
#code to post new bike
#connection.post(object)
puts "posted bike #{object.name} to #{#client}"
end
def delete(id)
#code to delete old bike
#connection.delete(id)
puts "deleted bike #{id} from #{#client}"
end
end
module Phones
def new(object)
#code to post new phone
#connection.post(object)
puts "posted phone #{object.name} to #{#client}"
end
end
end
This results in errors like:
NoMethodError: undefined method `Bikes' for #<Api:0x0000000003a543a0>
Is it possible to achieve my goal or are there better 'Ruby' ways to accomplish it?
Furthermore, is it possible to split the submodules to different files? eg:
api.rb
modules
+ -- authentication.rb
+ -- bikes.rb
+ -- phones.rb
There are some fundamental misconceptions of how Ruby OOP works in your example, and without a full code sample and the opportunity to interrogate you about what you're trying to accomplish it's hard to guide you to what might be the most appropriate answer. Any answer I give will be based partly on experience and partly on opinion, so you may see other answers as well.
At a high level, you should have classes in modules and not modules in classes. Although you can put modules in classes you better have a good understanding of why you're doing that before doing it.
Next, the modules and methods you've defined in them do not automatically become accessible to instances of the parent class, so client.Bikes will never work because Ruby expects to find an instance method named Bikes inside the Api class; it won't look for a module with that name.
The only way to access the modules and module methods that you have defined is to use them at the class/module level. So if you have this:
class Foo
module Bar
def baz
puts 'foobarbaz'
end
end
end
You can do this at the class/module level:
Foo::Bar.baz
foobarbaz
=> nil
But you can't do anything at the instance level:
Foo.new::Bar.baz
TypeError: #<Foo:0x00007fa037d39260> is not a class/module
Foo.new.Bar.baz
NoMethodError: undefined method `Bar' for #<Foo:0x00007fa037162e28>
So if you understand so far why the structure of your example doesn't work, then you can work on building something a little more sensible. Let's start with naming and the class/module structure.
First, Api is a poor name here because you'll typically use Api for something that provides an API, not connects to one, so I would recommend making the name a bit more descriptive and using a module to indicate that you are encapsulating one or more related classes:
module MonthyApiClient
end
Next, I'd recommend adding a Client class to encapsulate everything related to instantiating a client used to connect to the API:
module MonthyApiClient
class Client
def initialize
#client = nil # insert your logic here
#connection = nil # insert your logic here
end
end
end
The relationship between client and connection in your code example isn't clear, so for simplicity I am going to pretend that they can be combined into a single class (Client) and that we are dropping the module Authentication entirely.
Next, we need a reasonable way to integrate module Bikes and module Phones into this code. It doesn't make sense to convert these to classes because there's no need to instantiate them. These are purely helper functions that do something for an instance of Client, so they should be instance methods within that class:
module MonthyApiClient
class Client
def initialize
# insert your logic here
#client = nil
#connection = nil
end
def create_bike
# insert your logic here
# e.g., #connection.post(something)
end
def delete_bike
# insert your logic here
# e.g., #connection.delete(something)
end
def create_phone
# insert your logic here
# e.g., #connection.post(something)
end
end
end
Note that we've swapped new for create; you don't want to name a method new in Ruby, and in the context we're using this new would mean instantiate but do not save a new object whereas create would mean instantiate and save a new object.
And now that we're here, and now that we've eliminated all the nested modules by moving their logic elsewhere, we can see that the parent module we set up originally is unnecessarily redundant, and can eliminate it:
class MonthyApiClient
def initialize
# insert your logic here
#client = nil
#connection = nil
end
def create_bike
# insert your logic here
# e.g., #connection.post(something)
end
def delete_bike
# insert your logic here
# e.g., #connection.delete(something)
end
def create_phone
# insert your logic here
# e.g., #connection.post(something)
end
end
Then you can accomplish your original goal:
client_one = MonthyApiClient.new
client_one.create_bike
client_two = MonthyApiClient.new
client_two.create_phone
Having worked through this explanation, I think your original code is an example of spending a lot of time trying to over-optimize prematurely. It's better to plan out your business logic and make it as simple as possible first. There's some good information at https://softwareengineering.stackexchange.com/a/80094 that may help explain this concept.
I've even skipped trying to optimize the code I've shown here because I don't know exactly how much commonality there is between creating and deleting bikes and phones. With this functional class, and with a better understanding of other code within this app, I might try to DRY it up (and that might mean going back to having a module with a Client class and either module methods or other classes to encapsulate the DRY logic), but it would be premature to try.
Your last question was about how to structure files and directories for modules and classes, and I would refer you to Ideal ruby project structure (among many other questions on this site) for more information.
Related
Some open source code I'm integrating in my application has some classes that include code to that effect:
class SomeClass < SomeParentClass
def self.new(options = {})
super().tap { |o|
# do something with `o` according to `options`
}
end
def initialize(options = {})
# initialize some data according to `options`
end
end
As far as I understand, both self.new and initialize do the same thing - the latter one "during construction" and the former one "after construction", and it looks to me like a horrible pattern to use - why split up the object initialization into two parts where one is obviously "The Wrong Think(tm)"?
Ideally, I'd like to see what is inside the super().tap { |o| block, because although this looks like bad practice, just maybe there is some interaction required before or after initialize is called.
Without context, it is possible that you are just looking at something that works but is not considered good practice in Ruby.
However, maybe the approach of separate self.new and initialize methods allows the framework designer to implement a subclass-able part of the framework and still ensure setup required for the framework is completed without slightly awkward documentation that requires a specific use of super(). It would be a slightly easier to document and cleaner-looking API if the end user gets functionality they expect with just the subclass class MyClass < FrameworkClass and without some additional note like:
When you implement the subclass initialize, remember to put super at the start, otherwise the magic won't work
. . . personally I'd find that design questionable, but I think there would at least be a clear motivation.
There might be deeper Ruby language reasons to have code run in a custom self.new block - for instance it may allow constructor to switch or alter the specific object (even returning an object of a different class) before returning it. However, I have very rarely seen such things done in practice, there is nearly always some other way of achieving the goals of such code without customising new.
Examples of custom/different Class.new methods raised in the comments:
Struct.new which can optionally take a class name and return objects of that dynamically created class.
In-table inheritance for ActiveRecord, which allows end user to load an object of unknown class from a table and receive the right object.
The latter one could possibly be avoided with a different ORM design for inheritance (although all such schemes have pros/cons).
The first one (Structs) is core to the language, so has to work like that now (although the designers could have chosen a different method name).
It's impossible to tell why that code is there without seeing the rest of the code.
However, there is something in your question I want to address:
As far as I understand, both self.new and initialize do the same thing - the latter one "during construction" and the former one "after construction"
They do not do the same thing.
Object construction in Ruby is performed in two steps: Class#allocate allocates a new empty object from the object space and sets its internal class pointer to self. Then, you initialize the empty object with some default values. Customarily, this initialization is performed by a method called initialize, but that is just a convention; the method can be called anything you like.
There is an additional helper method called Class#new which does nothing but perform the two steps in sequence, for the programmer's convenience:
class Class
def new(*args, &block)
obj = allocate
obj.send(:initialize, *args, &block)
obj
end
def allocate
obj = __MagicVM__.__allocate_an_empty_object_from_the_object_space__
obj.__set_internal_class_pointer__(self)
obj
end
end
class BasicObject
private def initialize(*) end
end
The constructor new has to be a class method since you start from where there is no instance; you can't be calling that method on a particular instance. On the other hand, an initialization routine initialize is better defined as an instance method because you want to do something specifically with a certain instance. Hence, Ruby is designed to internally call the instance method initialize on a new instance right after its creation by the class method new.
My question has a couple layers to it so please bear with me? I built a module that adds workflows from the Workflow gem to an instance, when you call a method on that instance. It has to be able to receive the description as a Hash or some basic data structure and then turn that into something that puts the described workflow onto the class, at run-time. So everything has to happen at run-time. It's a bit complex to explain what all the crazy requirements are for but it's still a good question, I hope. Anyways, The best I can do to be brief for a context, here, is this:
Build a class and include this module I built.
Create an instance of Your class.
Call the inject_workflow(some_workflow_description) method on the instance. It all must be dynamic.
The tricky part for me is that when I use public_send() or eval() or exec(), I still have to send some nested method calls and it seems like they use 2 different scopes, the class' and Workflow's (the gem). When someone uses the Workflow gem, they hand write these method calls in their class so it scopes everything correctly. The gem gets to have access to the class it creates methods on. The way I'm trying to do it, the user doesn't hand write the methods on the class, they get added to the class via the method shown here. So I wasn't able to get it to work using blocks because I have to do nested block calls e.g.
workflow() do # first method call
# first nested method call. can't access my scope from here
state(:state_name) do
# second nested method call. can't access my scope
event(:event_name, transitions_to: :transition_to_state)
end
end
One of the things I'm trying to do is call the Workflow#state() method n number of times, while nesting the Workflow#event(with, custom_params) 0..n times. The problem for me seems to be that I can't get the right scope when I nest the methods like that.
It works just like I'd like it to (I think...) but I'm not too sure I hit the best implementation. In fact, I think I'll probably get some strong words for what I've done. I tried using public_send() and every other thing I could find to avoid using class_eval() to no avail.
Whenever I attempted to use one of the "better" methods, I couldn't quite get the scope right and sometimes, I was invoking methods on the wrong object, altogether. So I think this is where I need the help, yeah?
This is what a few of the attempts were going for but this is more pseudo-code because I could never get this version or any like it to fly.
# Call this as soon as you can, after .new()
def inject_workflow(description)
public_send :workflow do
description[:workflow][:states].each do |state|
state.map do |name, event|
public_send name.to_sym do # nested call occurs in Workflow gem
# nested call occurs in Workflow gem
public_send :event, event[:name], transitions_to: event[:transitions_to]
end
end
end
end
end
From what I was trying, all these kinds of attempts ended up in the same result, which was my scope isn't what I need because I'm evaluating code in the Workflow gem, not in the module or user's class.
Anyways, here's my implementation. I would really appreciate it if someone could point me in the right direction!
module WorkflowFactory
# ...
def inject_workflow(description)
# Build up an array of strings that will be used to create exactly what
# you would hand-write in your class, if you wanted to use the gem.
description_string_builder = ['include Workflow', 'workflow do']
description[:workflow][:states].each do |state|
state.map do |name, state_description|
if state_description.nil? # if this is a final state...
description_string_builder << "state :#{name}"
else # because it is not a final state, add event information too.
description_string_builder.concat([
"state :#{name} do",
"event :#{state_description[:event]}, transitions_to: :#{state_description[:transitions_to]}",
"end"
])
end
end
end
description_string_builder << "end\n"
begin
# Use class_eval to run that workflow specification by
# passing it off to the workflow gem, just like you would when you use
# the gem normally. I'm pretty sure this is where everyone's head pops...
self.class.class_eval(description_string_builder.join("\n"))
define_singleton_method(:has_workflow?) { true }
rescue Exception => e
define_singleton_method(:has_workflow?) { !!(puts e.backtrace) }
end
end
end
end
# This is the class in question.
class Job
include WorkflowFactory
# ... some interesting code for your class goes here
def next!
current_state.events.#somehow choose the correct event
end
end
# and in some other place where you want your "job" to be able to use a workflow, you have something like this...
job = Job.new
job.done?
# => false
until job.done? do job.next! end
# progresses through the workflow and manages its own state awareness
I started this question off under 300000 lines of text, I swear. Thanks for hanging in there! Here's even more documentation, if you're not asleep yet.
module in my gem
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.
Our customers order a Plan. A plan, contains business-logic, depending on what plan they actually order.
For example (Obviously the reality is a lot more complex, lots of additional differences):
Plan 1 creates one Site
Plan 2 creates one Site, and a Database
Plan 3 creates on Server of type X, one Site and mails the boss.
The way I am thinking to implement this is:
class Order
def initialize(plan_type)
# for e.g. plan_type = PlanOne, create a new PlanOne
# Details at http://stackoverflow.com/a/2811300/73673
#plan = Object.const_get(class_name).new
end
def finalize
#plan.finalize
end
end
class Plan
# All shared and common methods and attributes go here.
# Might be implemented as Module "ActsAsPlan", if a Duck is more appropriate.
end
class PlanOne < Plan
def finalize
Site.new.create
end
end
class PlanTwo < Plan
def finalize
Site.new.create
Database.new.create
end
end
class PlanThree < Plan
def finalize
server = Server.new.create
site = Site.new.create(:server => server)
BossMail.new(server, site).deliver
end
end
Used as:
Order.new("PlanOne").finalize
Order.new("PlanTwo").finalize
Order.new("PlanThree").finalize
Is this a good pattern for the problem at hand? This will be implemented in Rails. At most there will be four or five plans, it does not need to scale to hundreds of plans.
What is the name of the pattern I am implementing, or what would be the name of a more appropriate pattern? It is Ruby and I am not afraid to pull in a gem, if that simplifies work.
To me, it sounds like Abstract Factory.
To me it sounds like a Factory.
I've been bashing my head against this for about three days now. I've created a class that models html pages and tells cucumber step definitions where to populate form data:
class FlightSearchPage
def initialize(browser, page, brand)
#browser = browser
#start_url = page
#Get reference to config file
config_file = File.join(File.dirname(__FILE__), '..', 'config', 'site_config.yml')
#Store hash of config values in local variable
config = YAML.load_file config_file
#brand = brand #brand is specified by the customer in the features file
#Define instance variables from the hash keys
config.each do |k,v|
instance_variable_set("##{k}",v)
end
end
def method_missing(sym, *args, &block)
#browser.send sym, *args, &block
end
def page_title
#Returns contents of <title> tag in current page.
#browser.title
end
def visit
#browser.goto(#start_url)
end
def set_origin(origin)
self.text_field(#route[:attribute] => #route[:origin]).set origin
end
def set_destination(destination)
self.text_field(#route[:attribute] => #route[:destination]).set destination
end
def set_departure_date(outbound)
self.text_field(#route[:attribute] => #date[:outgoing_date]).set outbound
end
# [...snip]
end
As you can see, I've used instance_variable_set to create the variables that hold the references on the fly, and the variable names and values are supplied by the config file (which is designed to be editable by people who aren't necessarily familiar with Ruby).
Unfortunately, this is a big, hairy class and I'm going to have to edit the source code every time I want to add a new field, which is obviously bad design so I've been trying to go a stage further and create the methods that set the variable names dynamically with define_method and this is what's kept me awake until 4am for the last few nights.
This is what I've done:
require File.expand_path(File.dirname(__FILE__) + '/flight_search_page')
class SetFieldsByType < FlightSearchPage
def text_field(config_hash)
define_method(config_hash) do |data|
self.text_field(config_hash[:attribute] => config_hash[:origin]).set data
end
end
end
The idea is that all you need to do to add a new field is add a new entry to the YAML file and define_method will create the method to allow cucumber to populate it.
At the moment, I'm having problems with scope - Ruby thinks that define_method is a member of #browser. But what I want to know is: is this even feasible? Have I totally misunderstood define_method?
Do you mean that you'd expect to see the requires and file loads outside the class definition?
No, inside the class definition. Ruby class declarations are just code that execute in the order it's seen. Things like attr_accessor are just class methods that happen to do things to the class being defined, as it's being defined. This seems like what you want to do.
In your case you'd read the YAML file instead, and run your own logic to create accessors, build any backing data required, etc. I don't totally grok the usecase, but it doesn't sound unusual or difficult--yet.
That said, how much "convenience" do you get by putting these definitions in a YAML file? Consider something like I did once to create page instances I used to drive Watir:
class SomePage < HeavyWatir
has_text :fname # Assumed default CSS accessor pattern
has_text :whatever, accessor: 'some accessor mechanism', option: 'some other option'
end
The has_xxx were class methods that created instance variable accessors (just like attr_accessor does), built up some other data structures I used to make sure all the things that were supposed to be on the page actually were, and so on. For example, very roughly:
page = SomePage.new
page.visit
if page.missing_fields?
# Do something saying the page isn't complete
end
It sounds like you want something vaguely similar: you have a bunch of "things" you want to give to the class (or a sub-class, or you could mix it in to an arbitrary class, etc.)
Those "things" have additional functionality, that functionality works in multiple ways, like:
Things-that-happen-during-definition
E.g., has_text adds the name to a class instance hash ofthe page's metadata, like field names.
Things-that-happen-during-usage
E.g., when fname= is called set a flag in the instance's metadata saying the setter was called.
This is an appropriate case for metaprogramming, but it looks like you're going about it the wrong way.
First of all, is there going to be a different config file for each instance of FlightSearchPage or just one config file that controls all pages? It looks like you're loading the same config file regardless of the arguments to initialize so I'm guessing your case is the former.
If that is so, you need to move all of your metaprogramming code into the class (outside method definitions). I.e. when the class is defined, you want it to load the config file and then each instance is created based on that config. Right now you have it reloading the config file every time you create an instance, which seems incorrect. For example, define_method belongs to Module so it should appear in class scope, rather than in an instance method.
On the other hand, if you do want a different config for each instance, you need to move all of your metaprogramming code into the singleton class e.g. define_singleton_method rather than define_method.