Skip to content

Commit 22d1438

Browse files
committed
Made it so that all the model objects implement the 'service' attribute. That attribute creates a service object tailored to that specific object and should allow calls through the object to the SoftLayer API. In that it does so this
resolves #21
1 parent 6945a63 commit 22d1438

21 files changed

Lines changed: 269 additions & 105 deletions

examples/account_servers.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
$LOAD_PATH << File.join(File.dirname(__FILE__), "../lib" )
2+
3+
#
4+
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
5+
#
6+
# Permission is hereby granted, free of charge, to any person obtaining a copy
7+
# of this software and associated documentation files (the "Software"), to deal
8+
# in the Software without restriction, including without limitation the rights
9+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
# copies of the Software, and to permit persons to whom the Software is
11+
# furnished to do so, subject to the following conditions:
12+
#
13+
# The above copyright notice and this permission notice shall be included in
14+
# all copies or substantial portions of the Software.
15+
#
16+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
# THE SOFTWARE.
23+
#
24+
25+
require 'rubygems'
26+
require 'softlayer_api'
27+
require 'pp'
28+
29+
begin
30+
client = SoftLayer::Client.new(
31+
# :username => "joecustomer" # enter your username here
32+
# :api_key => "feeddeadbeefbadf00d..." # enter your api key here
33+
)
34+
35+
account = SoftLayer::Account.account_for_client(client)
36+
37+
# grab a list of all the servers on the account.
38+
servers = account.servers
39+
40+
# measure their fully qualified domain names so we can print a pretty table
41+
max_name_len = servers.inject(0) { |max_name, server| [max_name, server.fullyQualifiedDomainName.length].max }
42+
43+
printf "%#{-max_name_len}s\tPrimary Public IP\n", "Server FQDN"
44+
printf "%#{-max_name_len}s\t-----------------\n", "-----------"
45+
46+
servers.each do |server|
47+
ip_field = server.primary_public_ip ? server.primary_public_ip : "No Public Interface"
48+
printf "%#{-max_name_len}s\t#{ip_field}\n", server.fullyQualifiedDomainName
49+
end
50+
51+
rescue
52+
puts "An unexpected exception occurred!"
53+
end

lib/softlayer/APIParameterFilter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def object_filter(filter)
129129
##
130130
# A utility method that returns the server object ID (if any) stored
131131
# in this parameter set.
132-
def server_object_id
132+
def server_object_id
133133
self.parameters[:server_object_id]
134134
end
135135

lib/softlayer/Account.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ class Account < SoftLayer::ModelBase
137137
end
138138
end
139139

140+
def service
141+
softlayer_client["Account"].object_with_id(self.id)
142+
end
143+
140144
##
141145
# Using the login credentials in the client, retrieve
142146
# the account associated with those credentials.

lib/softlayer/BareMetalServer.rb

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,11 @@ def cancel!(reason = :unneeded, comment = '')
6868
end
6969

7070
##
71-
# Returns the SoftLayer Service used to work with instances of this class.
71+
# Returns the SoftLayer Service used to work with this Server
7272
# For Bare Metal Servers that is +SoftLayer_Hardware+ though in some special cases
7373
# you may have to use +SoftLayer_Hardware_Server+ as a type or service.
74-
#
75-
# This routine is largely an implementation detail of this object framework
7674
def service
77-
return softlayer_client["Hardware"]
75+
return softlayer_client["Hardware"].object_with_id(self.id)
7876
end
7977

8078
##
@@ -125,14 +123,14 @@ def self.cancellation_reasons
125123
# Retrive the bare metal server with the given server ID from the
126124
# SoftLayer API
127125
def self.server_with_id(softlayer_client, server_id, options = {})
128-
service = softlayer_client["Hardware"]
129-
service = service.object_mask(default_object_mask.to_sl_object_mask)
126+
hardware_service = softlayer_client["Hardware"]
127+
hardware_service = hardware_service.object_mask(default_object_mask.to_sl_object_mask)
130128

131129
if options.has_key?(:object_mask)
132-
object_mask = service.object_mask(options[:object_mask])
130+
object_mask = hardware_service.object_mask(options[:object_mask])
133131
end
134132

135-
server_data = service.object_with_id(server_id).getObject()
133+
server_data = hardware_service.object_with_id(server_id).getObject()
136134

137135
return BareMetalServer.new(softlayer_client, server_data)
138136
end
@@ -193,22 +191,22 @@ def self.find_servers(softlayer_client, options_hash = {})
193191
} ));
194192
end
195193

196-
service = softlayer_client['Account']
197-
service = service.object_filter(object_filter) unless object_filter.empty?
198-
service = service.object_mask(default_object_mask.to_sl_object_mask)
194+
account_service = softlayer_client['Account']
195+
account_service = account_service.object_filter(object_filter) unless object_filter.empty?
196+
account_service = account_service.object_mask(default_object_mask.to_sl_object_mask)
199197

