Better way to do this? Currently using two method_missing calls...


D

Dan

Hi,

I'm building a wrapper around a web service and I'd like to have my
class simply pass through calls to the remote service (since the
remote service will be adding new objects and methods faster then I'll
be updating the library, I need my class to be accept anything).

For example, if my ruby class is named API, I want to be able to call
API.images.find_by_name and have my class pass along "images" and
"find_by_name" to the remove service. I've got this working using two
method_missing calls (one as a class method and one as an instance
method), but it looks sloppy and I was wondering if there was a better
way to do this. Any ideas?

class API
def self.method_missing(remote_object)
api = API.new
api.instance_variable_set("@remote_object", remote_object)
return api
end
def method_missing(remote_method, *args)
API.call(@remote_object, remote_method, args)
end
def self.call(remote_object, remote_method, args)
puts "#{remote_object}.#{remote_method} #{args}"
end
end

Thanks!
Dan
 
Ad

Advertisements

X

Xavier Noria

El Aug 4, 2007, a las 1:50 AM, Dan escribi=F3:
I'm building a wrapper around a web service and I'd like to have my
class simply pass through calls to the remote service (since the
remote service will be adding new objects and methods faster then I'll
be updating the library, I need my class to be accept anything).

Wouldn't the standard delegator library suffice? The description in =20
the Pickaxe is more complete than the rdoc shown by ri.

-- fxn
 
D

Dan

Thanks for the reply. I'm probably being dense here, but I don't see
how Delegator would help in this scenario. Can you explain what you
mean?

Thanks!
Dan
 
X

Xavier Noria

El Aug 4, 2007, a las 1:29 PM, Dan escribi=F3:
Thanks for the reply. I'm probably being dense here, but I don't see
how Delegator would help in this scenario. Can you explain what you
mean?

I guess the one being dense was me then :).

You are dispatching everything to some object dynamically. That's =20
what the delegator library is made for and that's why it rang a bell =20
when you asked for alternatives to that implementation. It is just an =20=

idea for you to see whether it is worthwile compared to your approach.

According to the code there are two levels of catchall: The first one =20=

is a class-level catchall, it creates a new object out of something =20
like API.images. The second one dispatches to that object somehow. =20
Delegation would target the former so the class-level method_missing =20
would return an object that already has delegation builtin.

Without seeing the real intented usage I can't be more specific. It =20
could be that the way to call the remote service only uses symbols =20
and there's no real object locally. If that was the case the =20
delegator library wouldn't fit.

-- fxn
 
D

Dan

Ok, I see where you were going now. I guess I could go in that
direction, but since sending functions and objects to the web service
is just done via POST params, so I'd rather route all the requests to
the "call" class method on my API class. Any ideas on how to do this
without having to create a instance of the API class (or any class)
just to store the "object" name ?

Dan
 
X

Xavier Noria

El Aug 4, 2007, a las 4:20 PM, Dan escribi=F3:
Ok, I see where you were going now. I guess I could go in that
direction, but since sending functions and objects to the web service
is just done via POST params, so I'd rather route all the requests to
the "call" class method on my API class. Any ideas on how to do this
without having to create a instance of the API class (or any class)
just to store the "object" name ?

The idea is then that something like

API.images.find_by_name(name)

translates to a POST request where "images" and "find_by_name" are =20
string values?

-- fxn
 
Ad

Advertisements

D

Dan

El Aug 4, 2007, a las 4:20 PM, Dan escribió:


The idea is then that something like

API.images.find_by_name(name)

translates to a POST request where "images" and "find_by_name" are
string values?

-- fxn

exactly
 
X

Xavier Noria

El Aug 4, 2007, a las 5:05 PM, Dan escribi=F3:

Yeah, I would have written the same implementatcion than you to =20
support that usage.

Perhaps API.images would return a different class instead of API =20
itself with its own method missing depending on the role of that =20
API.images conceptually, but that would be essentially the same =20
approach anyway.

Looks fine to me.

-- fxn
 
R

Robert Klemme

------=_Part_109270_27931451.1186383202752
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

2007/8/4 said:
Ok, I see where you were going now. I guess I could go in that
direction, but since sending functions and objects to the web service
is just done via POST params, so I'd rather route all the requests to
the "call" class method on my API class. Any ideas on how to do this
without having to create a instance of the API class (or any class)
just to store the "object" name ?

This is what I'd do (attached). Note, you need to know the number of
invocations in this example. The pattern could however use other
criteria for triggering the action.

Kind regards

robert

------=_Part_109270_27931451.1186383202752
Content-Type: application/x-ruby; name=call-recorder.rb
Content-Transfer-Encoding: base64
X-Attachment-Id: f_f50lw6x7
Content-Disposition: attachment; filename="call-recorder.rb"

