Forum: Ruby Desktop <-> Web

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Thomas S. (Guest)
on 2009-01-15 19:12
(Received via mailing list)
I want to interface a desktop application to a backend web
application. What's the easiest way to go about this --what's the best
way to communicate between the two?

Thanks,
T.
Aria S. (Guest)
on 2009-01-15 19:16
(Received via mailing list)
On Fri, 2009-01-16 at 02:09 +0900, Trans wrote:
> I want to interface a desktop application to a backend web
> application. What's the easiest way to go about this --what's the best
> way to communicate between the two?

SOAP and XML-RPC are both good options, as is a REST-style interface.
Net::HTTP on the client, whatever on the server.
Brian C. (Guest)
on 2009-01-15 23:40
Aria S. wrote:
> On Fri, 2009-01-16 at 02:09 +0900, Trans wrote:
>> I want to interface a desktop application to a backend web
>> application. What's the easiest way to go about this --what's the best
>> way to communicate between the two?
>
> SOAP and XML-RPC are both good options, as is a REST-style interface.
> Net::HTTP on the client, whatever on the server.

There are APIs on the client side which are higher level than Net::HTTP.

* xmlrpc is bundled with Ruby
* soap4r (shudder) is bundled with Ruby
* ActiveResource is a REST client

Which you use typically depends on what the backend application
provides. Note that all three use XML, and so are inefficient for large
binary transfers, as you have to base64 encode them.

If you are writing both front and backend, you could consider other
protocols (e.g. DRb). That's harder to scale though, whereas it's easy
to set up a HTTP proxy for load-balancing.

The above are all synchronous protocols. If asynchronous semantics suit
you better (e.g. submit a request, poll for the response later) then you
could talk to a queuing system, e.g.
* Memcache protocol - Starling
* AMQP protocol - various servers inc RabbitMQ
* Stomp protocol over HTTP - various servers inc Apache ActiveMQ
* ... lots of others

Another option is to use XMPP (Jabber) as the transport. There is xmpp4r
as endpoint, and a selection of XMPP servers to route your messages
through. xmlrpc can run over XMPP. But this is likely to be overkill for
a single-server scenario.

Finally, consider seriously the possibility of using a plain web browser
as the client, using HTML and AJAX over HTTP. It's maybe a bit harder to
build an application with a desktop "feel" but it is doable (see Google
Docs), and you don't need to distribute any code to the clients.

Rails has particularly good support for this, and can generate most of
the necessary Javascript for you. However if you're not already a Rails
user, there's a steep learning curve to get there.
James G. (Guest)
on 2009-01-15 23:48
(Received via mailing list)
On Jan 15, 2009, at 3:39 PM, Brian C. wrote:

> * soap4r (shudder) is bundled with Ruby

Ruby 1.8.  I believe it's gone in Ruby 1.9.

James Edward G. II
Martin DeMello (Guest)
on 2009-01-16 07:25
(Received via mailing list)
On Thu, Jan 15, 2009 at 10:39 PM, Trans <removed_email_address@domain.invalid> 
wrote:
> I want to interface a desktop application to a backend web
> application. What's the easiest way to go about this --what's the best
> way to communicate between the two?

Are you writing the webapp too?

martin
Thomas S. (Guest)
on 2009-01-16 12:20
(Received via mailing list)
On Jan 16, 12:20 am, Martin DeMello <removed_email_address@domain.invalid> 
wrote:
> On Thu, Jan 15, 2009 at 10:39 PM, Trans <removed_email_address@domain.invalid> wrote:
> > I want to interface a desktop application to a backend web
> > application. What's the easiest way to go about this --what's the best
> > way to communicate between the two?
>
> Are you writing the webapp too?

yes.
Martin DeMello (Guest)
on 2009-01-16 13:41
(Received via mailing list)
On Fri, Jan 16, 2009 at 3:48 PM, Trans <removed_email_address@domain.invalid> 
wrote:
> yes.
I'd suggest returning data from the webapp as json, then, and reading
it as yaml from the ruby end. I did that in a recent project at work
and it was pretty painless. The benefit is if you want to build a rich
browser-based frontend later, client side javascript can use the same
json objects.

martin
Thomas S. (Guest)
on 2009-01-16 14:21
(Received via mailing list)
On Jan 16, 6:39 am, Martin DeMello <removed_email_address@domain.invalid> wrote:

> I'd suggest returning data from the webapp as json, then, and reading
> it as yaml from the ruby end. I did that in a recent project at work
> and it was pretty painless. The benefit is if you want to build a rich
> browser-based frontend later, client side javascript can use the same
> json objects.

I thought about this as well, but how did you go about transporting
the json?

At the moment I've taken Aria's suggestion of using XML-RPC, that's
working fine via the XMLRPC::WEBrickServlet. But ultimately I want to
use Rack instead. An example of a simple Rack-based JSON servlet would
be awesome. Anyone have one?