200198
if(options_hash.has_key? :object_mask)
201-
service = service.object_mask(options_hash[:object_mask])
199+
account_service = account_service.object_mask(options_hash[:object_mask])
202200
end
203201

204202
if options_hash.has_key?(:result_limit)
205203
offset = options[:result_limit][:offset]
206204
limit = options[:result_limit][:limit]
207205

208-
service = service.result_limit(offset, limit)
206+
account_service = account_service.result_limit(offset, limit)
209207
end
210208

211-
bare_metal_data = service.getHardware()
209+
bare_metal_data = account_service.getHardware()
212210
bare_metal_data.collect { |server_data| BareMetalServer.new(softlayer_client, server_data) }
213211
end
214212
end #BareMetalServer

lib/softlayer/Client.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ module SoftLayer
2626
# SoftLayer API.
2727
#
2828
# The following symbols can be used as hash arguments to pass options to the constructor:
29-
# - +:username+ - a non-empty string providing the username to use for requests to the service
30-
# - +:api_key+ - a non-empty string providing the api key to use for requests to the service
31-
# - +:endpoint_url+ - a non-empty string providing the endpoint URL to use for requests to the service
29+
# - +:username+ - a non-empty string providing the username to use for requests to the client
30+
# - +:api_key+ - a non-empty string providing the api key to use for requests to the client
31+
# - +:endpoint_url+ - a non-empty string providing the endpoint URL to use for requests to the client
3232
#
3333
# If any of the options above are missing then the constructor will try to use the corresponding
3434
# global variable declared in the SoftLayer Module:

lib/softlayer/ModelBase.rb

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ module SoftLayer
2727
# for objects in that hierarchy
2828
#
2929
# The SoftLayer API represents entities as a hash of properties. This class
30-
# stores that hash and uses +method_missing+ to allow code to access fields in
31-
# that hash through simple method calls.
30+
# stores that hash and allows the use of subscripting to access those properties
31+
# directly.
3232
#
3333
# The class also has a model for making network requests that will refresh
3434
# the stored hash so that it reflects the most up-to-date information about
@@ -37,8 +37,15 @@ module SoftLayer
3737
# refresh_details to ask an object to update itself.
3838
#
3939
class ModelBase
40+
# The client environment that this model object belongs to
4041
attr_reader :softlayer_client
4142

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)
4249
def initialize(softlayer_client, network_hash)
4350
raise ArgumentError, "A hash is required" if nil == network_hash
4451

@@ -49,8 +56,18 @@ def initialize(softlayer_client, network_hash)
4956
raise ArgumentError, "id must be non-nil and non-empty" unless self[:id]
5057
end
5158

52-
def to_ary
53-
return nil
59+
##
60+
# The service method of a Model object should return a SoftLayer Service
61+
# that best represents the modeled object. For example, a Ticket models
62+
# a particular entity in the SoftLayer_Ticket service. The particular
63+
# entity is identified by its id so the Ticket class would return
64+
#
65+
# softlayer_client["Ticket"].object_with_id
66+
#
67+
# which is a service which would allow calls to the ticket service
68+
# through that particular object.
69+
def service
70+
raise "Abstract method service in ModelBase was called"
5471
end
5572

5673
##
@@ -72,7 +89,7 @@ def refresh_details(object_mask = nil)
7289
# of this routine.
7390
#
7491
def softlayer_properties(object_mask = nil)
75-
raise RuntimeError.new("Abstract method softlayer_properties in ModelBase was called")
92+
raise "Abstract method softlayer_properties in ModelBase was called"
7693
end
7794

7895
##
@@ -101,12 +118,17 @@ def self.sl_attr(attribute_symbol, hash_key = nil)
101118

102119
define_method(attribute_symbol.to_sym) { self[hash_key ? hash_key : attribute_symbol.to_s]}
103120
end
121+
122+
sl_attr :id
104123

105-
##
106-
# :attr_reader: id
107-
# The unique identifier of this object within it's API service
108-
sl_attr(:id)
109-
124+
# When printing to the console using puts, ruby will call the
125+
# to_ary method trying to convert an object into an array of lines
126+
# for stdio. We override to_ary to return nil for model objects
127+
# so they may be printed
128+
def to_ary()
129+
return nil;
130+
end
131+
110132
protected
111133

112134
##

lib/softlayer/ProductItemCategory.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@ def free?
4949
class ProductItemCategory < ModelBase
5050
include ::SoftLayer::DynamicAttribute
5151

52-
sl_dynamic_attr :configuration_options do |resource|
53-
resource.should_update? do
52+
sl_dynamic_attr :configuration_options do |config_opts|
53+
config_opts.should_update? do
5454
# only retrieved once per instance
5555
@configuration_options == nil
5656
end
5757

