Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Inspired by @JEG2's talk at Rubyconf... Any ruby object, as a webapp! 'Cause we can. :-)
require 'rubygems'
require 'rack'
class Object
def webapp
class << self
define_method :call do |env|
func, *attrs = env['PATH_INFO'].split('/').reject(&:empty?)
[200, {}, send(func, *attrs)]
end
end
self
end
end
Rack::Handler::Mongrel.run [].webapp, :Port => 9292
# ^^^^^^^^^^^
# | (x)
# ROFLSCALE DB ---/
#
# http://localhost:9292/push/1 -> 1
# http://localhost:9292/push/2 -> 12
# http://localhost:9292/push/3 -> 123
# http://localhost:9292/to_a -> 123
# http://localhost:9292/pop -> 3
# http://localhost:9292/shift -> 1
# Implementations in other languages (thanks guys!):
# Node.js: https://gist.github.com/700995
# Groovy: https://gist.github.com/702337
# Python: https://gist.github.com/702001
# Perl w/ plack: https://gist.github.com/703620
# Perl w/ continuity: https://gist.github.com/703651
# Io: https://gist.github.com/703431
# Great explanation of how this works in Ruby on Stackoverflow:
# http://stackoverflow.com/questions/4198883/exposing-any-ruby-object-over-the-web
@lukaszkorecki

This comment has been minimized.

Show comment
Hide comment

Amazing.

@ream88

This comment has been minimized.

Show comment
Hide comment
@ream88

ream88 Nov 15, 2010

Omg! Nice :)

ream88 commented Nov 15, 2010

Omg! Nice :)

@rkh

This comment has been minimized.

Show comment
Hide comment
@rkh

rkh Nov 15, 2010

Should be [200, {}, [send(func, *attrs).to_s]].

rkh commented Nov 15, 2010

Should be [200, {}, [send(func, *attrs).to_s]].

@yonkeltron

This comment has been minimized.

Show comment
Hide comment
@yonkeltron

yonkeltron Nov 15, 2010

I quite like this. However, you could do really horrible stuff with eval and friends.

I quite like this. However, you could do really horrible stuff with eval and friends.

@Oshuma

This comment has been minimized.

Show comment
Hide comment
@Oshuma

Oshuma Nov 16, 2010

I changed REQUEST_PATH to PATH_INFO since REQUEST_PATH always returns "/" on Webrick. Also added the #to_s() call as suggested by @rkh.

Updated gist: https://gist.github.com/701304

Oshuma commented Nov 16, 2010

I changed REQUEST_PATH to PATH_INFO since REQUEST_PATH always returns "/" on Webrick. Also added the #to_s() call as suggested by @rkh.

Updated gist: https://gist.github.com/701304

@tobi

This comment has been minimized.

Show comment
Hide comment
@technoweenie

This comment has been minimized.

Show comment
Hide comment
@technoweenie

technoweenie Nov 16, 2010

The roflcopter in the comments is blowing my mind.

The roflcopter in the comments is blowing my mind.

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Nov 16, 2010

@Oshuma: good stuff, will update the gist in a sec.

@tobi: with great power comes.. nah, actually, your example wouldn't work because the "/" in rm would get swallowed by split! :-)

@technoweenie: woot! that's exactly what I was going for.

Owner

igrigorik commented Nov 16, 2010

@Oshuma: good stuff, will update the gist in a sec.

@tobi: with great power comes.. nah, actually, your example wouldn't work because the "/" in rm would get swallowed by split! :-)

@technoweenie: woot! that's exactly what I was going for.

@drnic

This comment has been minimized.

Show comment
Hide comment
@drnic

drnic Nov 16, 2010

I love the roflcopter

drnic commented Nov 16, 2010

I love the roflcopter

@wilson

This comment has been minimized.

Show comment
Hide comment

wilson commented Nov 16, 2010

Running one at http://rofl.vcloudlabs.com/ have fun

@visnup

This comment has been minimized.

Show comment
Hide comment

visnup commented Nov 16, 2010

node.js version woo https://gist.github.com/700995

@JamieFlournoy

This comment has been minimized.

Show comment
Hide comment

J2EE version at http://bit.ly/YjoRf

@markiz

This comment has been minimized.

Show comment
Hide comment

markiz commented Nov 16, 2010

@JamieFlournoy
Damnit.

@verborghs

This comment has been minimized.

Show comment
Hide comment
@verborghs

verborghs Nov 16, 2010