T.
Thomas S. (Guest)
on 2009-01-16 14:33
(Received via mailing list)
On Jan 15, 4:39 pm, Brian C. <removed_email_address@domain.invalid> wrote:
>
> to set up a HTTP proxy for load-balancing.
> as endpoint, and a selection of XMPP servers to route your messages
> user, there's a steep learning curve to get there.
Thanks Brian. Lots of useful info hear. I actually did create web-
browser client at first, but the problem is that I needed to save
files to the local file system, and that was just too cumbersome with
a web-client --it required that I had a local server running too,
which added additional headaches. I considered something like Google
Gears, Adobe Air, etc. but that ended real quick when I discovered
none of them supported 64bit Linux, which I run.

(Aside Rant: What the hell is going on with 64 bit support btw? Why
the hell are we stuck in 32-bit world? I mean how long has has it
bloody been already since 64 CPUs hit the mass-market? It's just
intolerable! Honestly, I can't say enough bad things about this state
of affairs. It really has to be the pinnacle example of how the whole
industry has gone to hell in a hand basket.)

So I'm using Shoes on the front-end. And for the most part that's
working pretty good so far.

T.
Brian C. (Guest)
on 2009-01-16 15:48
Thomas S. wrote:
> So I'm using Shoes on the front-end. And for the most part that's
> working pretty good so far.

Makes sense.

Some years ago I wrote a system which used DRb over HTTP: I had to
modify the DRb code slightly to do this (adding support for fastcgi
server processes). That was nice as it let me use native Ruby objects,
but if hashes containing just text are OK for you (and make sure they're
only ASCII or UTF-8 text), xmlrpc should be fine.

I did come across YAML RPC once; there's some code floating around on
the net for that, but it's possibly stale now.

I don't know if there's any sort of standard for "JSON RPC". I suspect
it just means REST with JSON response objects. If you want to try that,
and to keep as close to Rack as possible, then I suggest layering
something very lightweight like Sinatra on top of it. Of course, Rails
has render :json. Remember too that json supports *only* Unicode
strings, so anything binary needs base64 encoding.

Don't forget that your backend app will probably need some sort of
authentication framework, possibly just HTTP Basic Auth for simplicity,
to stop random clients making requests.

Even if you don't use a browser as a front-end, you might be able to
steal some ideas from those who do. e.g.
http://beebole.com/blog/pure/generate-html-from-a-...

Anyway, that's just a few random thoughts.

Regards,

Brian.
Brian C. (Guest)
on 2009-01-20 12:19
Thomas S. wrote:
> ultimately I want to
> use Rack instead. An example of a simple Rack-based JSON servlet would
> be awesome. Anyone have one?

How about this with Sinatra, which sits on top of Rack:

--- myapp.rb ---
require 'rubygems'
require 'sinatra'
require 'json'
require 'widget'

get '/widgets.json' do
  Widget.all.to_json
end

get '/widgets/:id.json' do
  Widget.find(Integer(params[:id])).to_json
end

post '/widgets.json' do
  item = JSON.parse(request.body.read)
  raise "Bad object" unless item.is_a? Widget
  Widget.add(item).to_s
end

--- widget.rb ---
# Rubbish model, not thread-safe!
class Widget
  attr_accessor :id, :name, :price
  def initialize(id, name, price)
    @id, @name, @price = id, name, price
  end
  def to_json(*a)
    {
      'json_class' => self.class.name,
      'data' => [ @id, @name, @price ],
    }.to_json(*a)
  end
  def self.json_create(o)
    new *o['data']
  end
  def self.all
    @all ||= []
  end
  def self.add(item)
    @seq ||= 0
    @seq += 1
    item.id = @seq
    all << item
    return @seq
  end
  def self.create(*args)
    add(new(*args))
  end
  def self.find(id)
    all.find { |item| item.id == id }
  end
end
Widget.create(nil, "flurble", 12.3)
Widget.create(nil, "boing", 4.56)
Brian C. (Guest)
on 2009-01-20 12:38
P.S. I'm not sure what is the "right" way to serialize Ruby objects in
JSON.

I took the to_json and json_create code from the ruby json library
documentatation, which gives

    {"json_class": "name", "data": ["values", ...]}

Maybe it would be more natural to serialize a Ruby object as
{"attr_name": "attr_value", ...}, since { ... } itself denotes an
"object" according to the JSON specification.

However I've also seen examples on the web using a hybrid:

    {"json_class": "name", data: {"attr_name": "attr_value", ... } }
James G. (Guest)
on 2009-01-20 15:56
(Received via mailing list)
On Jan 20, 2009, at 4:37 AM, Brian C. wrote:

> P.S. I'm not sure what is the "right" way to serialize Ruby objects in
> JSON.

I think it's probably better to think of JSON as simple primitives.
Thus JSON's "object" would map to Ruby's Hash.  From there, I just
create an output method that translates my objects to primitives and
an input method that restores them.

Most libraries do seem to try to dump everything though, so maybe this
is just flawed thinking on my part.