58-
resource.to_update do
58+
config_opts.to_update do
5959
# This method assumes that the group and price item data was sent in
6060
# as part of the +network_hash+ used to initialize this object (as is done)
6161
# by the ProductPackage class. That class, in turn, gets its information
@@ -85,6 +85,10 @@ class ProductItemCategory < ModelBase
8585
end
8686
end
8787

88+
def service
89+
softlayer_client["SoftLayer_Product_Item_Category"].object_with_id(self.id)
90+
end
91+
8892
##
8993
# If the category has a single option (regardless of fees) this method will return
9094
# that option. If the category has more than one option, this method will

lib/softlayer/ProductPackage.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,18 @@ def datacenter_options
116116
availableLocations.collect { |location_data| location_data["location"]["name"] }
117117
end
118118

119+
##
120+
# Given a datacenter name that was returned by datacenter_options, use information
121+
# in the package to retrieve a location id.
119122
def location_id_for_datacenter_name(datacenter_name)
120123
location_data = availableLocations.find { |location_data| location_data["location"]["name"] == datacenter_name }
121124
location_data["locationId"]
122125
end
123126

127+
def service
128+
softlayer_client['Product_Package'].object_with_id(self.id)
129+
end
130+
124131
##
125132
# Requests a list (array) of ProductPackages whose key names match the
126133
# one passed in.

lib/softlayer/Server.rb

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,6 @@ def initialize(softlayer_client, network_hash)
8383
end
8484
end
8585

86-
##
87-
# Returns the service responsible for handling the given
88-
# server. In the base class (this one) the server is abstract
89-
# but subclasses implement this to return the appropriate service
90-
# from their client.
91-
#
92-
def service
93-
raise RuntimeError, "This method is an abstract method in the Server base class"
94-
end
95-
9686
##
9787
# Make an API request to SoftLayer and return the latest properties hash
9888
# for this object.
@@ -105,7 +95,7 @@ def softlayer_properties(object_mask = nil)
10595
my_service = my_service.object_mask(self.class.default_object_mask.to_sl_object_mask)
10696
end
10797

108-
my_service.object_with_id(self.id).getObject()
98+
my_service.getObject()
10999
end
110100

111101
##
@@ -118,7 +108,7 @@ def notes=(new_notes)
118108
"notes" => new_notes
119109
}
120110

121-
service.object_with_id(self.id).editObject(edit_template)
111+
self.service.editObject(edit_template)
122112
self.refresh_details()
123113
end
124114

@@ -127,7 +117,7 @@ def notes=(new_notes)
127117
#
128118
def user_metadata=(new_metadata)
129119
raise ArgumentError.new("Cannot set user metadata to nil") unless new_metadata
130-
service.object_with_id(self.id).setUserMetadata([new_metadata])
120+
self.service.setUserMetadata([new_metadata])
131121
self.refresh_details()
132122
end
133123

@@ -143,7 +133,7 @@ def set_hostname!(new_hostname)
143133
"hostname" => new_hostname
144134
}
145135

146-
service.object_with_id(self.id).editObject(edit_template)
136+
self.service.editObject(edit_template)
147137
self.refresh_details()
148138
end
149139

@@ -161,7 +151,7 @@ def set_domain!(new_domain)
161151
"domain" => new_domain
162152
}
163153

164-
service.object_with_id(self.id).editObject(edit_template)
154+
self.service.editObject(edit_template)
165155
self.refresh_details()
166156
end
167157

@@ -177,9 +167,9 @@ def set_domain!(new_domain)
177167
#
178168
def change_port_speed(new_speed, public = true)
179169
if public
180-
service.object_with_id(self.id).setPublicNetworkInterfaceSpeed(new_speed)
170+
self.service.setPublicNetworkInterfaceSpeed(new_speed)
181171
else
182-
service.object_with_id(self.id).setPrivateNetworkInterfaceSpeed(new_speed)
172+
self.service.setPrivateNetworkInterfaceSpeed(new_speed)
183173
end
184174

185175
self.refresh_details()
@@ -206,7 +196,7 @@ def reload_os!(token = '', provisioning_script_uri = nil, ssh_keys = nil)
206196
configuration['customProvisionScriptUri'] = provisioning_script_uri if provisioning_script_uri
207197
configuration['sshKeyIds'] = ssh_keys if ssh_keys
208198

209-
service.object_with_id(self.id).reloadOperatingSystem(token, configuration)
199+
self.service.reloadOperatingSystem(token, configuration)
210200
end
211201

212202
def to_s

lib/softlayer/Service.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ def initialize(service_name, options = {})
134134
def related_service_named(service_name)
135135
@client.service_named(service_name)
136136
end
137+
138+
# Added here so that the interface of this class matches that
139+
# of APIParameterFilter. In APIParameterFilter the target is
140+
# a service. In a service, the target is itself.
141+
def target
142+
return self
143+
end
137144

138145
# Use this as part of a method call chain to identify a particular
139146
# object as the target of the request. The parameter is the SoftLayer

0 commit comments

Comments
 (0)