@JamieFlournoy J2EE had these kind of security feature a long time ago if i remember correctly the spring bean introspector feature(http://www.springsource.com/security/cve-2010-1622) and it was more lines of code, so better

@JamieFlournoy J2EE had these kind of security feature a long time ago if i remember correctly the spring bean introspector feature(http://www.springsource.com/security/cve-2010-1622) and it was more lines of code, so better

@tingletech

This comment has been minimized.

Show comment
Hide comment
@tingletech

tingletech Nov 16, 2010

RickRoll spoiler: Don't click on the trollish "J2EE version" link from @JamieFlournoy

RickRoll spoiler: Don't click on the trollish "J2EE version" link from @JamieFlournoy

@jamesgolick

This comment has been minimized.

Show comment
Hide comment
@walteryu

This comment has been minimized.

Show comment
Hide comment
@walteryu

walteryu Nov 16, 2010

Seriously, the roflcopter is amazing.

Seriously, the roflcopter is amazing.

@ataylor284

This comment has been minimized.

Show comment
Hide comment

Here's a groovy version: https://gist.github.com/702337

@rafmagana

This comment has been minimized.

Show comment
Hide comment
@rafmagana

rafmagana Nov 16, 2010

what about this hehehe:

class Object

  DANGEROUS_METHODS = methods.grep(/eval|instance|module|method|send|taint|extend|include|freeze/)

  def webapp
    class << self
      define_method :call do |env|
        func, *attrs = env['PATH_INFO'].split('/').reject(&:empty?)
        result = DANGEROUS_METHODS.include?(func) ? 'METHOD NOT ALLOWED' : send(func, *attrs).to_s
        [200, {}, [result]]
      end
    end
    self
  end
end

what about this hehehe:

class Object

  DANGEROUS_METHODS = methods.grep(/eval|instance|module|method|send|taint|extend|include|freeze/)

  def webapp
    class << self
      define_method :call do |env|
        func, *attrs = env['PATH_INFO'].split('/').reject(&:empty?)
        result = DANGEROUS_METHODS.include?(func) ? 'METHOD NOT ALLOWED' : send(func, *attrs).to_s
        [200, {}, [result]]
      end
    end
    self
  end
end
@markiz

This comment has been minimized.

Show comment
Hide comment
@markiz

markiz Nov 16, 2010

@rafmagana
`
alias (unsure)

It should be a whitelist instead of black list, I believe.

markiz commented Nov 16, 2010

@rafmagana
`
alias (unsure)

It should be a whitelist instead of black list, I believe.

@rafmagana

This comment has been minimized.

Show comment
Hide comment
@rafmagana

rafmagana Nov 16, 2010

mmm, well, yes, it might be, the only thing is that we'd need an ALLOWED_METHODS per class or something like that, I mean, I wouldn't do the following in the Object class:

ALLOWED_METHODS = w%[a lot of methods of different classes]

well, I don't know, maybe, hehe

mmm, well, yes, it might be, the only thing is that we'd need an ALLOWED_METHODS per class or something like that, I mean, I wouldn't do the following in the Object class:

ALLOWED_METHODS = w%[a lot of methods of different classes]

well, I don't know, maybe, hehe

@mbleigh

This comment has been minimized.

Show comment
Hide comment
@mbleigh

mbleigh Nov 17, 2010

Might take some inspiration from this to make it actually possible to expose objects as Rack endpoints in Grape...still thinking of how to make it work exactly.

mbleigh commented Nov 17, 2010

Might take some inspiration from this to make it actually possible to expose objects as Rack endpoints in Grape...still thinking of how to make it work exactly.

@jaymcgavren

This comment has been minimized.

Show comment
Hide comment
@jaymcgavren

jaymcgavren Nov 17, 2010

$SAFE = 1 would disable a lot of the nastier methods. You'd still have to undef them in JRuby, though.

$SAFE = 1 would disable a lot of the nastier methods. You'd still have to undef them in JRuby, though.

@rafmagana

This comment has been minimized.

Show comment
Hide comment
@rafmagana

rafmagana Nov 17, 2010

woow ROaaS = Ruby Objects as a Service hehehe

woow ROaaS = Ruby Objects as a Service hehehe

@mtodd

This comment has been minimized.

Show comment
Hide comment
@mtodd

mtodd Nov 17, 2010

ROFLSCALE! \m/

mtodd commented Nov 17, 2010

ROFLSCALE! \m/

@Oshuma

This comment has been minimized.

Show comment
Hide comment
@igrigorik

This comment has been minimized.

Show comment
Hide comment
Owner

igrigorik commented Nov 17, 2010

@Oshuma: Nice! :-)

@jinleileiking

This comment has been minimized.

Show comment
Hide comment

nice!

@draegtun

This comment has been minimized.

Show comment
Hide comment
@draegtun

draegtun Nov 18, 2010

Very nice. Here are some implementations I did in Perl & Io:

https://gist.github.com/703620 - Perl using plack
https://gist.github.com/703651 - Perl using Continuity
https://gist.github.com/703431 - Io

Very nice. Here are some implementations I did in Perl & Io:

https://gist.github.com/703620 - Perl using plack
https://gist.github.com/703651 - Perl using Continuity
https://gist.github.com/703431 - Io

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Nov 18, 2010

@draegtun: awesome, thanks! Added your gist links to the one above.

Owner

igrigorik commented Nov 18, 2010

@draegtun: awesome, thanks! Added your gist links to the one above.

@lucj

This comment has been minimized.

Show comment
Hide comment
@lucj

lucj Nov 21, 2010

Hello, this is really great but there are some things I am not really sure. For instance, I cannot get the object class with http://localhost:9292/class.

After:
http://localhost:9292/push/1 -> 1
http://localhost:9292/push/2 -> 12
http://localhost:9292/push/3 -> 123
http://localhost:9292/to_a -> 123

I would expect
http://localhost:9292/class -> Array

Sorry if I'm wrong, just trying to understand :)

Regards,
Luc

lucj commented Nov 21, 2010

Hello, this is really great but there are some things I am not really sure. For instance, I cannot get the object class with http://localhost:9292/class.

After:
http://localhost:9292/push/1 -> 1
http://localhost:9292/push/2 -> 12
http://localhost:9292/push/3 -> 123
http://localhost:9292/to_a -> 123

I would expect
http://localhost:9292/class -> Array

Sorry if I'm wrong, just trying to understand :)

Regards,
Luc

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Nov 21, 2010

Luc, you have to be careful with your version of Ruby. Rack expects an object on which you can call "each". Under Ruby 1.9, String does not have an .each method. Chances are, Rack is erroring out because of that. A simple workaround would be wrap every returned object into a "StringIO.new(send(...).to_s))".

Owner

igrigorik commented Nov 21, 2010

Luc, you have to be careful with your version of Ruby. Rack expects an object on which you can call "each". Under Ruby 1.9, String does not have an .each method. Chances are, Rack is erroring out because of that. A simple workaround would be wrap every returned object into a "StringIO.new(send(...).to_s))".

@lucj

This comment has been minimized.

Show comment
Hide comment
@lucj

lucj Nov 22, 2010

Hello,
Hmm, sounds strange. In fact I'm using Ruby 1.8.7

luc@venus:~/Projects/rubyobject ruby --version
ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]