James Edward G. II
Brian C. (Guest)
on 2009-01-20 16:12
James G. wrote:
> I think it's probably better to think of JSON as simple primitives.
> Thus JSON's "object" would map to Ruby's Hash.  From there, I just
> create an output method that translates my objects to primitives and
> an input method that restores them.

For a POST it seems to make sense to think of JSON as a "params" hash,
like a form post or an XML-wrapped hash.

Following that logic, PUT ought to be the same, and hence GET. And the
'class' is implicit in the REST URL: /widgets/ or whatever.

OK, I'm sold, for web use anyway. Maybe the documentation in the JSON
library is a bit misleading then.
Brian C. (Guest)
on 2009-01-20 17:06
That simplifies things a bit. Here it is for completeness:

--- myapp.rb ---
require 'rubygems'
require 'sinatra'
require 'json'
require 'widget'

get '/widgets.json' do
  Widget.all.to_json
end

get '/widgets/:id.json' do
  Widget.find(Integer(params[:id])).to_json
end

post '/widgets.json' do
  item = JSON.parse(request.body.read)
  Widget.create(item).to_s
end

--- widget.rb ---
# Rubbish model, not thread-safe!
class Widget
  attr_accessor :id, :name, :price
  def initialize(params)
    @id    = params["id"]
    @name  = params["name"]
    @price = params["price"]
  end
  def to_json(*a)
    instance_variables.inject({}) { |h,v|
      h[v.to_s[1..-1]] = instance_variable_get(v)
      h
    }.to_json(*a)
  end

  @all = []
  @seq = 0
  def self.all
    @all
  end
  def self.add(item)
    item.id = (@seq += 1)
    all << item
    return @seq
  end
  def self.create(params)
    add(new(params))
  end
  def self.find(id)
    all.find { |item| item.id == id }
  end
end
Widget.create("name" => "flurble", "price" => 12.3)
Widget.create("name" => "boing", "price" => 4.56)

If you require 'json/add/rails' then you don't need to define your own
to_json method, but it also allows people to create arbitrary Ruby
objects on your machine (which makes me uncomfortable)
Thomas S. (Guest)
on 2009-01-23 23:14
(Received via mailing list)
On Jan 20, 10:05 am, Brian C. <removed_email_address@domain.invalid> wrote:
> end
> --- widget.rb ---
>       h[v.to_s[1..-1]] = instance_variable_get(v)
>     item.id = (@seq += 1)
> Widget.create("name" => "flurble", "price" => 12.3)
> Widget.create("name" => "boing", "price" => 4.56)
>
> If you require 'json/add/rails' then you don't need to define your own
> to_json method, but it also allows people to create arbitrary Ruby
> objects on your machine (which makes me uncomfortable)

Thanks. I'll give it a go.

T.
Brian C. (Guest)
on 2009-01-23 23:23
I also learned from the Sinatra list that there is a Rack middleware
module you can load which which will perform the parsing of JSON POST
data for you, so it just ends up in params. Tidy.

http://groups.google.com/group/sinatrarb/msg/65791...

http://github.com/rack/rack-contrib/blob/44322288f...
Hemant K. (Guest)
on 2009-01-24 05:46
(Received via mailing list)
On Sat, Jan 24, 2009 at 2:51 AM, Brian C. <removed_email_address@domain.invalid>
wrote:
> I also learned from the Sinatra list that there is a Rack middleware
> module you can load which which will perform the parsing of JSON POST
> data for you, so it just ends up in params. Tidy.
>
> http://groups.google.com/group/sinatrarb/msg/65791...
>
> 
http://github.com/rack/rack-contrib/blob/44322288f...

If sole purpose is to ferry back JSON back forth, one may have a look
at:

http://halcyon.rubyforge.org/

Also, why bother converting JSON to YAML on ruby side? Marshalling
Ruby objects to JSON is equally easier. If you are using AR or
something it already has support for that, otherwise JSON gem already
comes with support for marshalling built-in types which can be easily
extended to custom classes with to_json method.
Matt T. (Guest)
on 2009-01-24 17:54
(Received via mailing list)
There's not much in Halcyon you'll find beyond what you've already
learned
here. The interesting bit, I guess, would be the
PostBodyContentTypeParser
middleware. Apologies for the name, haha. I wrote the middleware to
compliment Halcyon since everything coming out of it was JSON I figured
it
should at least support it coming in.
Halcyon behaves very similar to what's going on in your application...
essentially it breaks down to a simplistic pattern: always have a
resource
(a hash or an array of hashes, for example) to respond with as JSON
objects.
A lot of the time it was just calling #to_json on an object as the body
of
the response. Aside from that, everything is as you'd expect.

I recommend JSON wholeheartedly.

Matt



On Fri, Jan 23, 2009 at 10:41 PM, hemant <removed_email_address@domain.invalid> 
wrote:

>
>
--
Matt T.
Highgroove Studios
www.highgroove.com
cell: 404-314-2612
blog: maraby.org

Scout - Web Monitoring and Reporting Software
www.scoutapp.com
This topic is locked and can not be replied to.