abiquo 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -10,7 +10,7 @@ gem install abiquo
10
10
 
11
11
  The first version of the api just uses basic authentication, so we need to create an instance with our credentials:
12
12
 
13
- auth = Abiquo::Auth('Abiquo', 'username', 'password')
13
+ auth = Abiquo::BasicAuth.new('Abiquo', 'username', 'password')
14
14
 
15
15
  The entry point for the Abiquo's API is an atom document service:
16
16
 
@@ -1,15 +1,25 @@
1
1
  require 'resourceful'
2
2
  require 'nokogiri'
3
- require 'active_support'
3
+ require File.expand_path('../active_support/inflections', __FILE__)
4
4
  require File.expand_path('../core_ext', __FILE__)
5
5
  require File.expand_path('../to_xml', __FILE__)
6
6
  require 'uri'
7
7
 
8
8
  module Abiquo
9
+
10
+ class NotAllowed < RuntimeError
11
+ def initialize(method, url)
12
+ @method = method
13
+ @url = url
14
+ end
15
+
16
+ def message
17
+ "Method #{@method} not allowed for #{@url}"
18
+ end
19
+ end
9
20
 
10
21
  class BasicAuth < Resourceful::BasicAuthenticator
11
22
  def can_handle?(request); true; end
12
-
13
23
  end
14
24
 
15
25
  module HttpAccessor
@@ -17,7 +27,15 @@ module Abiquo
17
27
  def xml
18
28
  @xml ||= begin
19
29
  response = http.resource(url_with_params).get
20
- Nokogiri.parse(response.body).root
30
+ doc = Nokogiri.parse(response.body)
31
+ # HACK: collections have link and totalSize entities we
32
+ # don't want here
33
+ if not doc.search('/*/totalSize').empty?
34
+ doc.search('/*/totalSize | /*/link').each do |node|
35
+ node.remove
36
+ end
37
+ end
38
+ doc.root
21
39
  end
22
40
  end
23
41
 
@@ -33,6 +51,13 @@ module Abiquo
33
51
  def http
34
52
  Resourceful::HttpAccessor.new(:authenticator => @auth)
35
53
  end
54
+
55
+ def rest_options
56
+ @rest_options ||= begin
57
+ response = http.resource(@url).options
58
+ response.header['Allow'].map {|m| m.to_sym }
59
+ end
60
+ end
36
61
 
37
62
  end
38
63
 
@@ -50,11 +75,13 @@ module Abiquo
50
75
  end
51
76
 
52
77
  def update(attrs = {})
78
+ raise Abiquo::NotAllowed.new(:PUT, url) unless rest_options.include?(:PUT)
53
79
  response = http.resource(url).put(attrs.to_xml(:root => object_type), :content_type => "application/xml")
54
80
  @xml = Nokogiri.parse(response.body).root
55
81
  end
56
82
 
57
83
  def delete
84
+ raise Abiquo::NotAllowed.new(:DELETE, url) unless rest_options.include?(:DELETE)
58
85
  http.resource(url).delete
59
86
  end
60
87
 
@@ -111,8 +138,10 @@ module Abiquo
111
138
  end
112
139
 
113
140
  def create(attrs = {})
141
+ raise Abiquo::NotAllowed.new(:POST, url) unless rest_options.include?(:POST)
114
142
  response = http.resource(url_with_params).post(attrs.to_xml(:root => object_type, :convert_links => true), :content_type => "application/xml")
115
- Resource.new(nil, @auth, Nokogiri.parse(response.body).root)
143
+ doc = Nokogiri.parse(response.body)
144
+ Resource.new(nil, @auth, doc.root)
116
145
  end
117
146
 
118
147
  private
@@ -135,11 +164,13 @@ module Abiquo
135
164
  include HttpAccessor
136
165
 
137
166
  undef id
167
+ undef type
138
168
 
139
169
  delegate :inspect, :to => :get!
140
170
 
141
171
  def self.from_xml(xml, auth = nil)
142
- new(nil, auth, Nokogiri.parse(xml).root)
172
+ doc = Nokogiri.parse(xml)
173
+ new(nil, auth, doc.root)
143
174
  end
144
175
 
145
176
  def initialize(url, auth, xml = nil, options = {})
