crohr-restfully 0.0.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -8,9 +8,10 @@ begin
8
8
  gem.summary = %Q{Experimental code for auto-generation of wrappers on top of RESTful APIs that follow some specific conventions.}
9
9
  gem.description = %Q{Experimental code for auto-generation of wrappers on top of RESTful APIs that follow HATEOAS principles and provide OPTIONS methods and/or Allow headers.}
10
10
  gem.email = "cyril.rohr@gmail.com"
11
- gem.homepage = "http://github.com/cryx/restfully"
11
+ gem.homepage = "http://github.com/crohr/restfully"
12
12
  gem.authors = ["Cyril Rohr"]
13
- gem.add_dependency "rest-client"
13
+ gem.add_dependency "rest-client"
14
+
14
15
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
16
  end
16
17
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.1.1
data/bin/restfully ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+ # The command line Restfully client
3
+
4
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
5
+
6
+ require 'restfully'
7
+ require 'optparse'
8
+ require 'logger'
9
+ require 'pp'
10
+
11
+ @options = {}
12
+ option_parser = OptionParser.new do |opts|
13
+ opts.banner = "Usage: restfully base_url [root_uri] [options]"
14
+
15
+ opts.on("-u=", "--username=", "Sets the username") do |u|
16
+ @options['username'] = u
17
+ end
18
+ opts.on("-p=", "--password=", "Sets the user password") do |p|
19
+ @options['password'] = p
20
+ end
21
+ opts.on("-c=", "--config=", "Sets the various options based on a custom YAML configuration file") do |v|
22
+ @options['configuration_file'] = v
23
+ end
24
+ opts.on("-v", "--verbose", "Run verbosely") do |v|
25
+ @options['verbose'] = v
26
+ end
27
+ opts.on("--log=", "Outputs log messages to the given file. Defaults to stdout") do |v|
28
+ @options['log'] = v
29
+ end
30
+
31
+ opts.on_tail("-h", "--help", "Show this message") do
32
+ puts opts
33
+ exit
34
+ end
35
+
36
+ end
37
+
38
+ option_parser.parse!
39
+
40
+ @base_url = ARGV.shift
41
+ @root_uri = ARGV.shift || "/"
42
+
43
+ if (config_filename = @options.delete('configuration_file')) && File.exists?(File.expand_path(config_filename))
44
+ config = YAML.load_file(File.expand_path(config_filename))
45
+ @base_url = config.delete('base_url') || @base_url
46
+ @root_uri = config.delete('root_uri') || @root_uri
47
+ @options.merge!(config)
48
+ end
49
+
50
+ unless @base_url
51
+ $stderr.puts option_parser.help
52
+ exit(-1)
53
+ else
54
+ if (log_file=@options.delete('log'))
55
+ @logger = Logger.new(File.expand_path(log_file))
56
+ else
57
+ @logger = Logger.new(STDOUT)
58
+ end
59
+ if @options.delete('verbose')
60
+ @logger.level = Logger::DEBUG
61
+ else
62
+ @logger.level = Logger::WARN
63
+ end
64
+
65
+ def session
66
+ @session ||= Restfully::Session.new(@base_url, @options.merge('root' => @root_uri, 'logger' => @logger))
67
+ end
68
+ def root
69
+ @root ||= Restfully::Resource.new(session.root, session).load
70
+ end
71
+
72
+ root # preloads
73
+
74
+ require 'irb'
75
+ require 'irb/completion'
76
+
77
+ ARGV.clear
78
+ IRB.start
79
+ exit!
80
+ end
data/examples/grid5000.rb CHANGED
@@ -9,7 +9,7 @@ logger.level = Logger::INFO
9
9
  # Restfully.adapter = Restfully::HTTP::RestClientAdapter
10
10
  # Restfully.adapter = Patron::Session
11
11
  RestClient.log = 'stdout'
12
- Restfully::Session.new('https://localhost:3443/sid', 'root' => '/versions/current', 'logger' => logger) do |grid, session|
12
+ Restfully::Session.new('https://localhost:3443/sid', 'root' => '/grid5000', 'logger' => logger) do |grid, session|
13
13
  grid_stats = {'hardware' => {}, 'system' => {}}
14
14
  grid.sites.each do |site_uid, site|
