abiquo 0.1.0 → 0.1.2

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/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