@@ -0,0 +1,170 @@
1
+ #
2
+ # Code from activesupport 2.3.8
3
+ #
4
+ require 'active_support/inflector' unless defined?(ActiveSupport::Inflector)
5
+
6
+ module ActiveSupport #:nodoc:
7
+ module CoreExtensions #:nodoc:
8
+ module String #:nodoc:
9
+ # String inflections define new methods on the String class to transform names for different purposes.
10
+ # For instance, you can figure out the name of a database from the name of a class.
11
+ #
12
+ # "ScaleScore".tableize # => "scale_scores"
13
+ module Inflections
14
+ # Returns the plural form of the word in the string.
15
+ #
16
+ # "post".pluralize # => "posts"
17
+ # "octopus".pluralize # => "octopi"
18
+ # "sheep".pluralize # => "sheep"
19
+ # "words".pluralize # => "words"
20
+ # "the blue mailman".pluralize # => "the blue mailmen"
21
+ # "CamelOctopus".pluralize # => "CamelOctopi"
22
+ def pluralize
23
+ Inflector.pluralize(self)
24
+ end
25
+
26
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
27
+ #
28
+ # "posts".singularize # => "post"
29
+ # "octopi".singularize # => "octopus"
30
+ # "sheep".singularize # => "sheep"
31
+ # "word".singularize # => "word"
32
+ # "the blue mailmen".singularize # => "the blue mailman"
33
+ # "CamelOctopi".singularize # => "CamelOctopus"
34
+ def singularize
35
+ Inflector.singularize(self)
36
+ end
37
+
38
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
39
+ # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
40
+ #
41
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
42
+ #
43
+ # "active_record".camelize # => "ActiveRecord"
44
+ # "active_record".camelize(:lower) # => "activeRecord"
45
+ # "active_record/errors".camelize # => "ActiveRecord::Errors"
46
+ # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
47
+ def camelize(first_letter = :upper)
48
+ case first_letter
49
+ when :upper then Inflector.camelize(self, true)
50
+ when :lower then Inflector.camelize(self, false)
51
+ end
52
+ end
53
+ alias_method :camelcase, :camelize
54
+
55
+ # Capitalizes all the words and replaces some characters in the string to create
56
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
57
+ # used in the Rails internals.
58
+ #
59
+ # +titleize+ is also aliased as +titlecase+.
60
+ #
61
+ # "man from the boondocks".titleize # => "Man From The Boondocks"
62
+ # "x-men: the last stand".titleize # => "X Men: The Last Stand"
63
+ def titleize
64
+ Inflector.titleize(self)
65
+ end
66
+ alias_method :titlecase, :titleize
67
+
68
+ # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
69
+ #
70
+ # +underscore+ will also change '::' to '/' to convert namespaces to paths.
71
+ #
72
+ # "ActiveRecord".underscore # => "active_record"
73
+ # "ActiveRecord::Errors".underscore # => active_record/errors
74
+ def underscore
75
+ Inflector.underscore(self)
76
+ end
77
+
78
+ # Replaces underscores with dashes in the string.
79
+ #
80
+ # "puni_puni" # => "puni-puni"
81
+ def dasherize
82
+ Inflector.dasherize(self)
83
+ end
84
+
85
+ # Removes the module part from the constant expression in the string.
86
+ #
87
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
88
+ # "Inflections".demodulize # => "Inflections"
89
+ def demodulize
90
+ Inflector.demodulize(self)
91
+ end
92
+
93
+ # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
94
+ #
95
+ # ==== Examples
96
+ #
97
+ # class Person
98
+ # def to_param
99
+ # "#{id}-#{name.parameterize}"
100
+ # end
101
+ # end
102
+ #
103
+ # @person = Person.find(1)
104
+ # # => #<Person id: 1, name: "Donald E. Knuth">
105
+ #
106
+ # <%= link_to(@person.name, person_path %>
107
+ # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
108
+ def parameterize(sep = '-')
109
+ Inflector.parameterize(self, sep)
110
+ end
111
+
112
+ # Creates the name of a table like Rails does for models to table names. This method
113
+ # uses the +pluralize+ method on the last word in the string.
114
+ #
115
+ # "RawScaledScorer".tableize # => "raw_scaled_scorers"
116
+ # "egg_and_ham".tableize # => "egg_and_hams"
117
+ # "fancyCategory".tableize # => "fancy_categories"
118
+ def tableize
119
+ Inflector.tableize(self)
120
+ end
121
+
122
+ # Create a class name from a plural table name like Rails does for table names to models.
123
+ # Note that this returns a string and not a class. (To convert to an actual class
124
+ # follow +classify+ with +constantize+.)
125
+ #
126
+ # "egg_and_hams".classify # => "EggAndHam"
127
+ # "posts".classify # => "Post"
128
+ #
129
+ # Singular names are not handled correctly.
130
+ #
131
+ # "business".classify # => "Busines"
132
+ def classify
133
+ Inflector.classify(self)
134
+ end
135
+
136
+ # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
137
+ # Like +titleize+, this is meant for creating pretty output.
138
+ #
139
+ # "employee_salary" # => "Employee salary"
140
+ # "author_id" # => "Author"
141
+ def humanize
142
+ Inflector.humanize(self)
143
+ end
144
+
145
+ # Creates a foreign key name from a class name.
146
+ # +separate_class_name_and_id_with_underscore+ sets whether
147
+ # the method should put '_' between the name and 'id'.
148
+ #
149
+ # Examples
150
+ # "Message".foreign_key # => "message_id"
151
+ # "Message".foreign_key(false) # => "messageid"
152
+ # "Admin::Post".foreign_key # => "post_id"
153
+ def foreign_key(separate_class_name_and_id_with_underscore = true)
154
+ Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
155
+ end
156
+
157
+ # +constantize+ tries to find a declared constant with the name specified
158
+ # in the string. It raises a NameError when the name is not in CamelCase
159
+ # or is not initialized.
160
+ #
161
+ # Examples
162
+ # "Module".constantize # => Module
163
+ # "Class".constantize # => Class
164
+ def constantize
165
+ Inflector.constantize(self)
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,411 @@
1
+ #
2
+ # Code from activesupport 2.3.8
3
+ #
4
+ # encoding: utf-8
5
+ require 'singleton'
6
+ require 'iconv'
7
+ require 'kconv'
8
+
9
+ module ActiveSupport
10
+ # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
11
+ # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
12
+ # in inflections.rb.
13
+ #
14
+ # The Rails core team has stated patches for the inflections library will not be accepted
15
+ # in order to avoid breaking legacy applications which may be relying on errant inflections.
16
+ # If you discover an incorrect inflection and require it for your application, you'll need
17
+ # to correct it yourself (explained below).
18
+ module Inflector
19
+ extend self
20
+
21
+ # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
22
+ # inflection rules. Examples:
23
+ #
24
+ # ActiveSupport::Inflector.inflections do |inflect|
25
+ # inflect.plural /^(ox)$/i, '\1\2en'
26
+ # inflect.singular /^(ox)en/i, '\1'
27
+ #
28
+ # inflect.irregular 'octopus', 'octopi'
29
+ #
30
+ # inflect.uncountable "equipment"
31
+ # end
32
+ #
33
+ # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
34
+ # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
35
+ # already have been loaded.
36
+ class Inflections
37
+ include Singleton
38
+
39
+ attr_reader :plurals, :singulars, :uncountables, :humans
40
+
41
+ def initialize
42
+ @plurals, @singulars, @uncountables, @humans = [], [], [], []
43
+ end
44
+
45
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
46
+ # The replacement should always be a string that may include references to the matched data from the rule.
47
+ def plural(rule, replacement)
48
+ @uncountables.delete(rule) if rule.is_a?(String)
49
+ @uncountables.delete(replacement)
50
+ @plurals.insert(0, [rule, replacement])
51
+ end
52
+
53
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
54
+ # The replacement should always be a string that may include references to the matched data from the rule.
55
+ def singular(rule, replacement)
56
+ @uncountables.delete(rule) if rule.is_a?(String)
57
+ @uncountables.delete(replacement)
58
+ @singulars.insert(0, [rule, replacement])
59
+ end
60
+
61
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
62
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
63
+ #
64
+ # Examples:
65
+ # irregular 'octopus', 'octopi'
66
+ # irregular 'person', 'people'
67
+ def irregular(singular, plural)
68
+ @uncountables.delete(singular)
69
+ @uncountables.delete(plural)
70
+ if singular[0,1].upcase == plural[0,1].upcase
71
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
72
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
73
+ else
74
+ plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
75
+ plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
76
+ singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
77
+ singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
78
+ end
79
+ end
80
+
81
+ # Add uncountable words that shouldn't be attempted inflected.
82
+ #
83
+ # Examples:
84
+ # uncountable "money"
85
+ # uncountable "money", "information"
86
+ # uncountable %w( money information rice )
87
+ def uncountable(*words)
88
+ (@uncountables << words).flatten!
89
+ end
90
+
91
+ # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
92
+ # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
93
+ # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
94
+ #
95
+ # Examples:
96
+ # human /_cnt$/i, '\1_count'
97
+ # human "legacy_col_person_name", "Name"
98
+ def human(rule, replacement)
99
+ @humans.insert(0, [rule, replacement])
100
+ end
101
+
102
+ # Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
103
+ # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
104
+ # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
105
+ #
106
+ # Examples:
107
+ # clear :all
108
+ # clear :plurals
109
+ def clear(scope = :all)
110
+ case scope
111
+ when :all
112
+ @plurals, @singulars, @uncountables = [], [], []
113
+ else
114
+ instance_variable_set "@#{scope}", []
115
+ end
116
+ end
117
+ end
118
+
119
+ # Yields a singleton instance of Inflector::Inflections so you can specify additional
120
+ # inflector rules.
121
+ #
122
+ # Example:
123
+ # ActiveSupport::Inflector.inflections do |inflect|
124
+ # inflect.uncountable "rails"
125
+ # end
126
+ def inflections
127
+ if block_given?
128
+ yield Inflections.instance
129
+ else
130
+ Inflections.instance
131
+ end
132
+ end
133
+
134
+ # Returns the plural form of the word in the string.
135
+ #
136
+ # Examples:
137
+ # "post".pluralize # => "posts"
138
+ # "octopus".pluralize # => "octopi"
139
+ # "sheep".pluralize # => "sheep"
140
+ # "words".pluralize # => "words"
141
+ # "CamelOctopus".pluralize # => "CamelOctopi"
142
+ def pluralize(word)
143
+ result = word.to_s.dup
144
+
145
+ if word.empty? || inflections.uncountables.include?(result.downcase)
146
+ result
147
+ else
148
+ inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
149
+ result
150
+ end
151
+ end
152
+
153
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
154
+ #
155
+ # Examples:
156
+ # "posts".singularize # => "post"
157
+ # "octopi".singularize # => "octopus"
158
+ # "sheep".singluarize # => "sheep"
159
+ # "word".singularize # => "word"
160
+ # "CamelOctopi".singularize # => "CamelOctopus"
161
+ def singularize(word)
162
+ result = word.to_s.dup
163
+
164
+ if inflections.uncountables.any? { |inflection| result =~ /#{inflection}\Z/i }
165
+ result
166
+ else
167
+ inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
168
+ result
169
+ end
170
+ end
171
+
172
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
173
+ # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
174
+ #
175
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
176
+ #
177
+ # Examples:
178
+ # "active_record".camelize # => "ActiveRecord"
179
+ # "active_record".camelize(:lower) # => "activeRecord"
180
+ # "active_record/errors".camelize # => "ActiveRecord::Errors"
181
+ # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
182
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
183
+ if first_letter_in_uppercase
184
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
185
+ else
186
+ lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
187
+ end
188
+ end
189
+
190
+ # Capitalizes all the words and replaces some characters in the string to create
191
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
192
+ # used in the Rails internals.
193
+ #
194
+ # +titleize+ is also aliased as as +titlecase+.
195
+ #
196
+ # Examples:
197
+ # "man from the boondocks".titleize # => "Man From The Boondocks"
198
+ # "x-men: the last stand".titleize # => "X Men: The Last Stand"
199
+ def titleize(word)
200
+ humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
201
+ end
202
+
203
+ # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
204
+ #
205
+ # Changes '::' to '/' to convert namespaces to paths.
206
+ #
207
+ # Examples:
208
+ # "ActiveRecord".underscore # => "active_record"
209
+ # "ActiveRecord::Errors".underscore # => active_record/errors
210
+ def underscore(camel_cased_word)
211
+ camel_cased_word.to_s.gsub(/::/, '/').
212
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
213
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
214
+ tr("-", "_").
215
+ downcase
216
+ end
217
+
218
+ # Replaces underscores with dashes in the string.
219
+ #
220
+ # Example:
221
+ # "puni_puni" # => "puni-puni"
222
+ def dasherize(underscored_word)
223
+ underscored_word.gsub(/_/, '-')
224
+ end
225
+
226
+ # Capitalizes the first word and turns underscores into spaces and strips a
227
+ # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
228
+ #
229
+ # Examples:
230
+ # "employee_salary" # => "Employee salary"
231
+ # "author_id" # => "Author"
232
+ def humanize(lower_case_and_underscored_word)
233
+ result = lower_case_and_underscored_word.to_s.dup
234
+
235
+ inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
236
+ result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
237
+ end
238
+
239
+ # Removes the module part from the expression in the string.
240
+ #
241
+ # Examples:
242
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
243
+ # "Inflections".demodulize # => "Inflections"
244
+ def demodulize(class_name_in_module)
245
+ class_name_in_module.to_s.gsub(/^.*::/, '')
246
+ end
247
+
248
+ # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
249
+ #
250
+ # ==== Examples
251
+ #
252
+ # class Person
253
+ # def to_param
254
+ # "#{id}-#{name.parameterize}"
255
+ # end
256
+ # end
257
+ #
258
+ # @person = Person.find(1)
259
+ # # => #<Person id: 1, name: "Donald E. Knuth">
260
+ #
261
+ # <%= link_to(@person.name, person_path(@person)) %>
262
+ # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
263
+ def parameterize(string, sep = '-')
264
+ # remove malformed utf8 characters
265
+ string = string.toutf8 unless string.is_utf8?
266
+ # replace accented chars with ther ascii equivalents
267
+ parameterized_string = transliterate(string)
268
+ # Turn unwanted chars into the seperator
269
+ parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep)
270
+ unless sep.blank?
271
+ re_sep = Regexp.escape(sep)
272
+ # No more than one of the separator in a row.
273
+ parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
274
+ # Remove leading/trailing separator.
275
+ parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
276
+ end
277
+ parameterized_string.downcase
278
+ end
279
+
280
+
281
+ # Replaces accented characters with their ascii equivalents.
282
+ def transliterate(string)
283
+ Iconv.iconv('ascii//ignore//translit', 'utf-8', string).to_s
284
+ end
285
+
286
+ if RUBY_VERSION >= '1.9'
287
+ undef_method :transliterate
288
+ def transliterate(string)
289
+ warn "Ruby 1.9 doesn't support Unicode normalization yet"
290
+ string.dup
291
+ end
292
+
293
+ # The iconv transliteration code doesn't function correctly
294
+ # on some platforms, but it's very fast where it does function.
295
+ elsif "foo" != (Inflector.transliterate("föö") rescue nil)
296
+ undef_method :transliterate
297
+ def transliterate(string)
298
+ string.mb_chars.normalize(:kd). # Decompose accented characters
299
+ gsub(/[^\x00-\x7F]+/, '') # Remove anything non-ASCII entirely (e.g. diacritics).
300
+ end
301
+ end
302
+
303
+ # Create the name of a table like Rails does for models to table names. This method
304
+ # uses the +pluralize+ method on the last word in the string.
305
+ #
306
+ # Examples
307
+ # "RawScaledScorer".tableize # => "raw_scaled_scorers"
308
+ # "egg_and_ham".tableize # => "egg_and_hams"
309
+ # "fancyCategory".tableize # => "fancy_categories"
310
+ def tableize(class_name)
311
+ pluralize(underscore(class_name))
312
+ end
313
+
314
+ # Create a class name from a plural table name like Rails does for table names to models.
315
+ # Note that this returns a string and not a Class. (To convert to an actual class
316
+ # follow +classify+ with +constantize+.)
317
+ #
318
+ # Examples:
319
+ # "egg_and_hams".classify # => "EggAndHam"
320
+ # "posts".classify # => "Post"
321
+ #
322
+ # Singular names are not handled correctly:
323
+ # "business".classify # => "Busines"
324
+ def classify(table_name)
325
+ # strip out any leading schema name
326
+ camelize(singularize(table_name.to_s.sub(/.*\./, '')))
327
+ end
328
+
329
+ # Creates a foreign key name from a class name.
330
+ # +separate_class_name_and_id_with_underscore+ sets whether
331
+ # the method should put '_' between the name and 'id'.
332
+ #
333
+ # Examples:
334
+ # "Message".foreign_key # => "message_id"
335
+ # "Message".foreign_key(false) # => "messageid"
336
+ # "Admin::Post".foreign_key # => "post_id"
337
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
338
+ underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
339
+ end
340
+
341
+ # Ruby 1.9 introduces an inherit argument for Module#const_get and
342
+ # #const_defined? and changes their default behavior.
343
+ if Module.method(:const_get).arity == 1
344
+ # Tries to find a constant with the name specified in the argument string:
345
+ #
346
+ # "Module".constantize # => Module
347
+ # "Test::Unit".constantize # => Test::Unit
348
+ #
349
+ # The name is assumed to be the one of a top-level constant, no matter whether
350
+ # it starts with "::" or not. No lexical context is taken into account:
351
+ #
352
+ # C = 'outside'
353
+ # module M
354
+ # C = 'inside'
355
+ # C # => 'inside'
356
+ # "C".constantize # => 'outside', same as ::C
357
+ # end
358
+ #
359
+ # NameError is raised when the name is not in CamelCase or the constant is
360
+ # unknown.
361
+ def constantize(camel_cased_word)
362
+ names = camel_cased_word.split('::')
363
+ names.shift if names.empty? || names.first.empty?
364
+
365
+ constant = Object
366
+ names.each do |name|
367
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
368
+ end
369
+ constant
370
+ end
371
+ else
372
+ def constantize(camel_cased_word) #:nodoc:
373
+ names = camel_cased_word.split('::')
374
+ names.shift if names.empty? || names.first.empty?
375
+
376
+ constant = Object
377
+ names.each do |name|
378
+ constant = constant.const_get(name, false) || constant.const_missing(name)
379
+ end
380
+ constant
381
+ end
382
+ end
383
+
384
+ # Turns a number into an ordinal string used to denote the position in an
385
+ # ordered sequence such as 1st, 2nd, 3rd, 4th.
386
+ #
387
+ # Examples:
388
+ # ordinalize(1) # => "1st"
389
+ # ordinalize(2) # => "2nd"
390
+ # ordinalize(1002) # => "1002nd"
391
+ # ordinalize(1003) # => "1003rd"
392
+ def ordinalize(number)
393
+ if (11..13).include?(number.to_i % 100)
394
+ "#{number}th"
395
+ else
396
+ case number.to_i % 10
397
+ when 1; "#{number}st"
398
+ when 2; "#{number}nd"
399
+ when 3; "#{number}rd"
400
+ else "#{number}th"
401
+ end
402
+ end
403
+ end
404
+ end
405
+ end
406
+
407
+ # in case active_support/inflector is required without the rest of active_support
408
+ require 'active_support/inflections'
409
+ unless String.included_modules.include?(ActiveSupport::CoreExtensions::String::Inflections)
410
+ String.send :include, ActiveSupport::CoreExtensions::String::Inflections
411
+ end
@@ -1,10 +1,381 @@
1
+ require 'net/http'
2
+
3
+ #
4
+ # Stolen from activesupport 2.3.8
5
+ #
6
+ class Module
7
+ # Provides a delegate class method to easily expose contained objects' methods
8
+ # as your own. Pass one or more methods (specified as symbols or strings)
9
+ # and the name of the target object as the final <tt>:to</tt> option (also a symbol
10
+ # or string). At least one method and the <tt>:to</tt> option are required.
11
+ #
12
+ # Delegation is particularly useful with Active Record associations:
13
+ #
14
+ # class Greeter < ActiveRecord::Base
15
+ # def hello() "hello" end
16
+ # def goodbye() "goodbye" end
17
+ # end
18
+ #
19
+ # class Foo < ActiveRecord::Base
20
+ # belongs_to :greeter
21
+ # delegate :hello, :to => :greeter
22
+ # end
23
+ #
24
+ # Foo.new.hello # => "hello"
25
+ # Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
26
+ #
27
+ # Multiple delegates to the same target are allowed:
28
+ #
29
+ # class Foo < ActiveRecord::Base
30
+ # belongs_to :greeter
31
+ # delegate :hello, :goodbye, :to => :greeter
32
+ # end
33
+ #
34
+ # Foo.new.goodbye # => "goodbye"
35
+ #
36
+ # Methods can be delegated to instance variables, class variables, or constants
37
+ # by providing them as a symbols:
38
+ #
39
+ # class Foo
40
+ # CONSTANT_ARRAY = [0,1,2,3]
41
+ # @@class_array = [4,5,6,7]
42
+ #
43
+ # def initialize
44
+ # @instance_array = [8,9,10,11]
45
+ # end
46
+ # delegate :sum, :to => :CONSTANT_ARRAY
47
+ # delegate :min, :to => :@@class_array
48
+ # delegate :max, :to => :@instance_array
49
+ # end
50
+ #
51
+ # Foo.new.sum # => 6
52
+ # Foo.new.min # => 4
53
+ # Foo.new.max # => 11
54
+ #
55
+ # Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
56
+ # is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
57
+ # delegated to.
58
+ #
59
+ # Person = Struct.new(:name, :address)
60
+ #
61
+ # class Invoice < Struct.new(:client)
62
+ # delegate :name, :address, :to => :client, :prefix => true
63
+ # end
64
+ #
65
+ # john_doe = Person.new("John Doe", "Vimmersvej 13")
66
+ # invoice = Invoice.new(john_doe)
67
+ # invoice.client_name # => "John Doe"
68
+ # invoice.client_address # => "Vimmersvej 13"
69
+ #
70
+ # It is also possible to supply a custom prefix.
71
+ #
72
+ # class Invoice < Struct.new(:client)
73
+ # delegate :name, :address, :to => :client, :prefix => :customer
74
+ # end
75
+ #
76
+ # invoice = Invoice.new(john_doe)
77
+ # invoice.customer_name # => "John Doe"
78
+ # invoice.customer_address # => "Vimmersvej 13"
79
+ #
80
+ # If the object to which you delegate can be nil, you may want to use the
81
+ # :allow_nil option. In that case, it returns nil instead of raising a
82
+ # NoMethodError exception:
83
+ #
84
+ # class Foo
85
+ # attr_accessor :bar
86
+ # def initialize(bar = nil)
87
+ # @bar = bar
88
+ # end
89
+ # delegate :zoo, :to => :bar
90
+ # end
91
+ #
92
+ # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
93
+ #
94
+ # class Foo
95
+ # attr_accessor :bar
96
+ # def initialize(bar = nil)
97
+ # @bar = bar
98
+ # end
99
+ # delegate :zoo, :to => :bar, :allow_nil => true
100
+ # end
101
+ #
102
+ # Foo.new.zoo # returns nil
103
+ #
104
+ def delegate(*methods)
105
+ options = methods.pop
106
+ unless options.is_a?(Hash) && to = options[:to]
107
+ raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
108
+ end
109
+
110
+ if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
111
+ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
112
+ end
113
+
114
+ prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
115
+
116
+ file, line = caller.first.split(':', 2)
117
+ line = line.to_i
118
+
119
+ methods.each do |method|
120
+ on_nil =
121
+ if options[:allow_nil]
122
+ 'return'
123
+ else
124
+ %(raise "#{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
125
+ end
126
+
127
+ module_eval(<<-EOS, file, line)
128
+ def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
129
+ #{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
130
+ rescue NoMethodError # rescue NoMethodError
131
+ if #{to}.nil? # if client.nil?
132
+ #{on_nil}
133
+ else # else
134
+ raise # raise
135
+ end # end
136
+ end # end
137
+ EOS
138
+ end
139
+ end
140
+ # Encapsulates the common pattern of:
141
+ #
142
+ # alias_method :foo_without_feature, :foo
143
+ # alias_method :foo, :foo_with_feature
144
+ #
145
+ # With this, you simply do:
146
+ #
147
+ # alias_method_chain :foo, :feature
148
+ #
149
+ # And both aliases are set up for you.
150
+ #
151
+ # Query and bang methods (foo?, foo!) keep the same punctuation:
152
+ #
153
+ # alias_method_chain :foo?, :feature
154
+ #
155
+ # is equivalent to
156
+ #
157
+ # alias_method :foo_without_feature?, :foo?
158
+ # alias_method :foo?, :foo_with_feature?
159
+ #
160
+ # so you can safely chain foo, foo?, and foo! with the same feature.
161
+ def alias_method_chain(target, feature)
162
+ # Strip out punctuation on predicates or bang methods since
163
+ # e.g. target?_without_feature is not a valid method name.
164
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
165
+ yield(aliased_target, punctuation) if block_given?
166
+
167
+ with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
168
+
169
+ alias_method without_method, target
170
+ alias_method target, with_method
171
+
172
+ case
173
+ when public_method_defined?(without_method)
174
+ public target
175
+ when protected_method_defined?(without_method)
176
+ protected target
177
+ when private_method_defined?(without_method)
178
+ private target
179
+ end
180
+ end
181
+
182
+ # Allows you to make aliases for attributes, which includes
183
+ # getter, setter, and query methods.
184
+ #
185
+ # Example:
186
+ #
187
+ # class Content < ActiveRecord::Base
188
+ # # has a title attribute
189
+ # end
190
+ #
191
+ # class Email < Content
192
+ # alias_attribute :subject, :title
193
+ # end
194
+ #
195
+ # e = Email.find(1)
196
+ # e.title # => "Superstars"
197
+ # e.subject # => "Superstars"
198
+ # e.subject? # => true
199
+ # e.subject = "Megastars"
200
+ # e.title # => "Megastars"
201
+ def alias_attribute(new_name, old_name)
202
+ module_eval <<-STR, __FILE__, __LINE__ + 1
203
+ def #{new_name}; self.#{old_name}; end # def subject; self.title; end
204
+ def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
205
+ def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
206
+ STR
207
+ end
208
+ end
209
+
210
+ class Object
211
+ # Invokes the method identified by the symbol +method+, passing it any arguments
212
+ # and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
213
+ #
214
+ # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
215
+ # and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
216
+ #
217
+ # ==== Examples
218
+ #
219
+ # Without try
220
+ # @person && @person.name
221
+ # or
222
+ # @person ? @person.name : nil
223
+ #
224
+ # With try
225
+ # @person.try(:name)
226
+ #
227
+ # +try+ also accepts arguments and/or a block, for the method it is trying
228
+ # Person.try(:find, 1)
229
+ # @people.try(:collect) {|p| p.name}
230
+ #--
231
+ # This method definition below is for rdoc purposes only. The alias_method call
232
+ # below overrides it as an optimization since +try+ behaves like +Object#send+,
233
+ # unless called on +NilClass+.
234
+ def try(method, *args, &block)
235
+ send(method, *args, &block)
236
+ end
237
+ remove_method :try
238
+ alias_method :try, :__send__
239
+ end
240
+
241
+ class NilClass
242
+ def try(*args)
243
+ nil
244
+ end
245
+ end
246
+
1
247
  module Resourceful
2
248
  class Resource
249
+ def options(header = {})
250
+ request(:options, nil, header)
251
+ end
252
+
3
253
  def host_with_port
4
254
  add = Addressable::URI.parse(uri)
5
- !add.port.blank? && add.port != 80 ? [add.host, add.port].join(':') : add.host
255
+ !add.port.nil? && add.port != 80 ? [add.host, add.port].join(':') : add.host
6
256
  end
7
257
 
8
258
  alias_method_chain :host, :port
9
259
  end
260
+
261
+ class NetHttpAdapter
262
+ def net_http_request_class_with_options(method)
263
+ if method == :options
264
+ Net::HTTP::Options
265
+ else
266
+ net_http_request_class_without_options(method)
267
+ end
268
+ end
269
+
270
+ alias_method_chain :net_http_request_class, :options
271
+ end
272
+ end
273
+
274
+ class Array
275
+ # Returns a string that represents this array in XML by sending +to_xml+
276
+ # to each element. Active Record collections delegate their representation
277
+ # in XML to this method.
278
+ #
279
+ # All elements are expected to respond to +to_xml+, if any of them does
280
+ # not an exception is raised.
281
+ #
282
+ # The root node reflects the class name of the first element in plural
283
+ # if all elements belong to the same type and that's not Hash:
284
+ #
285
+ # customer.projects.to_xml
286
+ #
287
+ # <?xml version="1.0" encoding="UTF-8"?>
288
+ # <projects type="array">
289
+ # <project>
290
+ # <amount type="decimal">20000.0</amount>
291
+ # <customer-id type="integer">1567</customer-id>
292
+ # <deal-date type="date">2008-04-09</deal-date>
293
+ # ...
294
+ # </project>
295
+ # <project>
296
+ # <amount type="decimal">57230.0</amount>
297
+ # <customer-id type="integer">1567</customer-id>
298
+ # <deal-date type="date">2008-04-15</deal-date>
299
+ # ...
300
+ # </project>
301
+ # </projects>
302
+ #
303
+ # Otherwise the root element is "records":
304
+ #
305
+ # [{:foo => 1, :bar => 2}, {:baz => 3}].to_xml
306
+ #
307
+ # <?xml version="1.0" encoding="UTF-8"?>
308
+ # <records type="array">
309
+ # <record>
310
+ # <bar type="integer">2</bar>
311
+ # <foo type="integer">1</foo>
312
+ # </record>
313
+ # <record>
314
+ # <baz type="integer">3</baz>
315
+ # </record>
316
+ # </records>
317
+ #
318
+ # If the collection is empty the root element is "nil-classes" by default:
319
+ #
320
+ # [].to_xml
321
+ #
322
+ # <?xml version="1.0" encoding="UTF-8"?>
323
+ # <nil-classes type="array"/>
324
+ #
325
+ # To ensure a meaningful root element use the <tt>:root</tt> option:
326
+ #
327
+ # customer_with_no_projects.projects.to_xml(:root => "projects")
328
+ #
329
+ # <?xml version="1.0" encoding="UTF-8"?>
330
+ # <projects type="array"/>
331
+ #
332
+ # By default root children have as node name the one of the root
333
+ # singularized. You can change it with the <tt>:children</tt> option.
334
+ #
335
+ # The +options+ hash is passed downwards:
336
+ #
337
+ # Message.all.to_xml(:skip_types => true)
338
+ #
339
+ # <?xml version="1.0" encoding="UTF-8"?>
340
+ # <messages>
341
+ # <message>
342
+ # <created-at>2008-03-07T09:58:18+01:00</created-at>
343
+ # <id>1</id>
344
+ # <name>1</name>
345
+ # <updated-at>2008-03-07T09:58:18+01:00</updated-at>
346
+ # <user-id>1</user-id>
347
+ # </message>
348
+ # </messages>
349
+ #
350
+ def to_xml(options = {})
351
+ raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml }
352
+ require 'builder' unless defined?(Builder)
353
+
354
+ options = options.dup
355
+ options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize.tr('/', '-') : "records"
356
+ options[:children] ||= options[:root].singularize
357
+ options[:indent] ||= 2
358
+ options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
359
+
360
+ root = options.delete(:root).to_s
361
+ children = options.delete(:children)
362
+
363
+ if !options.has_key?(:dasherize) || options[:dasherize]
364
+ root = root.dasherize
365
+ end
366
+
367
+ options[:builder].instruct! unless options.delete(:skip_instruct)
368
+
369
+ opts = options.merge({ :root => children })
370
+
371
+ xml = options[:builder]
372
+ if empty?
373
+ xml.tag!(root, options[:skip_types] ? {} : {:type => "array"})
374
+ else
375
+ xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) {
376
+ yield xml if block_given?
377
+ each { |e| e.to_xml(opts.merge({ :skip_instruct => true })) }
378
+ }
379
+ end
380
+ end
10
381
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abiquo
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 31
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 1
8
- - 0
9
- version: 0.1.0
9
+ - 2
10
+ version: 0.1.2
10
11
  platform: ruby