15
15
  site_stats = site.status.inject({'hardware' => {}, 'system' => {}}) {|accu, (node_uid, node_status)|
@@ -24,7 +24,4 @@ Restfully::Session.new('https://localhost:3443/sid', 'root' => '/versions/curren
24
24
  p [:total, grid_stats]
25
25
  puts "Getting status of a few nodes in rennes:"
26
26
  pp grid.sites['rennes'].status(:query => {:only => ['paradent-1', 'paradent-10', 'paramount-3']})
27
-
28
- # TODO: Auto discovery of allowed HTTP methods: create raises an error if not available, otherwise POSTs the given object (and auto-select the content-type)
29
- # grid.sites["rennes"].jobs.create({:walltime => 120, :whatever => ''})
30
27
  end
data/lib/restfully.rb CHANGED
@@ -1,41 +1,12 @@
1
- class BasicObject #:nodoc:
2
- instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
3
- end unless defined?(BasicObject)
4
-
5
- # monkey patching:
6
- class Hash
7
- def symbolize_keys
8
- inject({}) do |options, (key, value)|
9
- options[(key.to_sym rescue key) || key] = value
10
- options
11
- end
12
- end
13
-
14
- def to_params
15
- params = ''
16
-
17
- each do |k, v|
18
- if v.is_a?(Array)
19
- params << "#{k}=#{v.join(",")}&"
20
- else
21
- params << "#{k}=#{v.to_s}&"
22
- end
23
- end
24
-
25
- params.chop!
26
- params
27
- end
28
-
29
- end
30
1
 
31
2
  module Restfully
32
3
  class RestfullyError < StandardError; end
33
4
  end
34
5
 
35
- require File.dirname(__FILE__)+'/restfully/parsing'
36
- require File.dirname(__FILE__)+'/restfully/session'
37
- require File.dirname(__FILE__)+'/restfully/special_hash'
38
- require File.dirname(__FILE__)+'/restfully/special_array'
39
- require File.dirname(__FILE__)+'/restfully/link'
40
- require File.dirname(__FILE__)+'/restfully/resource'
41
- require File.dirname(__FILE__)+'/restfully/collection'
6
+ require 'restfully/extensions'
7
+ require 'restfully/session'
8
+ require 'restfully/special_hash'
9
+ require 'restfully/special_array'
10
+ require 'restfully/link'
11
+ require 'restfully/resource'
12
+ require 'restfully/collection'
@@ -32,6 +32,7 @@ module Restfully
32
32
  self
33
33
  else
34
34
  @raw = session.get(path, options) if raw.nil? || force_reload
35
+ @items.clear
35
36
  raw.each do |key, value|
36
37
  next if key == 'links'
37
38
  self_link = (value['links'] || []).map{|link| Link.new(link)}.detect{|link| link.self?}
@@ -46,6 +47,21 @@ module Restfully
46
47
  end
47
48
  end
48
49
 
50
+ def inspect(options = {:space => " "})
51
+ output = "#<#{self.class}:0x#{self.object_id.to_s(16)}"
52
+ if loaded?
53
+ output += "\n#{options[:space]}------------ META ------------"
54
+ output += "\n#{options[:space]}@uri: #{uri.inspect}"
55
+ unless @items.empty?
56
+ output += "\n#{options[:space]}------------ PROPERTIES ------------"
57
+ @items.each do |key, value|
58
+ output += "\n#{options[:space]}#{key.inspect} => #{value.class.name}"
59
+ end
60
+ end
61
+ end
62
+ output += ">"
63
+ end
64
+
49
65
 
50
66
  end
51
67
  end
@@ -0,0 +1,41 @@
1
+ class BasicObject #:nodoc:
2
+ instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
3
+ end unless defined?(BasicObject)
4
+
5
+ # monkey patching:
6
+ class Hash
7
+ # Taken from ActiveSupport
8
+ def symbolize_keys
9
+ inject({}) do |options, (key, value)|
10
+ options[(key.to_sym rescue key) || key] = value
11
+ options
12
+ end
13
+ end
14
+
15
+ # Taken from ActiveSupport
16
+ def stringify_keys
17
+ inject({}) do |options, (key, value)|
18
+ options[key.to_s] = value
19
+ options
20
+ end
21
+ end
22
+
23
+
24
+ # This is by no means the standard way to transform ruby objects into query parameters
25
+ # but it is targeted at our own needs
26
+ def to_params
27
+ params = ''
28
+
29
+ each do |k, v|
30
+ if v.is_a?(Array)
31
+ params << "#{k}=#{v.join(",")}&"
32
+ else
33
+ params << "#{k}=#{v.to_s}&"
34
+ end
35
+ end
36
+
37
+ params.chop!
38
+ params
39
+ end
40
+
41
+ end
@@ -1,7 +1,7 @@
1
1
  module Restfully
2
2
  class Link
3
3
 
4
- VALID_RELATIONSHIPS = %w{member parent collection self}
4
+ VALID_RELATIONSHIPS = %w{member parent collection self alternate}
5
5
  RELATIONSHIPS_REQUIRING_TITLE = %w{collection member}
6
6
 
7
7
  attr_reader :rel, :title, :href, :errors
@@ -5,13 +5,13 @@ module Restfully
5
5
  # Suppose that the load method has been called on the resource before trying to access its attributes or associations
6
6
 
7
7
  class Resource < DelegateClass(Hash)
8
+
8
9
  undef :type
9
- attr_reader :uri, :session, :state, :raw, :uid, :associations
10
+ attr_reader :uri, :session, :state, :raw, :uid, :associations, :type
10
11
 
11
12
  def initialize(uri, session, options = {})
12
13
  @uri = uri
13
14
  @session = session
14
- @parent = options['parent']
15
15
  @raw = options['raw']
16
16
  @state = :unloaded
17
17
  @attributes = {}
@@ -23,7 +23,7 @@ module Restfully
23
23
 
24
24
  def method_missing(method, *args)
25
25
  if association = @associations[method.to_s]
26
- session.logger.debug "loading association #{method} with #{args.inspect}"
26
+ session.logger.debug "Loading association #{method}, args=#{args.inspect}"
27
27
  association.load(*args)
28
28
  else
29
29
  super(method, *args)
@@ -51,7 +51,8 @@ module Restfully
51
51
  raw.each do |key, value|
52
52
  case key
53
53
  when 'links' then next
54
- when 'uid' then @uid = value
54
+ when 'uid' then @uid = value
55
+ when 'type' then @type = value
55
56
  else
56
57
  case value
57
58
  when Hash
@@ -72,10 +73,32 @@ module Restfully
72
73
  @associations.has_key?(method.to_s) || super(method, *args)
73
74
  end
74
75
 
76
+ def inspect(options = {:space => " "})
77
+ output = "#<#{self.class}:0x#{self.object_id.to_s(16)}"
78
+ if loaded?
79
+ output += "\n#{options[:space]}------------ META ------------"
80
+ output += "\n#{options[:space]}@uri: #{uri.inspect}"
81
+ output += "\n#{options[:space]}@uid: #{uid.inspect}"
82
+ output += "\n#{options[:space]}@type: #{type.inspect}"
83
+ @associations.each do |title, assoc|
84
+ output += "\n#{options[:space]}@#{title}: #{assoc.class.name}"
85
+ end
86
+ unless @attributes.empty?
87
+ output += "\n#{options[:space]}------------ PROPERTIES ------------"
88
+ @attributes.each do |key, value|
89
+ output += "\n#{options[:space]}#{key.inspect} => #{value.inspect}"
90
+ end
91
+ end
92
+ end
93
+ output += ">"
94
+ end
95
+
75
96
  protected
76
97
  def define_link(link)
77
98
  if link.valid?
78
99
  case link.rel
100
+ when 'parent'
101
+ @associations['parent'] = Resource.new(link.href, session)
79
102
  when 'collection'
80
103
  raw_included = link.resolved? ? raw[link.title] : nil
81
104
  @associations[link.title] = Collection.new(link.href, session,
@@ -86,7 +109,7 @@ module Restfully
86
109
  @associations[link.title] = Resource.new(link.href, session,
87
110
  'raw' => raw_included)
88
111
  when 'self'
89
- # uri is supposed to be equal to link['href'] for self relations. so we do nothing
112
+ @uri = link.href
90
113
  end
91
114
  else
92
115
  session.logger.warn link.errors.join("\n")
@@ -1,6 +1,7 @@
1
1
  require 'uri'
2
2
  require 'logger'
3
3
  require 'restclient'
4
+ require 'restfully/parsing'
4
5
 
5
6
  module Restfully
6
7
  class Error < StandardError; end
@@ -16,10 +17,11 @@ module Restfully
16
17
 
17
18
  # TODO: use CacheableResource
18
19
  def initialize(base_url, options = {})
20
+ options = options.stringify_keys
19
21
  @base_url = base_url
20
22
  @root = options['root'] || '/'
21
23
  @logger = options['logger'] || NullLogger.new
22
- @user = options['user']
24
+ @username = options['username']
23
25
  @password = options['password']
24
26
  # TODO: generalize
25
27
  # @connection = Patron::Session.new
@@ -29,7 +31,7 @@ module Restfully
29
31
  # @connection.insecure = true
30
32
  # @connection.username = @user
31
33
  # @connection.password = @password
32
- @connection = RestClient::Resource.new(base_url || '', :user => @user, :password => @password)
34
+ @connection = RestClient::Resource.new(base_url || '', :user => @username, :password => @password)
33
35
  yield Resource.new(root, self).load, self if block_given?
34
36
  end
35
37
 
data/restfully.gemspec ADDED
@@ -0,0 +1,75 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{restfully}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Cyril Rohr"]
12
+ s.date = %q{2009-09-15}
13
+ s.default_executable = %q{restfully}
14
+ s.description = %q{Experimental code for auto-generation of wrappers on top of RESTful APIs that follow HATEOAS principles and provide OPTIONS methods and/or Allow headers.}
15
+ s.email = %q{cyril.rohr@gmail.com}
16
+ s.executables = ["restfully"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/restfully",
29
+ "examples/grid5000.rb",
30
+ "lib/restfully.rb",
31
+ "lib/restfully/collection.rb",
32
+ "lib/restfully/extensions.rb",
33
+ "lib/restfully/link.rb",
34
+ "lib/restfully/parsing.rb",
35
+ "lib/restfully/resource.rb",
36
+ "lib/restfully/session.rb",
37
+ "lib/restfully/special_array.rb",
38
+ "lib/restfully/special_hash.rb",
39
+ "restfully.gemspec",
40
+ "spec/collection_spec.rb",
41
+ "spec/link_spec.rb",
42
+ "spec/resource_spec.rb",
43
+ "spec/restfully_spec.rb",
44
+ "spec/session_spec.rb",
45
+ "spec/spec_helper.rb"
46
+ ]
47
+ s.has_rdoc = true
48
+ s.homepage = %q{http://github.com/crohr/restfully}
49
+ s.rdoc_options = ["--charset=UTF-8"]
50
+ s.require_paths = ["lib"]
51
+ s.rubygems_version = %q{1.3.2}
52
+ s.summary = %q{Experimental code for auto-generation of wrappers on top of RESTful APIs that follow some specific conventions.}
53
+ s.test_files = [
54
+ "spec/collection_spec.rb",
55
+ "spec/link_spec.rb",
56
+ "spec/resource_spec.rb",
57
+ "spec/restfully_spec.rb",
58
+ "spec/session_spec.rb",
59
+ "spec/spec_helper.rb",
60
+ "examples/grid5000.rb"
61
+ ]
62
+
63
+ if s.respond_to? :specification_version then
64
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
65
+ s.specification_version = 3
66
+
67
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
68
+ s.add_runtime_dependency(%q<rest-client>, [">= 0"])
69
+ else
70
+ s.add_dependency(%q<rest-client>, [">= 0"])
71
+ end
72
+ else
73
+ s.add_dependency(%q<rest-client>, [">= 0"])
74
+ end
75
+ end
@@ -143,7 +143,7 @@ describe Resource do
143
143
  resource.should respond_to(:status)
144
144
  status.should_receive(:load).and_return(status)
145
145
  resource.status.should == status
146
- resource.uri.should == 'uri' # self link should not override it
146
+ resource.uri.should == "/sites/rennes/versions/123" # self link should override the URI attribute
147
147
  end
148
148
  end
149
149
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crohr-restfully
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Rohr
@@ -9,8 +9,8 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-31 00:00:00 -07:00
13
- default_executable:
12
+ date: 2009-09-15 00:00:00 -07:00
13
+ default_executable: restfully
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rest-client
@@ -24,8 +24,8 @@ dependencies:
24
24
  version:
25
25
  description: Experimental code for auto-generation of wrappers on top of RESTful APIs that follow HATEOAS principles and provide OPTIONS methods and/or Allow headers.
26
26
  email: cyril.rohr@gmail.com
27
- executables: []
28
-
27
+ executables:
28
+ - restfully
29
29
  extensions: []
30
30
 
31
31
  extra_rdoc_files:
@@ -38,15 +38,18 @@ files:
38
38
  - README.rdoc
39
39
  - Rakefile
40
40
  - VERSION
41
+ - bin/restfully
41
42
  - examples/grid5000.rb
42
43
  - lib/restfully.rb
43
44
  - lib/restfully/collection.rb
45
+ - lib/restfully/extensions.rb
44
46
  - lib/restfully/link.rb
45
47
  - lib/restfully/parsing.rb
46
48
  - lib/restfully/resource.rb
47
49
  - lib/restfully/session.rb
48
50
  - lib/restfully/special_array.rb
49
51
  - lib/restfully/special_hash.rb
52
+ - restfully.gemspec
50
53
  - spec/collection_spec.rb
51
54
  - spec/link_spec.rb
52
55
  - spec/resource_spec.rb
@@ -54,7 +57,8 @@ files:
54
57
  - spec/session_spec.rb
55
58
  - spec/spec_helper.rb
56
59
  has_rdoc: true
57
- homepage: http://github.com/cryx/restfully
60
+ homepage: http://github.com/crohr/restfully
61
+ licenses:
58
62
  post_install_message:
59
63
  rdoc_options:
60
64
  - --charset=UTF-8
@@ -75,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
79
  requirements: []
76
80
 
77
81
  rubyforge_project:
78
- rubygems_version: 1.2.0
82
+ rubygems_version: 1.3.5
79
83
  signing_key:
80
84
  specification_version: 3
81
85
  summary: Experimental code for auto-generation of wrappers on top of RESTful APIs that follow some specific conventions.