restfully 0.3.2 → 0.4.0

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/CHANGELOG ADDED
@@ -0,0 +1,6 @@
1
+ 0.4.0
2
+ * removed 'root_path' option: 'base_uri' must point to the starting resource;
3
+ * objects can now be pretty printed using 'pp object';
4
+ * Restfully::Collection now include the Enumerable module;
5
+ * Restfully::Collection inherits from Restfully::Resource;
6
+ * 'object.uid' is no longer valid. Use 'object["uid"]' instead;
data/README.rdoc CHANGED
@@ -10,105 +10,95 @@ Alpha work.
10
10
  == Usage
11
11
  === Command line
12
12
  $ export RUBYOPT="-rubygems"
13
- $ restfully base_uri [root_path] [-u username] [-p password]
13
+ $ restfully base_uri [-u username] [-p password]
14
14
 
15
15
  e.g., for the Grid5000 API:
16
- $ restfully https://api.grid5000.fr/sid /grid5000 -u username -p password
17
-
18
- If the connection was successful, you should get a prompt. Call the +root+ function to see what are the available resources. <tt>@property</tt> means that you can call the +property+ method on the object. Other properties are available via the <tt>[]</tt> function.
19
- e.g.:
20
- irb(main):005:0> root
21
- => #<Restfully::Resource:0x8848de
22
- ------------ META ------------
23
- @uri: #<URI::Generic:0x11091a8 URL:/grid5000>
24
- @uid: "grid5000"
25
- @type: "grid"
26
- @environments: Restfully::Collection
27
- @sites: Restfully::Collection
28
- @version: Restfully::Resource
29
- @versions: Restfully::Collection
30
- ------------ PROPERTIES ------------
31
- "version" => "4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8">
32
-
33
- irb(main):006:0> root.uri
34
- => #<URI::Generic:0x11091a8 URL:/grid5000>
35
-
36
- irb(main):007:0> root.sites
37
- => #<Restfully::Collection:0x881f6c
38
- ------------ META ------------
39
- @uri: "/grid5000/sites"
40
- @offset: 0
41
- @total: 9
42
- @version: Restfully::Resource
43
- @versions: Restfully::Collection
44
- ------------ PROPERTIES ------------
45
- "version" => "4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8"
46
- ------------ ITEMS ------------
47
- Restfully::Resource
48
- Restfully::Resource
49
- Restfully::Resource
50
- Restfully::Resource
51
- Restfully::Resource
52
- Restfully::Resource
53
- Restfully::Resource
54
- Restfully::Resource
55
- Restfully::Resource>
56
-
57
- irb(main):008:0> root.sites.by_uid('rennes').clusters.by_uid('paradent').nodes.by_uid('paradent-1').metrics.by_uid('mem_free', 'cpu_idle', 'bytes_in')
58
- => [#<Restfully::Resource:0x4259a
59
- ------------ META ------------
60
- @uri: "/grid5000/sites/rennes/clusters/paradent/nodes/paradent-1/metrics/mem_free"
61
- @uid: "mem_free"
62
- @type: "metric"
63
- @parent: Restfully::Resource
64
- @timeseries: Restfully::Resource
65
- ------------ PROPERTIES ------------
66
- "last_update" => -901639018
67
- "step" => 15
68
- "timeseries" => [{"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>1}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>24}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>168}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>672}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>374, "pdp_per_row"=>5760}]>, #<Restfully::Resource:0x12444
69
- ------------ META ------------
70
- @uri: "/grid5000/sites/rennes/clusters/paradent/nodes/paradent-1/metrics/cpu_idle"
71
- @uid: "cpu_idle"
72
- @type: "metric"
73
- @parent: Restfully::Resource
74
- @timeseries: Restfully::Resource
75
- ------------ PROPERTIES ------------
76
- "last_update" => -901639018
77
- "step" => 15
78
- "timeseries" => [{"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>1}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>24}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>168}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>672}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>374, "pdp_per_row"=>5760}]>, #<Restfully::Resource:0xe4c870
79
- ------------ META ------------
80
- @uri: "/grid5000/sites/rennes/clusters/paradent/nodes/paradent-1/metrics/bytes_in"
81
- @uid: "bytes_in"
82
- @type: "metric"
83
- @parent: Restfully::Resource
84
- @timeseries: Restfully::Resource
85
- ------------ PROPERTIES ------------
86
- "last_update" => -901639018
87
- "step" => 15
88
- "timeseries" => [{"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>1}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>24}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>168}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>244, "pdp_per_row"=>672}, {"xff"=>0.5, "cf"=>"AVERAGE", "rows"=>374, "pdp_per_row"=>5760}]>]
89
-
90
- irb(main):009:0> root.version
91
- => #<Restfully::Resource:0x8facf0
92
- ------------ META ------------
93
- @uri: "/grid5000/versions/ee1f8111c8835496a9e4a6f7c9491c0238ee9827"
94
- @uid: "ee1f8111c8835496a9e4a6f7c9491c0238ee9827"
95
- @type: "version"
96
- @parent: Restfully::Resource
97
- ------------ PROPERTIES ------------
98
- "author" => "Cyril Constantin <>"
99
- "date" => "Fri, 11 Sep 2009 14:32:48 GMT"
100
- "message" => "[environments] update of environments on sites">
101
-
102
- You may also prefer to use a configuration file to avoid entering the command line options:
16
+ $ restfully https://api.grid5000.fr/sid/grid5000 -u username -p password
17
+
18
+ If the connection was successful, you should get a prompt. You may enter
19
+ irb(main):001:0> pp root
20
+
21
+ to get back a pretty-printed output of the root resource:
22
+ #<Restfully::Resource:0x91f08c
23
+ @uri=#<URI::HTTP:0x123e30c URL:http://api.local/sid/grid5000>
24
+ LINKS
25
+ @environments=#<Restfully::Collection:0x917666>,
26
+ @sites=#<Restfully::Collection:0x9170d0>,
27
+ @version=#<Restfully::Resource:0x91852a>,
28
+ @versions=#<Restfully::Collection:0x917e68>
29
+ PROPERTIES
30
+ "uid"=>"grid5000",
31
+ "type"=>"grid",
32
+ "version"=>"4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8">
33
+
34
+ You can see the LINKS and PROPERTIES headers that respectively indicate what links you can follow from there (by calling +root.link_name+) and what properties are available (by calling +root[property_name]+).
35
+
36
+ Let's say you want to access the collection of +sites+, you would enter:
37
+ irb(main):002:0> pp root.sites
38
+
39
+ and get back:
40
+ #<Restfully::Collection:0x9170d0
41
+ @uri=#<URI::HTTP:0x122e128 URL:http://api.local/sid/grid5000/sites>
42
+ LINKS
43
+ @version=#<Restfully::Resource:0x8f553e>,
44
+ @versions=#<Restfully::Collection:0x8f52be>
45
+ PROPERTIES
46
+ "total"=>9,
47
+ "version"=>"4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8",
48
+ "offset"=>0
49
+ ITEMS (0..9)/9
50
+ #<Restfully::Resource:0x9058bc uid="bordeaux">,
51
+ #<Restfully::Resource:0x903d0a uid="grenoble">,
52
+ #<Restfully::Resource:0x901cc6 uid="lille">,
53
+ #<Restfully::Resource:0x8fff0c uid="lyon">,
54
+ #<Restfully::Resource:0x8fe288 uid="nancy">,
55
+ #<Restfully::Resource:0x8fc4a6 uid="orsay">,
56
+ #<Restfully::Resource:0x8fa782 uid="rennes">,
57
+ #<Restfully::Resource:0x8f8bb2 uid="sophia">,
58
+ #<Restfully::Resource:0x8f6c9a uid="toulouse">>
59
+
60
+ A Restfully::Collection is a special kind of Resource, which includes the Enumerable module, which means you can call all of its methods on the Restfully::Collection object. For example:
61
+ irb(main):003:0> pp root.sites.find{|s| s['uid'] == 'rennes'}
62
+ #<Restfully::Resource:0x8fa782
63
+ @uri=#<URI::HTTP:0x11f4e64 URL:http://api.local/sid/grid5000/sites/rennes>
64
+ LINKS
65
+ @environments=#<Restfully::Collection:0x8f9ab2>,
66
+ @parent=#<Restfully::Resource:0x8f981e>,
67
+ @deployments=#<Restfully::Collection:0x8f935a>,
68
+ @clusters=#<Restfully::Collection:0x8f9d46>,
69
+ @version=#<Restfully::Resource:0x8fa354>,
70
+ @versions=#<Restfully::Collection:0x8fa0b6>,
71
+ @status=#<Restfully::Collection:0x8f95ee>
72
+ PROPERTIES
73
+ "name"=>"Rennes",
74
+ "latitude"=>48.1,
75
+ "location"=>"Rennes, France",
76
+ "security_contact"=>"rennes-staff@lists.grid5000.fr",
77
+ "uid"=>"rennes",
78
+ "type"=>"site",
79
+ "user_support_contact"=>"rennes-staff@lists.grid5000.fr",
80
+ "version"=>"4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8",
81
+ "description"=>"",
82
+ "longitude"=>-1.6667,
83
+ "compilation_server"=>false,
84
+ "email_contact"=>"rennes-staff@lists.grid5000.fr",
85
+ "web"=>"http://www.irisa.fr",
86
+ "sys_admin_contact"=>"rennes-staff@lists.grid5000.fr">
87
+
88
+ or:
89
+ irb(main):006:0> root.sites.map{|s| s['uid']}.grep(/re/)
90
+ => ["grenoble", "rennes"]
91
+
92
+
93
+ To enter the command-line options, you may prefer to use a configuration file to avoid re-entering the options every time you use the client:
103
94
  $ echo '
104
- base_uri: https://api.grid5000.fr/sid
105
- root_path: /grid5000
95
+ base_uri: https://api.grid5000.fr/sid/grid5000
106
96
  username: MYLOGIN
107
97
  password: MYPASSWORD
108
- ' > ~/somewhere/api.grid5000.fr.yml
98
+ ' > ~/.restfully/api.grid5000.fr.yml && chmod 600 ~/.restfully/api.grid5000.fr.yml
109
99
 
110
100
  And then:
111
- $ restfully -c ~/somewhere/api.grid5000.fr.yml
101
+ $ restfully -c ~/.restfully/api.grid5000.fr.yml
112
102
 
113
103
  === As a library
114
104
  See the +examples+ directory for examples.
data/Rakefile CHANGED
@@ -11,6 +11,7 @@ begin
11
11
  gem.homepage = "http://github.com/crohr/restfully"
12
12
  gem.authors = ["Cyril Rohr"]
13
13
  gem.add_dependency "rest-client", '>= 1.0'
14
+ gem.add_dependency "backports"
14
15
  gem.rubyforge_project = 'restfully'
15
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
17
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.2
1
+ 0.4.0
data/bin/restfully CHANGED
@@ -1,19 +1,26 @@
1
1
  #!/usr/bin/env ruby
2
2
  # The command line Restfully client
3
3
 
4
- $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
4
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' unless $LOAD_PATH.include? File.dirname(__FILE__) + '/../lib'
5
5
 
6
6
  require 'restfully'
7
7
  require 'optparse'
8
8
  require 'logger'
9
9
  require 'yaml'
10
+ require 'pp'
10
11
 
11
12
 
12
13
  logger = Logger.new(STDOUT)
13
14
  logger.level = Logger::WARN
14
15
  @options = {:logger => logger}
15
16
  option_parser = OptionParser.new do |opts|
16
- opts.banner = "Usage: restfully base_uri [root_path] [options]"
17
+ opts.banner = <<BANNER
18
+ * Description
19
+ Restfully #{Restfully::VERSION} - Access REST APIs effortlessly
20
+ * Usage
21
+ restfully [base_uri] [root_path] [options]
22
+ * Options
23
+ BANNER
17
24
 
18
25
  opts.on("-u=", "--username=", "Sets the username") do |u|
19
26
  @options[:username] = u
@@ -43,7 +50,6 @@ end
43
50
  option_parser.parse!
44
51
 
45
52
  @options[:base_uri] = ARGV.shift
46
- @options[:root_path] = ARGV.shift || "/"
47
53
 
48
54
  def session
49
55
  @session ||= Restfully::Session.new(@options)
@@ -51,52 +57,10 @@ end
51
57
 
52
58
  def root
53
59
  @root ||= session.root.load
54
- rescue Restfully::HTTP::Error => e
55
- puts "#{e.class.name}: #{e.message}"
56
60
  end
57
61
 
58
62
  require 'irb'
59
63
  require 'irb/completion'
60
64
  ARGV.clear
61
65
  IRB.start
62
- exit!
63
-
64
-
65
- # if (config_filename = @options.delete('configuration_file')) && File.exists?(File.expand_path(config_filename))
66
- # config = YAML.load_file(File.expand_path(config_filename))
67
- # @base_uri = config.delete('base_uri') || @base_uri
68
- # @root_path = config.delete('root_path') || @root_path
69
- # @options.merge!(config)
70
- # end
71
- #
72
- # unless @base_uri
73
- # $stderr.puts option_parser.help
74
- # exit(-1)
75
- # else
76
- # if (log_file=@options.delete('log'))
77
- # @logger = Logger.new(File.expand_path(log_file))
78
- # else
79
- # @logger = Logger.new(STDOUT)
80
- # end
81
- # if @options.delete('verbose')
82
- # @logger.level = Logger::DEBUG
83
- # else
84
- # @logger.level = Logger::WARN
85
- # end
86
- #
87
- # def session
88
- # @session ||= Restfully::Session.new(@base_uri, @options.merge('root_path' => @root_path, 'logger' => @logger))
89
- # end
90
- # def root
91
- # @root ||= Restfully::Resource.new(session.root_path, session).load
92
- # end
93
- #
94
- # root # preloads
95
- #
96
- # require 'irb'
97
- # require 'irb/completion'
98
- #
99
- # ARGV.clear
100
- # IRB.start
101
- # exit!
102
- # end
66
+ exit!
data/examples/grid5000.rb CHANGED
@@ -5,12 +5,12 @@ require 'pp'
5
5
  require File.dirname(__FILE__)+'/../lib/restfully'
6
6
 
7
7
  logger = Logger.new(STDOUT)
8
- logger.level = Logger::INFO
8
+ logger.level = Logger::WARN
9
9
 
10
10
  # Restfully.adapter = Restfully::HTTP::RestClientAdapter
11
11
  # Restfully.adapter = Patron::Session
12
12
  RestClient.log = 'stdout'
13
- Restfully::Session.new(:base_uri => 'http://api.local/sid', :root_path => '/grid5000', :logger => logger) do |grid, session|
13
+ Restfully::Session.new(:base_uri => 'http://api.local/sid/grid5000', :logger => logger) do |grid, session|
14
14
  grid_stats = {'hardware' => {}, 'system' => {}}
15
15
  grid.sites.each do |site|
16
16
  site_stats = site.status.inject({'hardware' => {}, 'system' => {}}) {|accu, node_status|
@@ -20,9 +20,9 @@ Restfully::Session.new(:base_uri => 'http://api.local/sid', :root_path => '/grid
20
20
  } rescue {'hardware' => {}, 'system' => {}}
21
21
  grid_stats['hardware'].merge!(site_stats['hardware']) { |key,oldval,newval| oldval+newval }
22
22
  grid_stats['system'].merge!(site_stats['system']) { |key,oldval,newval| oldval+newval }
23
- p [site.uid, site_stats]
23
+ p [site['uid'], site_stats]
24
24
  end
25
25
  p [:total, grid_stats]
26
26
  puts "Getting status of a few nodes in rennes:"
27
- pp grid.sites.by_uid('rennes').status(:query => {:only => ['paradent-1', 'paradent-10', 'paramount-3']})
28
- end
27
+ pp grid.sites.find{|s| s['uid'] == 'rennes'}.status(:query => {:only => ['paradent-1', 'paradent-10', 'paramount-3']})
28
+ end
data/lib/restfully.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'backports'
1
2
  require 'restfully/error'
2
3
  require 'restfully/parsing'
3
4
  require 'restfully/http'
@@ -12,9 +13,9 @@ require 'restfully/collection'
12
13
 
13
14
 
14
15
  module Restfully
15
- VERSION = "0.3.2"
16
+ VERSION = "0.4.0"
16
17
  class << self
17
18
  attr_accessor :adapter
18
19
  end
19
20
  self.adapter = Restfully::HTTP::Adapters::RestClientAdapter
20
- end
21
+ end
@@ -1,161 +1,86 @@
1
- require 'delegate'
2
1
 
3
2
  module Restfully
4
- # Resource and Collection classes should clearly include a common module to deal with links, attributes, associations and loading
5
- class Collection < DelegateClass(Array)
6
-
7
- attr_reader :state, :raw, :uri, :session, :title, :offset, :total, :items
3
+ # This includes the <tt>Enumerable</tt> module, but does not have all the
4
+ # methods that you could expect from an <tt>Array</tt>.
5
+ # Remember that this class inherits from a <tt>Restfully::Resource</tt> and
6
+ # as such, the <tt>#[]</tt> method gives access to Resource properties, and
7
+ # not to an item in the collection.
8
+ # If you want to operate on the array of items, you MUST call <tt>#to_a</tt>
9
+ # first (or <tt>#items</tt>) on the Restfully::Collection.
10
+ class Collection < Resource
11
+ include Enumerable
12
+ attr_reader :items
8
13
 
14
+ # See Resource#new
9
15
  def initialize(uri, session, options = {})
10
- options = options.symbolize_keys
11
- @uri = uri
12
- @title = options[:title]
13
- @session = session
14
- reset
15
- super(@items)
16
+ super(uri, session, options)
16
17
  end
17
-
18
- def loaded?; @state == :loaded; end
19
-
18
+
19
+ # See Resource#reset
20
20
  def reset
21
- @last_request_hash = nil
22
- @associations = {}
23
- @attributes = {}
24
- @indexes = {}
25
- @items = []
26
- @raw = nil
27
- @state = :unloaded
21
+ super
22
+ @items = Array.new
23
+ @indexes = Hash.new
24
+ self
28
25
  end
29
26
 
30
- def [](key)
31
- if key.is_a? Symbol
32
- by_uid(key.to_s)
33
- else
34
- super(key)
35
- end
27
+ # Iterates over the collection of items
28
+ def each(*args, &block)
29
+ @items.each(*args, &block)
36
30
  end
37
-
38
- def method_missing(method, *args)
39
- load
40
- if association = @associations[method.to_s]
41
- session.logger.debug "Loading association #{method}, args=#{args.inspect}"
42
- association.load(*args)
43
- elsif method.to_s =~ /^by_(.+)$/
44
- key = $1
45
- @indexes[method.to_s] ||= self.inject({}) { |accu, item|
46
- accu[item.has_key?(key) ? item[key] : item.send(key.to_sym)] = item
47
- accu
48
- }
49
- if args.empty?
50
- @indexes[method.to_s]
51
- elsif args.length == 1
52
- @indexes[method.to_s][args.first]
53
- else
54
- @indexes[method.to_s].values_at(*args)
55
- end
56
- else
57
- super(method, *args)
58
- end
59
- end
60
-
61
- def load(options = {})
62
- options = options.symbolize_keys
63
- force_reload = !!options.delete(:reload)
64
- request_hash = [:get, options].hash
65
- if loaded? && !force_reload && request_hash == @last_request_hash
66
- self
67
- else
68
- reset
69
- @last_request_hash = request_hash
70
- @raw = options[:raw]
71
- if raw.nil? || force_reload
72
- response = session.get(uri, options)
73
- @raw = response.body
74
- end
75
- raw.each do |key, value|
76
- case key
77
- when "total", "offset"
78
- instance_variable_set("@#{key}".to_sym, value)
79
- when "links"
80
- value.each{|link| define_link(Link.new(link))}
81
- when "items"
82
- value.each do |item|
83
- self_link = (item['links'] || []).map{|link| Link.new(link)}.detect{|link| link.self?}
84
- if self_link && self_link.valid?
85
- self.push Resource.new(self_link.href, session).load(:raw => item)
86
- else
87
- session.logger.warn "Resource #{key} does not have a 'self' link. skipped."
88
- end
89
- end
31
+
32
+ def populate_object(key, value)
33
+ case key
34
+ when "links"
35
+ value.each{|link| define_link(Link.new(link))}
36
+ when "items"
37
+ value.each do |item|
38
+ self_link = (item['links'] || []).
39
+ map{|link| Link.new(link)}.detect{|link| link.self?}
40
+ if self_link && self_link.valid?
41
+ @items.push Resource.new(uri.merge(self_link.href), session).load(:body => item)
90
42
  else
91
- case value
92
- when Hash
93
- @attributes.store(key, SpecialHash.new.replace(value)) unless @associations.has_key?(key)
94
- when Array
95
- @attributes.store(key, SpecialArray.new(value))
96
- else
97
- @attributes.store(key, value)
98
- end
43
+ session.logger.warn "Resource #{key} does not have a 'self' link. skipped."
99
44
  end
100
45
  end
101
- @state = :loaded
102
- self
46
+ else
47
+ case value
48
+ when Hash
49
+ @properties.store(key, SpecialHash.new.replace(value)) unless @links.has_key?(key)
50
+ when Array
51
+ @properties.store(key, SpecialArray.new(value))
52
+ else
53
+ @properties.store(key, value)
54
+ end
103
55
  end
56
+ end
57
+
58
+ def inspect
59
+ @items.inspect
104
60
  end
105
61
 
106
- def respond_to?(method, *args)
107
- @associations.has_key?(method.to_s) || super(method, *args)
62
+ # Returns the current number of items (not the total number)
63
+ # in the collection.
64
+ def length
65
+ @items.length
108
66
  end
109
67
 
110
- # Removed: use `y resource` to get pretty output
111
- # def inspect(options = {:space => "\t"})
112
- # output = "#<#{self.class}:0x#{self.object_id.to_s(16)}"
113
- # if loaded?
114
- # output += "\n#{options[:space]}------------ META ------------"
115
- # output += "\n#{options[:space]}@uri: #{uri.inspect}"
116
- # output += "\n#{options[:space]}@offset: #{offset.inspect}"
117
- # output += "\n#{options[:space]}@total: #{total.inspect}"
118
- # @associations.each do |title, assoc|
119
- # output += "\n#{options[:space]}@#{title}: #{assoc.class.name}"
120
- # end
121
- # unless @attributes.empty?
122
- # output += "\n#{options[:space]}------------ PROPERTIES ------------"
123
- # @attributes.each do |key, value|
124
- # output += "\n#{options[:space]}#{key.inspect} => #{value.inspect}"
125
- # end
126
- # end
127
- # unless self.empty?
128
- # output += "\n#{options[:space]}------------ ITEMS ------------"
129
- # self.each do |value|
130
- # output += "\n#{options[:space]}#{value.class.name}"
131
- # end
132
- # end
133
- # end
134
- # output += ">"
135
- # end
136
-
137
- protected
138
- def define_link(link)
139
- if link.valid?
140
- case link.rel
141
- when 'parent'
142
- @associations['parent'] = Resource.new(link.href, session)
143
- when 'collection'
144
- raw_included = link.resolved? ? raw[link.title] : nil
145
- @associations[link.title] = Collection.new(link.href, session,
146
- :raw => raw_included,
147
- :title => link.title)
148
- when 'member'
149
- raw_included = link.resolved? ? raw[link.title] : nil
150
- @associations[link.title] = Resource.new(link.href, session,
151
- :title => link.title,
152
- :raw => raw_included)
153
- when 'self'
154
- # we do nothing
68
+ def pretty_print(pp)
69
+ super(pp) do |pp|
70
+ if @items.length > 0
71
+ pp.breakable
72
+ pp.text "ITEMS (#{self["offset"]}..#{self["offset"]+@items.length})/#{self["total"]}"
73
+ pp.nest 2 do
74
+ @items.each_with_index do |item, i|
75
+ pp.breakable
76
+ pp.text "#<#{item.class}:0x#{item.object_id.to_s(16)} uid=#{item['uid'].inspect}>"
77
+ pp.text "," if i < @items.length-1
78
+ end
79
+ end
155
80
  end
156
- else
157
- session.logger.warn link.errors.join("\n")
158
81
  end
159
82
  end
83
+
84
+ alias_method :size, :length
160
85
  end
161
86
  end