11
12
  authors:
12
13
  - Abiquo
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-07-28 00:00:00 +02:00
18
+ date: 2011-02-23 00:00:00 +01:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: resourceful
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 3
27
30
  segments:
28
31
  - 0
29
32
  version: "0"
@@ -33,50 +36,44 @@ dependencies:
33
36
  name: nokogiri
34
37
  prerelease: false
35
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
36
40
  requirements:
37
41
  - - ">="
38
42
  - !ruby/object:Gem::Version
43
+ hash: 3
39
44
  segments:
40
45
  - 0
41
46
  version: "0"
42
47
  type: :runtime
43
48
  version_requirements: *id002
44
49
  - !ruby/object:Gem::Dependency
45
- name: activesupport
50
+ name: steak
46
51
  prerelease: false
47
52
  requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
48
54
  requirements:
49
55
  - - ">="
50
56
  - !ruby/object:Gem::Version
57
+ hash: 3
51
58
  segments:
52
59
  - 0
53
60
  version: "0"
54
- type: :runtime
61
+ type: :development
55
62
  version_requirements: *id003
56
63
  - !ruby/object:Gem::Dependency
57
- name: steak
64
+ name: webmock
58
65
  prerelease: false
59
66
  requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
60
68
  requirements:
61
69
  - - ">="