Should it work with this version ?
Thanks a lot for your help.
Regards,
Luc

lucj commented Nov 22, 2010

Hello,
Hmm, sounds strange. In fact I'm using Ruby 1.8.7

luc@venus:~/Projects/rubyobject ruby --version
ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]

Should it work with this version ?
Thanks a lot for your help.
Regards,
Luc

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Nov 23, 2010

Yep, 1.8.7 should work. What does your console output when you run the server and make that request?

Owner

igrigorik commented Nov 23, 2010

Yep, 1.8.7 should work. What does your console output when you run the server and make that request?

@lucj

This comment has been minimized.

Show comment
Hide comment
@lucj

lucj Nov 24, 2010

hmmm, you'r right, it's talking about the each method... but I'm running ruby 1.8.7

config.ru:1:in new' config.ru:1 Wed Nov 24 16:03:46 +0100 2010: Read error: #<NoMethodError: undefined methodeach' for Array:Class>
/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/chunked.rb:37:in each' /Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/handler/mongrel.rb:80:inprocess'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:159:in process_client' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:ineach'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:in `process_cli

lucj commented Nov 24, 2010

hmmm, you'r right, it's talking about the each method... but I'm running ruby 1.8.7

config.ru:1:in new' config.ru:1 Wed Nov 24 16:03:46 +0100 2010: Read error: #<NoMethodError: undefined methodeach' for Array:Class>
/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/chunked.rb:37:in each' /Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/handler/mongrel.rb:80:inprocess'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:159:in process_client' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:ineach'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:in `process_cli

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Nov 25, 2010

Right, you're seeing the problem I described below. You need to convert everything to a StringIO and then you're good to go.

Owner

igrigorik commented Nov 25, 2010

Right, you're seeing the problem I described below. You need to convert everything to a StringIO and then you're good to go.

@danny

This comment has been minimized.

Show comment
Hide comment
@danny

danny Dec 12, 2010

So I updated it a bit in my fork; it removes url encoding and returns json serialization of results

https://gist.github.com/737959

danny commented Dec 12, 2010

So I updated it a bit in my fork; it removes url encoding and returns json serialization of results

https://gist.github.com/737959

@igrigorik

This comment has been minimized.

Show comment
Hide comment
Owner

igrigorik commented Dec 12, 2010

@danny: nice!

@Burgestrand

This comment has been minimized.

Show comment
Hide comment
@Burgestrand

Burgestrand Jan 25, 2011

How come you didn’t settle with def self.call(env) instead?

require 'rack'

class Object
  def to_webapp
    def self.call(env)
      func, *attrs = env['PATH_INFO'].split('/').reject(&:empty?)
      [200, {}, send(func || :inspect, *attrs)]
    end
    self
  end
end

Rack::Handler::WEBrick.run [].to_webapp, :Port => 9292

How come you didn’t settle with def self.call(env) instead?

require 'rack'

class Object
  def to_webapp
    def self.call(env)
      func, *attrs = env['PATH_INFO'].split('/').reject(&:empty?)
      [200, {}, send(func || :inspect, *attrs)]
    end
    self
  end
end

Rack::Handler::WEBrick.run [].to_webapp, :Port => 9292
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment