Seeking advice

Discussion in 'Ruby' started by Alex Rothbard, Apr 5, 2011.

  1. Hi. I am writing a library that will have the ability to access a server
    by many different "transports" (JSON over HTTP, Thrift, etc). Depending
    on which transport the user chooses, different files and classes will
    need to be loaded.

    A complete implementation of a transport requires that three classes
    (such as the 'Grid' class below) be implemented from their respective
    base class. Additionally, if the user wants to use JSON over HTTP, they
    shouldn't need to depend on the thrift gem and vice versa. These
    transport classes are used by other classes within the library, and
    should never be used directly by the user.

    What is the best way to allow the user of the library to specify a
    transport at runtime? Could I wrap all my implemented transport classes
    in a module and do something like this?:

    def initialize(transport)
    @t = transport
    @grid = @t::Grid.new
    end

    where "transport" could be "MyProject::Transport::Thrift".

    Is this a good idea? Is there a better way?

    --
    Posted via http://www.ruby-forum.com/.
     
    Alex Rothbard, Apr 5, 2011
    #1
    1. Advertising

  2. On Tue, Apr 5, 2011 at 3:22 PM, Alex Rothbard <> wrote:
    > Hi. I am writing a library that will have the ability to access a server
    > by many different "transports" (JSON over HTTP, Thrift, etc). Depending
    > on which transport the user chooses, different files and classes will
    > need to be loaded.
    >
    > A complete implementation of a transport requires that three classes
    > (such as the 'Grid' class below) be implemented from their respective
    > base class. Additionally, if the user wants to use JSON over HTTP, they
    > shouldn't need to depend on the thrift gem and vice versa. These
    > transport classes are used by other classes within the library, and
    > should never be used directly by the user.
    >
    > What is the best way to allow the user of the library to specify a
    > transport at runtime? Could I wrap all my implemented transport classes
    > in a module and do something like this?:
    >
    > def initialize(transport)
    > =A0@t =3D transport
    > =A0@grid =3D @t::Grid.new
    > end
    >
    > where "transport" could be "MyProject::Transport::Thrift".
    >
    > Is this a good idea? Is there a better way?


    I'd go for factory pattern.

    def initialize(transport)
    @t =3D transport
    @grid =3D @t.grid
    @grid =3D @t.grid.new # alternative
    end

    All transports must implement the particular set of methods of course.

    Kind regards

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Apr 5, 2011
    #2
    1. Advertising

  3. On Tue, 5 Apr 2011 22:22:17 +0900
    Alex Rothbard <> wrote:

    > What is the best way to allow the user of the library to specify a
    > transport at runtime?


    Use factory pattern

    The user should call something like

    MyProject::Transport::ThriftFactory.build

    Then they don't need to find the transport themselves.
    But it should be obvious what ThriftFactory returns.

    The following might be how you would do it, up to you really :)

    # I don't know what this is about, so I'm calling your class Foo here
    class Foo

    # My Foo transports all the data to the yard
    def initialize(transport)
    @t = transport
    @grid = @t::Grid.new
    end

    end

    # Factory to build the Foo which uses the Thrift Transporter
    class ThriftFactory

    # Users can make Foos that transport with Thrift without having to
    # find the Thrift themselves
    def build
    Foo.new MyProject::Transport::Thrift
    end

    end
     
    Johnny Morrice, Apr 5, 2011
    #3
  4. On Tue, Apr 5, 2011 at 3:22 PM, Alex Rothbard <> wrote:
    > A complete implementation of a transport requires that three classes
    > (such as the 'Grid' class below) be implemented from their respective
    > base class.


    > These transport classes are used by other classes within the
    > library, and should never be used directly by the user.


    I interpreted this to mean that the base grid class would not be part
    of the interface presented to the user either. Is this correct?

    On Tue, 5 Apr 2011 22:39:33 +0900
    Robert Klemme <> wrote:
    > def initialize(transport)
    > @t = transport
    > @grid = @t.grid
    > @grid = @t.grid.new # alternative
    > end


    Cus the way Robert saw it was that the transport should be the factory
    for the grid, and the user will see the grid. The transport, which is
    the factory for the grid, is fed to the object.

    But the way I saw it was that the transport should be created by the
    factory and so the user should see no grid at all. The factory
    feeds the transport to the object.

    Have I missed the point? More importantly, have you made the code win?

    Cheers
    Johnny
     
    Johnny Morrice, Apr 5, 2011
    #4
  5. On Tue, Apr 5, 2011 at 4:44 PM, Johnny Morrice <> wrot=
    e:
    > On Tue, Apr 5, 2011 at 3:22 PM, Alex Rothbard <> wrote:
    >> A complete implementation of a transport requires that three classes
    >> (such as the 'Grid' class below) be implemented from their respective
    >> base class.

    >
    >> These transport classes are used by other classes within the
    >> library, and should never be used directly by the user.

    >
    > I interpreted this to mean that the base grid class would not be part
    > of the interface presented to the user either. Is this correct?
    >
    > On Tue, 5 Apr 2011 22:39:33 +0900
    > Robert Klemme <> wrote:
    >> def initialize(transport)
    >> =A0@t =3D transport
    >> =A0@grid =3D @t.grid
    >> =A0@grid =3D @t.grid.new # alternative
    >> end

    >
    > Cus the way Robert saw it was that the transport should be the factory
    > for the grid, and the user will see the grid. =A0The transport, which is
    > the factory for the grid, is fed to the object.
    >
    > But the way I saw it was that the transport should be created by the
    > factory and so the user should see no grid at all. =A0The factory
    > feeds the transport to the object.


    We probably need more context. In an attempt to create a bigger example:

    $ ./TrueGrit.rb
    sending "basic message" across TheLibrary::JasonTransport
    closing TheLibrary::JasonTransport
    waiting for two weeks to transmit "basic message" via TheLibrary::CarrierPi=
    geon
    TheLibrary::CarrierPigeon is flying home
    $ cat -n TrueGrit.rb
    1 #!/bin/env ruby19
    2
    3 module TheLibrary
    4
    5 class BaseTransport
    6 def self.open(*params)
    7 grid =3D new(*params)
    8
    9 if block_given?
    10 begin
    11 yield grid
    12 ensure
    13 grid.close
    14 end
    15 else
    16 grid
    17 end
    18 end
    19 end
    20
    21 class JasonTransport < BaseTransport
    22 def send(msg)
    23 printf "sending %p across %p\n", msg, self.class
    24 end
    25
    26 def close
    27 printf "closing %p\n", self.class
    28 end
    29 end
    30
    31 class ThriftTransport < BaseTransport
    32 def send(msg)
    33 printf "sending %p across %p\n", msg, self.class
    34 end
    35
    36 def close
    37 printf "%p says, man I'm glad I get home\n", self.class
    38 end
    39 end
    40
    41 class CarrierPigeon < BaseTransport
    42 def send(msg)
    43 printf "waiting for two weeks to transmit %p via %p\n",
    msg, self.class
    44 end
    45
    46 def close
    47 printf "%p is flying home\n", self.class
    48 end
    49 end
    50
    51 end
    52
    53 include TheLibrary;
    54
    55 JasonTransport.open:)host =3D> "foo.bar.com") do |grid|
    56 grid.send("basic message")
    57 end
    58
    59 CarrierPigeon.open:)destination =3D> "Stockholm") do |grid|
    60 grid.send("basic message")
    61 end
    $

    It really does not matter what method #new of the factory returns (or
    whether it's called "new" at all). Basically one needs a defined
    interface (note that I left some flexibility in there in order to be
    able to provide different configurations depending on transport used).
    Also, the approach with open is just an example how to do this in the
    same manner as File and IO and other classes work which use a block
    for safe resource deallocation.

    > Have I missed the point? =A0More importantly, have you made the code win?


    I'm not sure what "win" means in this context...

    Kind regards

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Apr 5, 2011
    #5
  6. The factory design pattern is sort of what I want, but not exactly.
    Remember that there are three different classes (services) that need to
    take advantage of a transport. The library is broken down like this:

    There is a high level class that represents a user's overall account.
    The user uses this class only. This account class needs to access three
    different "services" (classes): the grid, the archive, and the
    transaction service. All three services need to communicate with the
    remote server using a transport.

    In the example above, the transport is providing methods for accessing
    the grid, but I am thinking of it in another way: the
    grid/archive/transaction classes should provide methods which correspond
    to the API on the remote server, and those classes then use the selected
    transport to send it across.

    In short, I have a server library which offers up three sets of APIs
    (one for each service), and I want to be able to allow the end user to
    easily choose which transport the client library uses when accessing
    these services.

    --
    Posted via http://www.ruby-forum.com/.
     
    Alex Rothbard, Apr 5, 2011
    #6
  7. On Tue, Apr 5, 2011 at 8:30 PM, Alex Rothbard <> wrote:
    > The factory design pattern is sort of what I want, but not exactly.
    > Remember that there are three different classes (services) that need to
    > take advantage of a transport. The library is broken down like this:
    >
    > There is a high level class that represents a user's overall account.
    > The user uses this class only. This account class needs to access three
    > different "services" (classes): the grid, the archive, and the
    > transaction service. All three services need to communicate with the
    > remote server using a transport.
    >
    > In the example above, the transport is providing methods for accessing
    > the grid, but I am thinking of it in another way: the
    > grid/archive/transaction classes should provide methods which correspond
    > to the API on the remote server, and those classes then use the selected
    > transport to send it across.
    >
    > In short, I have a server library which offers up three sets of APIs
    > (one for each service), and I want to be able to allow the end user to
    > easily choose which transport the client library uses when accessing
    > these services.


    Then something like:

    class Grid
    TRANSPORTS = {:json => JsonTransport, :thrift => ThriftTransport}

    def initialize transport, params
    @transport = TRANSPORTS[transport]
    # ... initialize the transport with params if required or whatever
    end

    def method_xxx param1, param2
    @transport.call_remote_method:)xxx, param1, param2) # the
    transport defines how to call remote methods, I guess
    end
    end

    grid = Grid.new :json, {:url => "http://my.json.service"}
    grid.method_xxx ("a", "b")

    If you don't want the client to know about transport params, you can
    hide the configuration inside the Grid class, or read it from a file
    or something.

    Jesus.
     
    Jesús Gabriel y Galán, Apr 6, 2011
    #7
  8. On Tue, Apr 5, 2011 at 8:30 PM, Alex Rothbard <> wrote:
    > The factory design pattern is sort of what I want, but not exactly.
    > Remember that there are three different classes (services) that need to
    > take advantage of a transport. The library is broken down like this:


    I'm not convinced we are actually that far away from what you want.

    > There is a high level class that represents a user's overall account.
    > The user uses this class only. This account class needs to access three
    > different "services" (classes): the grid, the archive, and the
    > transaction service. All three services need to communicate with the
    > remote server using a transport.


    Do all three services need their own implementation of each transport
    (i.e. thrift for grid, thrift for archive, thrift for TX) or do you
    only need / want a single transport implementation per service?

    > In the example above, the transport is providing methods for accessing
    > the grid, but I am thinking of it in another way: the


    Well, that's just a name. You can replace it by "connection". :)

    > grid/archive/transaction classes should provide methods which correspond
    > to the API on the remote server, and those classes then use the selected
    > transport to send it across.
    >
    > In short, I have a server library which offers up three sets of APIs
    > (one for each service), and I want to be able to allow the end user to
    > easily choose which transport the client library uses when accessing
    > these services.


    It would be important to know when the user must decide about the
    transport. Do you want to have him do it initially when he opens his
    account? Do you want to allow for later changes of the transport? Or
    do you even want to use a different transport per interaction? Also,
    how do you want your transports to work: should they be connection
    oriented or message oriented? This will determine whether you need
    something like a connection object you must store somewhere.

    Kind regards

    robert

    --
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Apr 6, 2011
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. bill
    Replies:
    3
    Views:
    687
  2. iksrazal
    Replies:
    2
    Views:
    350
    enrique
    Apr 27, 2005
  3. Steve W. Jackson

    Seeking class hierarchy advice

    Steve W. Jackson, Apr 26, 2006, in forum: Java
    Replies:
    1
    Views:
    282
    Chris Uppal
    Apr 27, 2006
  4. Dave
    Replies:
    2
    Views:
    372
    David Harmon
    Apr 13, 2004
  5. Eric
    Replies:
    0
    Views:
    301
Loading...

Share This Page