62
70
  - !ruby/object:Gem::Version
71
+ hash: 3
63
72
  segments:
64
73
  - 0
65
74
  version: "0"
66
75
  type: :development
67
76
  version_requirements: *id004
68
- - !ruby/object:Gem::Dependency
69
- name: webmock
70
- prerelease: false
71
- requirement: &id005 !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- segments:
76
- - 0
77
- version: "0"
78
- type: :development
79
- version_requirements: *id005
80
77
  description:
81
78
  email: support@abiquo.com
82
79
  executables: []
@@ -86,12 +83,14 @@ extensions: []
86
83
  extra_rdoc_files:
87
84
  - README
88
85
  files:
86
+ - lib/active_support/inflector.rb
87
+ - lib/active_support/inflections.rb
89
88
  - lib/to_xml.rb
90
89
  - lib/core_ext.rb
91
90
  - lib/abiquo.rb
92
91
  - README
93
92
  has_rdoc: true
94
- homepage: http://wiki.github.com/abiquo/abiquo/
93
+ homepage: http://github.com/abiquo/api_ruby_client
95
94
  licenses: []
96
95
 
97
96
  post_install_message:
@@ -101,23 +100,27 @@ rdoc_options:
101
100
  require_paths:
102
101
  - lib
103
102
  required_ruby_version: !ruby/object:Gem::Requirement
103
+ none: false
104
104
  requirements:
105
105
  - - ">="
106
106
  - !ruby/object:Gem::Version
107
+ hash: 3
107
108
  segments:
108
109
  - 0
109
110
  version: "0"
110
111
  required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
111
113
  requirements:
112
114
  - - ">="
113
115
  - !ruby/object:Gem::Version
116
+ hash: 3
114
117
  segments:
115
118
  - 0
116
119
  version: "0"
117
120
  requirements: []
118
121
 
119
122
  rubyforge_project:
120
- rubygems_version: 1.3.6
123
+ rubygems_version: 1.3.7
121
124
  signing_key:
122
125
  specification_version: 3
123
126
  summary: Abiquo API client