On 06/10/10 15:57 -0400, Toby Crawley wrote:

The issue is that all three of these methods are defined on the class as closures over the action list for a particular object. So if you are iterating over a collection, *all* members of the collection will have the three methods defined for the *last* collection member, since the methods are defined at the class level. This results in:

irb> client.keys.collect {|x| [x.id, x.actions_urls]}
[
    [0] [
        [0] "somekey",
        [1] {
"destroy" => "http://localhost:8080/deltacloud/api/keys/default";
        }
    ],
    [1] [
        [0] "default",
        [1] {
"destroy" => "http://localhost:8080/deltacloud/api/keys/default";
        }
    ]
]

which means a call to client.keys.first.destroy! actually destroys client.keys.last.

xml_to_class has a good bit of meta-programming, and is difficult to decipher. Is there a reason why the client starts with a bare Class, and turns it in to Instance, Key, etc? I understand the motivation of taking some base class and creating Instance, Key, etc. dynamically, but wouldn't it make sense to have a base class that provided some functionality (written off the cuff, and untested):

class DeltaCloud::API::BaseObject

  def action_meta
    @action_meta ||= { }
  end

  def add_action(action, method, url)
    action_meta[action.to_s] = [method, url]
# you could also define_method here instead of relying on method_missing (below)
  end

  def actions
    actions_meta.keys
  end

  def actions_urls
action_meta.inject({}) { |accum, action, method_and_url| accum[action] = method_and_url.last; accum }
  end

  def call_action(action)
    method, url = action_meta[action]
    client.request(method.to_sym, url, {}, {})
    # would need to get status here
  end

  def method_missing(meth, *args, &block)
    if meth.to_s =~ /^(.*)!$/ and actions.include?($1)
      call_action($1)
    else
      super
    end
  end

end

Yes, that's true. I know that xml_to_class method is monstrous and it can
cause serious troubles like action_url issue. I'm totally open for
splitting this method to smallest chunks, which will be more maintainable
and at least readable.

I like idea of having:

  'BaseObject' (For holding 'reals', 'hardware_profiles' and stuff like this)
  |
  -> 'ActionObject' ('instances', 'buckets', 'volumes', 'keys')

So 'BaseObject' should hold all resource which is some kind of 'static',
means they doesn't have .action! methods. And 'ActionObject', which will
add 'actions'. Let me know what do you think.

DeltaCloud.define_class could then instead create a new class that subclassed the above class, and xml_to_class (for actions at least) becomes much simpler:

                when "actions":
                  actions = []
                  attribute.xpath('link').each do |link|
                    actions << [link['rel'], link[:href]]
                    define_method :"#{link['rel'].sanitize}!" do
client.request(:"#{link['method']}", link['href'], {}, {}) @current_state = client.send(:"#{item.name}", item['id']).state
                      obj.instance_eval do |o|
                        def state
                          @current_state
                        end
                      end
                    end
                  end
                  define_method :actions do
                    actions.collect { |a| a.first }
                  end
                  define_method :actions_urls do
                    urls = {}
                    actions.each { |a| urls[a.first] = a.last }
                    urls
                  end

would become (ignoring the 'state' method for now):

                when "actions":
                  attribute.xpath('link').each do |link|
obj.add_action(link['rel'].sanitize, link['method'], link[:href])
                  end

There is much more the base class would need to do in order to satisfy all of what xml_to_class, base_object, and other methods on the client set up, but you would end up with an implementation that would be more readable and more easily tested.

Absolutely agree.

  -- Michal

--
--------------------------------------------------------
Michal Fojtik, [email protected]
Deltacloud API: http://deltacloud.org
--------------------------------------------------------

Reply via email to