softlayer_api 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.textile +36 -0
- data/README.textile +17 -7
- data/examples/account_info.rb +6 -3
- data/examples/account_servers.rb +48 -0
- data/examples/create_ticket.rb +33 -22
- data/examples/open_tickets.rb +14 -19
- data/examples/order_bare_metal_package.rb +154 -0
- data/examples/order_virtual_server.rb +85 -0
- data/examples/ticket_info.rb +13 -14
- data/lib/softlayer/APIParameterFilter.rb +100 -23
- data/lib/softlayer/Account.rb +140 -0
- data/lib/softlayer/BareMetalServer.rb +233 -0
- data/lib/softlayer/BareMetalServerOrder.rb +227 -0
- data/lib/softlayer/BareMetalServerOrder_Package.rb +162 -0
- data/lib/softlayer/Client.rb +54 -9
- data/lib/softlayer/Config.rb +2 -3
- data/lib/softlayer/DynamicAttribute.rb +170 -0
- data/lib/softlayer/ModelBase.rb +141 -0
- data/lib/softlayer/ObjectFilter.rb +61 -21
- data/lib/softlayer/ObjectMaskParser.rb +157 -0
- data/lib/softlayer/ObjectMaskProperty.rb +83 -0
- data/lib/softlayer/ObjectMaskToken.rb +107 -0
- data/lib/softlayer/ObjectMaskTokenizer.rb +88 -0
- data/lib/softlayer/ProductItemCategory.rb +137 -0
- data/lib/softlayer/ProductPackage.rb +196 -0
- data/lib/softlayer/Server.rb +245 -0
- data/lib/softlayer/Service.rb +12 -9
- data/lib/softlayer/Ticket.rb +210 -0
- data/lib/softlayer/VirtualServer.rb +388 -0
- data/lib/softlayer/VirtualServerOrder.rb +263 -0
- data/lib/softlayer/base.rb +9 -9
- data/lib/softlayer/object_mask_helpers.rb +46 -18
- data/lib/softlayer_api.rb +15 -0
- metadata +49 -15
data/lib/softlayer/Config.rb
CHANGED
@@ -28,7 +28,7 @@ module SoftLayer
|
|
28
28
|
result = {}
|
29
29
|
result[:username] = $SL_API_USERNAME if $SL_API_USERNAME
|
30
30
|
result[:api_key] = $SL_API_KEY if $SL_API_KEY
|
31
|
-
result[:endpoint_url] = $SL_API_BASE_URL
|
31
|
+
result[:endpoint_url] = $SL_API_BASE_URL || API_PUBLIC_ENDPOINT
|
32
32
|
result
|
33
33
|
end
|
34
34
|
|
@@ -39,7 +39,7 @@ module SoftLayer
|
|
39
39
|
result
|
40
40
|
end
|
41
41
|
|
42
|
-
FILE_LOCATIONS = ['/etc/softlayer.conf', '~/.softlayer']
|
42
|
+
FILE_LOCATIONS = ['/etc/softlayer.conf', '~/.softlayer', './.softlayer']
|
43
43
|
|
44
44
|
def Config.file_settings(*additional_files)
|
45
45
|
result = {}
|
@@ -49,7 +49,6 @@ module SoftLayer
|
|
49
49
|
search_path = search_path.map { |file_path| File.expand_path(file_path) }
|
50
50
|
|
51
51
|
search_path.each do |file_path|
|
52
|
-
|
53
52
|
if File.readable? file_path
|
54
53
|
config = ConfigParser.new file_path
|
55
54
|
softlayer_section = config["softlayer"]
|
@@ -0,0 +1,170 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
|
23
|
+
module SoftLayer
|
24
|
+
|
25
|
+
##
|
26
|
+
# This module is inteneded to be used by classes in the SoftLayer
|
27
|
+
# object model. It creates a small DSL for creating attributes
|
28
|
+
# that update themselves dynamically (usually by making requests
|
29
|
+
# to the SoftLayer API)
|
30
|
+
#
|
31
|
+
# +sl_dynamic_attr+ is an implementation of a memoization scheme
|
32
|
+
# The module creates a getter which is implemented in terms of a
|
33
|
+
# predicate (identifying whether or not the attribute needs to be updated) and
|
34
|
+
# an update routine
|
35
|
+
#
|
36
|
+
# When the getter is called, it checks the predicate routine to see if
|
37
|
+
# the attribute needs to be updated. If it doesn't, then the getter simply
|
38
|
+
# returns the cached value for the attribute. If the attribute does need
|
39
|
+
# to be updated, the getter calls the update routine to get a new value
|
40
|
+
# and caches that value off before returning it to the caller.
|
41
|
+
#
|
42
|
+
# Declaring a attribute adds three methods to a class and
|
43
|
+
# a corresponding instance variable in instances of the class
|
44
|
+
# All three are based on the name of the attribute:
|
45
|
+
#
|
46
|
+
# * The getter simply has the same name as the attribute
|
47
|
+
# * The predicate routine is called +should_update_<attribute name>?+
|
48
|
+
# * The updating routine is called +update_<attribute name>!+
|
49
|
+
#
|
50
|
+
# The getter can also be called with a boolean argument. If that
|
51
|
+
# argument is true, the getter will force the attribute to be updated
|
52
|
+
# without consulting the +should_update?+ predicate
|
53
|
+
#
|
54
|
+
# When a attribute is defined, the definition takes a block.
|
55
|
+
# Inside the block there is a small DSL that allows you to
|
56
|
+
# set the behavior of the +should_update?+ predicate and the +update_!+
|
57
|
+
# routine.
|
58
|
+
#
|
59
|
+
# A attribute definition might look something like this:
|
60
|
+
#
|
61
|
+
# sl_dynamic_attr :lollipop do |lollipop|
|
62
|
+
# lollipop.should_update? do
|
63
|
+
# self.lollipop_supply_is_low?
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# lollipop.to_update do
|
67
|
+
# candy_store.buy_lollipops(bakers_dozen)
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
module DynamicAttribute
|
72
|
+
|
73
|
+
# The DynamicAttributeDefinition inner class is to collect and
|
74
|
+
# store information about how and when a sl_dynamic_attr
|
75
|
+
# should be updated. This class is an implementation detail
|
76
|
+
# of dynamic attributes and is not intended to be useful
|
77
|
+
# outside of that context.
|
78
|
+
class DynamicAttributeDefinition
|
79
|
+
# the name of the attribute this definition is for
|
80
|
+
attr_reader :attribute_name
|
81
|
+
|
82
|
+
# The block to call in order to update the attribute. The
|
83
|
+
# return value of this block should be the new value of the
|
84
|
+
# attribute.
|
85
|
+
attr_reader :update_block
|
86
|
+
|
87
|
+
# The block to call to see if the attribute needs to be updated.
|
88
|
+
attr_reader :should_update_block
|
89
|
+
|
90
|
+
def initialize(attribute_name)
|
91
|
+
raise ArgumentError if attribute_name.nil?
|
92
|
+
raise ArgumentError if attribute_name.to_s.empty?
|
93
|
+
|
94
|
+
@attribute_name = attribute_name;
|
95
|
+
@update_block = Proc.new { nil; };
|
96
|
+
@should_update_block = Proc.new { true; }
|
97
|
+
end
|
98
|
+
|
99
|
+
# This method is used to provide behavior for the
|
100
|
+
# should_update_ predicate for the attribute
|
101
|
+
def should_update? (&block)
|
102
|
+
@should_update_block = block
|
103
|
+
end
|
104
|
+
|
105
|
+
# This method is used to provide the behavior for
|
106
|
+
# the update_! method for the attribute.
|
107
|
+
def to_update (&block)
|
108
|
+
@update_block = block
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
module ClassMethods
|
113
|
+
# sl_dynamic_attr declares a new dynamic softlayer attribute and accepts
|
114
|
+
# a block in which the should_update? and to_update methods for the
|
115
|
+
# attribute are established.
|
116
|
+
def sl_dynamic_attr (attribute_name, &block)
|
117
|
+
attribute_definition = DynamicAttributeDefinition.new(attribute_name)
|
118
|
+
|
119
|
+
# allow the block to update the attribute definition
|
120
|
+
yield attribute_definition if block_given?
|
121
|
+
|
122
|
+
# store off the attribute definition where we can find it later
|
123
|
+
@attribute_definitions ||= {};
|
124
|
+
@attribute_definitions[attribute_name] = attribute_definition;
|
125
|
+
|
126
|
+
# define a method called "update_<attribute_name>!" which calls the update block
|
127
|
+
# stored in the attribute definition
|
128
|
+
update_symbol = "update_#{attribute_name}!".to_sym
|
129
|
+
define_method(update_symbol, &attribute_definition.update_block)
|
130
|
+
|
131
|
+
# define a method called "should_update_<attribute_name>?" which calls the
|
132
|
+
# should update block stored in the attribute definition
|
133
|
+
should_update_symbol = "should_update_#{attribute_name}?".to_sym
|
134
|
+
define_method(should_update_symbol, &attribute_definition.should_update_block)
|
135
|
+
|
136
|
+
# define an instance method of the class this is being
|
137
|
+
# called on which will get the value of the attribute.
|
138
|
+
#
|
139
|
+
# The getter will take one argument "force_update" which
|
140
|
+
# is treated as boolean value. If true, then the getter will
|
141
|
+
# force the attribute to update (by using its "to_update") block.
|
142
|
+
#
|
143
|
+
# If the force variable is false, or not given, then the
|
144
|
+
# getter will call the "should update" block to find out if the
|
145
|
+
# attribute needs to be updated.
|
146
|
+
#
|
147
|
+
getter_name = attribute_name.to_sym
|
148
|
+
value_instance_variable = "@#{attribute_name}".to_sym
|
149
|
+
|
150
|
+
define_method(getter_name) do |*args|
|
151
|
+
force_update = args[0] || false
|
152
|
+
|
153
|
+
if force_update || __send__(should_update_symbol)
|
154
|
+
instance_variable_set(value_instance_variable, __send__(update_symbol))
|
155
|
+
end
|
156
|
+
|
157
|
+
instance_variable_get(value_instance_variable)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def sl_dynamic_attr_definition(attribute_name)
|
162
|
+
@attribute_definitions[attribute_name]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.included(included_in)
|
167
|
+
included_in.extend(ClassMethods)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
|
23
|
+
module SoftLayer
|
24
|
+
##
|
25
|
+
# The SoftLayer Gem defines an Object Hierarchy representing entities in
|
26
|
+
# an account's SoftLayer environment. This class is the base object class
|
27
|
+
# for objects in that hierarchy
|
28
|
+
#
|
29
|
+
# The SoftLayer API represents entities as a hash of properties. This class
|
30
|
+
# stores that hash and allows the use of subscripting to access those properties
|
31
|
+
# directly.
|
32
|
+
#
|
33
|
+
# The class also has a model for making network requests that will refresh
|
34
|
+
# the stored hash so that it reflects the most up-to-date information about
|
35
|
+
# an entity from the server. Subclasses should override softlayer_properties
|
36
|
+
# to retrieve information from the server. Client code should call
|
37
|
+
# refresh_details to ask an object to update itself.
|
38
|
+
#
|
39
|
+
class ModelBase
|
40
|
+
# The client environment that this model object belongs to
|
41
|
+
attr_reader :softlayer_client
|
42
|
+
|
43
|
+
##
|
44
|
+
# :attr_reader: id
|
45
|
+
# The unique identifier of this object within its API service
|
46
|
+
|
47
|
+
# Construct a new model object in the environment of the given client and
|
48
|
+
# with the given hash of network data (presumably returned by the SoftLayer API)
|
49
|
+
def initialize(softlayer_client, network_hash)
|
50
|
+
raise ArgumentError, "A hash is required" if nil == network_hash
|
51
|
+
raise ArgumentError, "Model objects must be created in the context of a client" if nil == softlayer_client
|
52
|
+
|
53
|
+
@softlayer_client = softlayer_client
|
54
|
+
@softlayer_hash = network_hash
|
55
|
+
|
56
|
+
raise ArgumentError, "The hash used to construct a softlayer model object must have an id" unless has_sl_property?(:id)
|
57
|
+
raise ArgumentError, "id must be non-nil and non-empty" unless self[:id]
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# The service method of a Model object should return a SoftLayer Service
|
62
|
+
# that best represents the modeled object. For example, a Ticket models
|
63
|
+
# a particular entity in the SoftLayer_Ticket service. The particular
|
64
|
+
# entity is identified by its id so the Ticket class would return
|
65
|
+
#
|
66
|
+
# softlayer_client["Ticket"].object_with_id
|
67
|
+
#
|
68
|
+
# which is a service which would allow calls to the ticket service
|
69
|
+
# through that particular object.
|
70
|
+
def service
|
71
|
+
raise "Abstract method service in ModelBase was called"
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Asks a model object to reload itself from the SoftLayer API.
|
76
|
+
#
|
77
|
+
# Subclasses should not override this method, rather they should
|
78
|
+
# implement softlayer_properties to actually make the API request
|
79
|
+
# and return the new hash.
|
80
|
+
#
|
81
|
+
def refresh_details(object_mask = nil)
|
82
|
+
@softlayer_hash = self.softlayer_properties(object_mask)
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Subclasses should implement this method as part of enabling the
|
87
|
+
# refresh_details fuctionality The implementation should make a request
|
88
|
+
# to the SoftLayer API and retrieve an up-to-date SoftLayer hash
|
89
|
+
# representation of this object. That hash should be the return value
|
90
|
+
# of this routine.
|
91
|
+
#
|
92
|
+
def softlayer_properties(object_mask = nil)
|
93
|
+
raise "Abstract method softlayer_properties in ModelBase was called"
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Returns the value of of the given property as stored in the
|
98
|
+
# softlayer_hash. This gives you access to the low-level, raw
|
99
|
+
# properties that underly this model object. The need for this
|
100
|
+
# is not uncommon, but using this method should still be done
|
101
|
+
# with deliberation.
|
102
|
+
def [](softlayer_property)
|
103
|
+
self.softlayer_hash[softlayer_property.to_s]
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Returns true if the given property can be found in the softlayer hash
|
108
|
+
def has_sl_property?(softlayer_property)
|
109
|
+
softlayer_hash && softlayer_hash.has_key?(softlayer_property.to_s)
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# allows subclasses to define attributes as sl_attr
|
114
|
+
# sl_attr are attributes that draw their value from the
|
115
|
+
# low-level hash representation of the object.
|
116
|
+
def self.sl_attr(attribute_symbol, hash_key = nil)
|
117
|
+
raise "The sl_attr expects a symbol for the attribute to define" unless attribute_symbol.kind_of?(Symbol)
|
118
|
+
raise "The hash key used to define an attribute cannot be empty" if hash_key && hash_key.empty?
|
119
|
+
|
120
|
+
define_method(attribute_symbol.to_sym) { self[hash_key ? hash_key : attribute_symbol.to_s]}
|
121
|
+
end
|
122
|
+
|
123
|
+
sl_attr :id
|
124
|
+
|
125
|
+
# When printing to the console using puts, ruby will call the
|
126
|
+
# to_ary method trying to convert an object into an array of lines
|
127
|
+
# for stdio. We override to_ary to return nil for model objects
|
128
|
+
# so they may be printed
|
129
|
+
def to_ary()
|
130
|
+
return nil;
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
##
|
136
|
+
# The softlayer_hash stores the low-level information about an
|
137
|
+
# object as it was retrieved from the SoftLayer API.
|
138
|
+
attr_reader :softlayer_hash
|
139
|
+
|
140
|
+
end # class ModelBase
|
141
|
+
end # module SoftLayer
|
@@ -35,9 +35,13 @@ module SoftLayer
|
|
35
35
|
'!~' # Does not Contain (case sensitive)
|
36
36
|
]
|
37
37
|
|
38
|
-
# A class whose instances represent an Object Filter
|
38
|
+
# A class whose instances represent an Object Filter operator and the value it is applied to.
|
39
39
|
class ObjectFilterOperation
|
40
|
+
|
41
|
+
# The operator, should be a member of the SoftLayer::OBJECT_FILTER_OPERATORS array
|
40
42
|
attr_reader :operator
|
43
|
+
|
44
|
+
# The operand of the operator
|
41
45
|
attr_reader :value
|
42
46
|
|
43
47
|
def initialize(operator, value)
|
@@ -49,71 +53,105 @@ module SoftLayer
|
|
49
53
|
end
|
50
54
|
|
51
55
|
def to_h
|
52
|
-
|
56
|
+
result = ObjectFilter.new
|
57
|
+
result['operation'] = "#{operator} #{value}"
|
58
|
+
|
59
|
+
result
|
53
60
|
end
|
54
61
|
end
|
55
62
|
|
56
|
-
#
|
57
|
-
# ObjectFilter.build.
|
63
|
+
# This class defines the routines that are valid within the block provided to a call to
|
64
|
+
# ObjectFilter.build. This allows you to create object filters like:
|
65
|
+
#
|
66
|
+
# object_filter = SoftLayer::ObjectFilter.build("hardware.memory") { is_greater_than(2) }
|
67
|
+
#
|
58
68
|
class ObjectFilterBlockHandler
|
59
|
-
#
|
69
|
+
# Matches when the value is found within the field
|
70
|
+
# the search is not case sensitive
|
60
71
|
def contains(value)
|
61
72
|
ObjectFilterOperation.new('*=', value)
|
62
73
|
end
|
63
74
|
|
64
|
-
#
|
75
|
+
# Matches when the value is found at the beginning of the
|
76
|
+
# field. This search is not case sensitive
|
65
77
|
def begins_with(value)
|
66
78
|
ObjectFilterOperation.new('^=', value)
|
67
79
|
end
|
68
80
|
|
69
|
-
#
|
81
|
+
# Matches when the value is found at the end of the
|
82
|
+
# field. This search is not case sensitive
|
70
83
|
def ends_with(value)
|
71
84
|
ObjectFilterOperation.new('$=', value)
|
72
85
|
end
|
73
86
|
|
74
|
-
#
|
87
|
+
# Matches when the value in the field is exactly equal to the
|
88
|
+
# given value. This is a case-sensitive match
|
75
89
|
def is(value)
|
76
90
|
ObjectFilterOperation.new('_=', value)
|
77
91
|
end
|
78
92
|
|
93
|
+
# Matches is the value in the field does not exactly equal
|
94
|
+
# the value passed in.
|
79
95
|
def is_not(value)
|
80
96
|
ObjectFilterOperation.new('!=', value)
|
81
97
|
end
|
82
98
|
|
99
|
+
# Matches when the value in the field is greater than the given value
|
83
100
|
def is_greater_than(value)
|
84
101
|
ObjectFilterOperation.new('>', value)
|
85
102
|
end
|
86
103
|
|
104
|
+
# Matches when the value in the field is less than the given value
|
87
105
|
def is_less_than(value)
|
88
106
|
ObjectFilterOperation.new('<', value)
|
89
107
|
end
|
90
108
|
|
109
|
+
# Matches when the value in the field is greater than or equal to the given value
|
91
110
|
def is_greater_or_equal_to(value)
|
92
111
|
ObjectFilterOperation.new('>=', value)
|
93
112
|
end
|
94
113
|
|
114
|
+
# Matches when the value in the field is less than or equal to the given value
|
95
115
|
def is_less_or_equal_to(value)
|
96
116
|
ObjectFilterOperation.new('<=', value)
|
97
117
|
end
|
98
118
|
|
119
|
+
# Matches when the value is found within the field
|
120
|
+
# the search _is_ case sensitive
|
99
121
|
def contains_exactly(value)
|
100
122
|
ObjectFilterOperation.new('~', value)
|
101
123
|
end
|
102
124
|
|
125
|
+
# Matches when the value is not found within the field
|
126
|
+
# the search _is_ case sensitive
|
103
127
|
def does_not_contain(value)
|
104
128
|
ObjectFilterOperation.new('!~', value)
|
105
129
|
end
|
106
130
|
end
|
107
131
|
|
108
|
-
#
|
132
|
+
#
|
133
|
+
# An ObjectFilter is a tool that, when passed to the SoftLayer API
|
134
|
+
# allows the API server to filter, or limit the result set for a call.
|
135
|
+
#
|
136
|
+
# Constructing ObjectFilters is an art that is currently somewhat
|
137
|
+
# arcane. This class tries to simplify filtering for the fundamental
|
138
|
+
# cases, while still allowing for more complex ObjectFilters to be
|
139
|
+
# created.
|
140
|
+
#
|
141
|
+
# The ObjectFilter class is implemented as a hash that, when asked to provide
|
109
142
|
# an value for an unknown key, will create a sub element
|
110
|
-
# at that key
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
143
|
+
# at that key which is, itself, an object filter. This allows you to build
|
144
|
+
# up object filters by chaining [] dereference operations.
|
145
|
+
#
|
146
|
+
# Starting empty object filter when you ask for +object_filter["foo"]+
|
147
|
+
# either the value at that hash location will be returned, or a new +foo+ key
|
148
|
+
# will be *added* to the object. The value of that key will be an +ObjectFilter+
|
149
|
+
# and that +ObjectFilter+ will be returned.
|
114
150
|
#
|
115
|
-
#
|
116
|
-
#
|
151
|
+
# By way of an example of chaining together +[]+ calls:
|
152
|
+
# object_filter["foo"]["bar"]["baz"] = 3
|
153
|
+
# yields an object filter like this:
|
154
|
+
# {"foo" => { "bar" => {"baz" => 3}}}
|
117
155
|
#
|
118
156
|
class ObjectFilter < Hash
|
119
157
|
# The default initialize for a hash is overridden
|
@@ -146,7 +184,7 @@ module SoftLayer
|
|
146
184
|
end
|
147
185
|
|
148
186
|
# if there is a block, then the query will come from
|
149
|
-
# calling the block.
|
187
|
+
# calling the block. We warn in debug mode if you override a
|
150
188
|
# query that was passed directly with the value from a block.
|
151
189
|
if block
|
152
190
|
$stderr.puts "The query from the block passed to ObjectFilter:build will override the query passed as a parameter" if $DEBUG && query
|
@@ -154,7 +192,7 @@ module SoftLayer
|
|
154
192
|
query = block_handler.instance_eval(&block)
|
155
193
|
end
|
156
194
|
|
157
|
-
# If we have a query, we assign
|
195
|
+
# If we have a query, we assign its value to the last key
|
158
196
|
# otherwise, we build an emtpy filter at the bottom
|
159
197
|
if query
|
160
198
|
case
|
@@ -176,9 +214,9 @@ module SoftLayer
|
|
176
214
|
result
|
177
215
|
end
|
178
216
|
|
179
|
-
# This method
|
180
|
-
# by
|
181
|
-
#
|
217
|
+
# This method simplifies creating correct object filter structures
|
218
|
+
# by defining a simple query language. It translates strings in that
|
219
|
+
# language into an Object Filter operations
|
182
220
|
#
|
183
221
|
# Object Filter comparisons are done using operators. Some operators make
|
184
222
|
# case sensitive comparisons and some do not. The general form of an Object
|
@@ -187,13 +225,15 @@ module SoftLayer
|
|
187
225
|
# "*= smaug"
|
188
226
|
#
|
189
227
|
# The query language also accepts some aliases using asterisks
|
190
|
-
# in a regular-expression-like way.
|
228
|
+
# in a regular-expression-like way. Those aliases look like:
|
191
229
|
#
|
192
230
|
# 'value' Exact value match (translates to '_= value')
|
193
231
|
# 'value*' Begins with value (translates to '^= value')
|
194
232
|
# '*value' Ends with value (translates to '$= value')
|
195
233
|
# '*value*' Contains value (translates to '*= value')
|
196
234
|
#
|
235
|
+
# This method corresponds to the +query_filter+ method in the SoftLayer-Python
|
236
|
+
# API.
|
197
237
|
def self.query_to_filter_operation(query)
|
198
238
|
if query.kind_of? String then
|
199
239
|
query.strip!
|