IyFydWJ5DQoNCiMgcmVjb3JkIGludm9jYXRpb25zIGFuZCBhY3QgaWYgYSBjZXJ0YWluDQojIG51
bWJlciBvZiBjYWxscyBpcyByZWFjaGVkDQojIG90aGVyIHRlcm1pbmF0aW9uIGNvbmRpdGlvbnMg
Y2FuIGJlIHRob3VnaA0KIyBvZiBhcyB3ZWxsDQpjbGFzcyBDYWxsUmVjb3JkZXINCiAgZGVmIGlu
aXRpYWxpemUoY2FsbHMsJmIpDQogICAgQGNhbGxzID0gY2FsbHMNCiAgICBAYWN0aW9uID0gYg0K
ICAgIEBhcmdzID0gW10NCiAgZW5kDQogIA0KICBkZWYgbWV0aG9kX21pc3NpbmcoKmEpDQogICAg
QGFyZ3MgPDwgYQ0KDQogICAgaWYgQGFyZ3Muc2l6ZSA9PSBAY2FsbHMNCiAgICAgIEBhY3Rpb25b
QGFyZ3NdDQogICAgZWxzaWYgQGFyZ3Muc2l6ZSA+IEBjYWxscw0KICAgICAgc3VwZXINCiAgICBl
bmQNCiAgICBzZWxmDQogIGVuZA0KICANCiAgIyB1bmRlZmluZSBzdGFuZGFyZCBtZXRob2RzIGhl
cmUNCmVuZA0KDQoNCmNsYXNzIEludm9rZXINCiAgZGVmIGZvbw0KICAgIENhbGxSZWNvcmRlci5u
ZXcgMiBkbyB8YXJnc3wNCiAgICAgIHByaW50ICJDaGFpbiBpcyAiLCBhcmdzLmluc3BlY3QsICJc
biINCiAgICBlbmQNCiAgZW5kDQplbmQNCg0KDQppID0gSW52b2tlci5uZXcNCmkuZm9vLmJhcigx
LDIpLmJheg0KDQppID0gSW52b2tlci5uZXcNCmkuZm9vLmJhcigxLDIpLmJhei5nb28NCg==
------=_Part_109270_27931451.1186383202752--
 
D

Dan

El Aug 4, 2007, a las 5:05 PM, Dan escribió:



Yeah, I would have written the same implementatcion than you to
support that usage.

Perhaps API.images would return a different class instead of API
itself with its own method missing depending on the role of that
API.images conceptually, but that would be essentially the same
approach anyway.

Looks fine to me.

-- fxn

Thanks - I appreciate your working this through with me.
 
D

Dan

This is what I'd do (attached). Note, you need to know the number of
invocations in this example. The pattern could however use other
criteria for triggering the action.

Kind regards

robert

call-recorder.rb
1KDownload

Hi Robert,

Thanks for the reply. I was wondering if that there was a way to do
this sort of thing in ruby without having to create a class instance
just to store the method calls. However, it looks like that's not
possible, which I kind of understand. Not perfect for my situation,
but it should be good enough.

Dan
 
Ad

Advertisements

R

Robert Klemme

2007/8/7 said:
Hi Robert,

Thanks for the reply. I was wondering if that there was a way to do
this sort of thing in ruby without having to create a class instance
just to store the method calls. However, it looks like that's not
possible, which I kind of understand. Not perfect for my situation,
but it should be good enough.

Can you explain why you are reluctant to create an instance for this?
Generally, creating and manipulating objects is all OO is about. :)
More specifically, since your remote call is likely an order of
magnitude slower than the instance creation performance effects are
negligible.

Kind regards

robert
 
D

Dan

Can you explain why you are reluctant to create an instance for this?
Generally, creating and manipulating objects is all OO is about. :)
More specifically, since your remote call is likely an order of
magnitude slower than the instance creation performance effects are
negligible.

Fair point :). Actually, I'm less concerned about performance then
code clarity. Since I'm just posting to a url, I only want to convert
the object-like notation to a string. Creating a bunch of objects just
to facilitate that notation seems like extra confusion.

Dan
 
R

Robert Klemme

Fair point :). Actually, I'm less concerned about performance then
code clarity. Since I'm just posting to a url, I only want to convert
the object-like notation to a string. Creating a bunch of objects just
to facilitate that notation seems like extra confusion.

We're not talking about a "bunch", are we? The reason I keep stressing
this point is that more often than not I have seen OO programs make too
few use of classes than making too much use of classes. Somehow people
seem to think a class is something big or heavyweight so it should be
avoided - or at least kept to a minimum. IMHO that leads to less
modularized and more difficult to read code. Stuffing everything into
only few classes that have a large number of methods makes things harder
to understand and reuse than having many classes which focus on only few
related tasks and that use other classes for unrelated tasks.

Sorry, I *had* to say it at some point in time. :)

Kind regards

robert
 
Ad

Advertisements

P

Paolo Nusco Perrotta

For example, if my ruby class is named API, I want to be able to call
API.images.find_by_name and have my class pass along "images" and
"find_by_name" to the remove service. I've got this working using two
method_missing calls (one as a class method and one as an instance
method), but it looks sloppy and I was wondering if there was a better
way to do this. Any ideas?

The flickr library (available via RubyGems) walks around the problem
by turning a Flickr API call such as:

flickr.tags.getListUser

into a Ruby call like:

flickr.tags_getListUser

so that it can forward the method to Flickr through a single
method_missing.

It also provides a more granular object model around the call, so
there is still some duplication left:

class Flickr
class User
def tags
# @client is a Flickr object
@client.tags_getListUser('user_id'=>@id)['who']['tags']
['tag'].collect { |tag| tag }
end
....

You might or might not like this, but it looks like a decent trade-off
to me. It's simple, and you can still make calls to the back-end that
are not directly supported by your Ruby code.
 

Top