maestrano 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +43 -0
  5. data/LICENSE +21 -0
  6. data/README.md +4 -0
  7. data/Rakefile +32 -0
  8. data/bin/maestrano-console +9 -0
  9. data/lib/maestrano.rb +114 -0
  10. data/lib/maestrano/account/bill.rb +14 -0
  11. data/lib/maestrano/api/error/authentication_error.rb +8 -0
  12. data/lib/maestrano/api/error/base_error.rb +24 -0
  13. data/lib/maestrano/api/error/connection_error.rb +8 -0
  14. data/lib/maestrano/api/error/invalid_request_error.rb +14 -0
  15. data/lib/maestrano/api/list_object.rb +37 -0
  16. data/lib/maestrano/api/object.rb +187 -0
  17. data/lib/maestrano/api/operation/base.rb +216 -0
  18. data/lib/maestrano/api/operation/create.rb +18 -0
  19. data/lib/maestrano/api/operation/delete.rb +13 -0
  20. data/lib/maestrano/api/operation/list.rb +18 -0
  21. data/lib/maestrano/api/operation/update.rb +59 -0
  22. data/lib/maestrano/api/resource.rb +39 -0
  23. data/lib/maestrano/api/util.rb +121 -0
  24. data/lib/maestrano/saml/attribute_value.rb +15 -0
  25. data/lib/maestrano/saml/metadata.rb +64 -0
  26. data/lib/maestrano/saml/request.rb +93 -0
  27. data/lib/maestrano/saml/response.rb +201 -0
  28. data/lib/maestrano/saml/schemas/saml20assertion_schema.xsd +283 -0
  29. data/lib/maestrano/saml/schemas/saml20protocol_schema.xsd +302 -0
  30. data/lib/maestrano/saml/schemas/xenc_schema.xsd +146 -0
  31. data/lib/maestrano/saml/schemas/xmldsig_schema.xsd +318 -0
  32. data/lib/maestrano/saml/settings.rb +37 -0
  33. data/lib/maestrano/saml/validation_error.rb +7 -0
  34. data/lib/maestrano/sso.rb +81 -0
  35. data/lib/maestrano/sso/base_group.rb +31 -0
  36. data/lib/maestrano/sso/base_user.rb +75 -0
  37. data/lib/maestrano/sso/group.rb +24 -0
  38. data/lib/maestrano/sso/session.rb +63 -0
  39. data/lib/maestrano/sso/user.rb +34 -0
  40. data/lib/maestrano/version.rb +3 -0
  41. data/lib/maestrano/xml_security/signed_document.rb +170 -0
  42. data/maestrano.gemspec +32 -0
  43. data/test/helpers/api_helpers.rb +82 -0
  44. data/test/helpers/saml_helpers.rb +62 -0
  45. data/test/maestrano/account/bill_test.rb +48 -0
  46. data/test/maestrano/api/list_object_test.rb +20 -0
  47. data/test/maestrano/api/object_test.rb +28 -0
  48. data/test/maestrano/api/resource_test.rb +343 -0
  49. data/test/maestrano/api/util_test.rb +31 -0
  50. data/test/maestrano/maestrano_test.rb +49 -0
  51. data/test/maestrano/saml/request_test.rb +168 -0
  52. data/test/maestrano/saml/response_test.rb +290 -0
  53. data/test/maestrano/saml/settings_test.rb +51 -0
  54. data/test/maestrano/sso/base_group_test.rb +54 -0
  55. data/test/maestrano/sso/base_user_test.rb +114 -0
  56. data/test/maestrano/sso/group_test.rb +47 -0
  57. data/test/maestrano/sso/session_test.rb +108 -0
  58. data/test/maestrano/sso/user_test.rb +65 -0
  59. data/test/maestrano/sso_test.rb +81 -0
  60. data/test/maestrano/xml_security/signed_document.rb +163 -0
  61. data/test/support/saml/certificates/certificate1 +12 -0
  62. data/test/support/saml/certificates/r1_certificate2_base64 +1 -0
  63. data/test/support/saml/responses/adfs_response_sha1.xml +46 -0
  64. data/test/support/saml/responses/adfs_response_sha256.xml +46 -0
  65. data/test/support/saml/responses/adfs_response_sha384.xml +46 -0
  66. data/test/support/saml/responses/adfs_response_sha512.xml +46 -0
  67. data/test/support/saml/responses/no_signature_ns.xml +48 -0
  68. data/test/support/saml/responses/open_saml_response.xml +56 -0
  69. data/test/support/saml/responses/r1_response6.xml.base64 +1 -0
  70. data/test/support/saml/responses/response1.xml.base64 +1 -0
  71. data/test/support/saml/responses/response2.xml.base64 +79 -0
  72. data/test/support/saml/responses/response3.xml.base64 +66 -0
  73. data/test/support/saml/responses/response4.xml.base64 +93 -0
  74. data/test/support/saml/responses/response5.xml.base64 +102 -0
  75. data/test/support/saml/responses/response_with_ampersands.xml +139 -0
  76. data/test/support/saml/responses/response_with_ampersands.xml.base64 +93 -0
  77. data/test/support/saml/responses/response_with_multiple_attribute_values.xml +57 -0
  78. data/test/support/saml/responses/simple_saml_php.xml +71 -0
  79. data/test/support/saml/responses/starfield_response.xml.base64 +1 -0
  80. data/test/support/saml/responses/wrapped_response_2.xml.base64 +150 -0
  81. data/test/test_helper.rb +46 -0
  82. metadata +305 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 46d72cf16b89994faf979aeb8054a4e428fde7ab
4
+ data.tar.gz: 54d8aeb2955cbf58c1e06ccadb572060bdfaf86c
5
+ SHA512:
6
+ metadata.gz: d35c0dcf93d7d231154141bedab869c0f24cd38c3304e3cfc57c0265932f7dc8ebaeb33fa59dfee69126f490b067950aeafd6a6b9beda2fd08695b81a20c91cb
7
+ data.tar.gz: c649204b85ed1ebab729d783f6516d6c7940a83d3ef4e4c7abc208e1cf2d0fbab0fdab72fa847ce1fde289dd7c5792661d04a8a75d236e5cc4ff641d29ebf35d
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ maestrano (0.1.0)
5
+ json (~> 1.8)
6
+ mime-types (~> 1.25)
7
+ nokogiri (>= 1.5.0)
8
+ rest-client (~> 1.4)
9
+ uuid (~> 2.3)
10
+
11
+ GEM
12
+ remote: http://rubygems.org/
13
+ specs:
14
+ json (1.8.1)
15
+ macaddr (1.7.1)
16
+ systemu (~> 2.6.2)
17
+ metaclass (0.0.4)
18
+ mime-types (1.25.1)
19
+ mini_portile (0.6.0)
20
+ mocha (0.13.3)
21
+ metaclass (~> 0.0.1)
22
+ nokogiri (1.6.2.1)
23
+ mini_portile (= 0.6.0)
24
+ rake (10.3.2)
25
+ rest-client (1.6.7)
26
+ mime-types (>= 1.16)
27
+ shoulda (2.11.3)
28
+ systemu (2.6.4)
29
+ test-unit (2.5.5)
30
+ timecop (0.6.0)
31
+ uuid (2.3.7)
32
+ macaddr (~> 1.0)
33
+
34
+ PLATFORMS
35
+ ruby
36
+
37
+ DEPENDENCIES
38
+ maestrano!
39
+ mocha (~> 0.13.2)
40
+ rake
41
+ shoulda (~> 2.11.0)
42
+ test-unit
43
+ timecop (<= 0.6.0)
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2014 Maestrano Pty. Ltd.
2
+
3
+ The MIT License (MIT)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ maestrano-ruby
2
+ ==============
3
+
4
+ Maestrano Ruby Bindings
@@ -0,0 +1,32 @@
1
+ task :default => [:test]
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/*_test.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ Rake::TestTask.new(:saml) do |test|
11
+ test.libs << 'lib' << 'test'
12
+ test.pattern = 'test/maestrano/saml/**/*_test.rb'
13
+ test.verbose = true
14
+ end
15
+
16
+ Rake::TestTask.new(:api) do |test|
17
+ test.libs << 'lib' << 'test'
18
+ test.pattern = 'test/maestrano/api/**/*_test.rb'
19
+ test.verbose = true
20
+ end
21
+
22
+ Rake::TestTask.new(:sso) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/maestrano/sso{_test.rb,/**/*_test.rb}'
25
+ test.verbose = true
26
+ end
27
+
28
+ Rake::TestTask.new(:xmlsec) do |test|
29
+ test.libs << 'lib' << 'test'
30
+ test.pattern = 'test/maestrano/xml_security/**/*_test.rb'
31
+ test.verbose = true
32
+ end
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3
+
4
+ libs = " -r irb/completion"
5
+ libs << " -I #{File.dirname(__FILE__) + '/../lib'}"
6
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/maestrano'}"
7
+ puts ">> Loading Maestrano console"
8
+ puts ">>"
9
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,114 @@
1
+ # libs
2
+ require 'rest_client'
3
+ require 'json'
4
+
5
+ # Version
6
+ require 'maestrano/version'
7
+
8
+ # XMLSecurity
9
+ require 'maestrano/xml_security/signed_document'
10
+
11
+ # SAML
12
+ require 'maestrano/saml/request'
13
+ require 'maestrano/saml/attribute_value'
14
+ require 'maestrano/saml/response'
15
+ require 'maestrano/saml/settings'
16
+ require 'maestrano/saml/validation_error'
17
+ require 'maestrano/saml/metadata'
18
+
19
+ # SSO
20
+ require 'maestrano/sso'
21
+ require 'maestrano/sso/base_user'
22
+ require 'maestrano/sso/base_group'
23
+ require 'maestrano/sso/session'
24
+ require 'maestrano/sso/user'
25
+ require 'maestrano/sso/group'
26
+
27
+ # API Errors
28
+ require 'maestrano/api/error/base_error'
29
+ require 'maestrano/api/error/authentication_error'
30
+ require 'maestrano/api/error/connection_error'
31
+ require 'maestrano/api/error/invalid_request_error'
32
+
33
+ # API Operations
34
+ require 'maestrano/api/operation/base'
35
+ require 'maestrano/api/operation/create'
36
+ require 'maestrano/api/operation/delete'
37
+ require 'maestrano/api/operation/list'
38
+ require 'maestrano/api/operation/update'
39
+
40
+ # API
41
+ require 'maestrano/api/util'
42
+ require 'maestrano/api/object'
43
+ require 'maestrano/api/list_object'
44
+ require 'maestrano/api/resource'
45
+
46
+ # API - Account Entities
47
+ require 'maestrano/account/bill'
48
+
49
+ module Maestrano
50
+
51
+ class << self
52
+ attr_accessor :config
53
+ end
54
+
55
+ # Maestrano Configuration block
56
+ def self.configure
57
+ self.config ||= Configuration.new
58
+ yield(config)
59
+ end
60
+
61
+ # Get configuration parameter value
62
+ # E.g:
63
+ # Maestrano.param('api_key')
64
+ # Maestrano.param(:api_key)
65
+ def self.param(parameter)
66
+ self.config.param(parameter)
67
+ end
68
+
69
+ class Configuration
70
+ attr_accessor :environment, :api_key, :sso_enabled,
71
+ :app_host, :sso_app_init_path, :sso_app_consume_path, :user_creation_mode,
72
+ :verify_ssl_certs, :api_version
73
+
74
+ def initialize
75
+ @environment = 'test'
76
+ @api_key = nil
77
+ @sso_enabled = true
78
+ @app_host = 'http://localhost:3000'
79
+ @sso_app_init_path = '/maestrano/auth/saml/init'
80
+ @sso_app_consume_path = '/maestrano/auth/saml/consume'
81
+ @user_creation_mode = 'virtual'
82
+ @verify_ssl_certs = false
83
+ @api_version = nil
84
+ end
85
+
86
+ # Get configuration parameter value
87
+ def param(parameter)
88
+ if self.respond_to?(parameter)
89
+ self.send(parameter)
90
+ elsif EVT_CONFIG[@environment.to_sym].has_key?(parameter.to_sym)
91
+ EVT_CONFIG[@environment.to_sym][parameter.to_sym]
92
+ else
93
+ raise ArgumentError, "No such configuration parameter: '#{parameter}'"
94
+ end
95
+ end
96
+
97
+ EVT_CONFIG = {
98
+ test: {
99
+ api_host: 'http://api-sandbox.maestrano.io',
100
+ api_base: '/api/v1/',
101
+ sso_name_id_format: Maestrano::Saml::Settings::NAMEID_PERSISTENT,
102
+ sso_x509_fingerprint: '01:06:15:89:25:7d:78:12:28:a6:69:c7:de:63:ed:74:21:f9:f5:36',
103
+ sso_x509_certificate: "-----BEGIN CERTIFICATE-----\nMIIDezCCAuSgAwIBAgIJAOehBr+YIrhjMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\nVQQGEwJBVTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxGjAYBgNVBAoT\nEU1hZXN0cmFubyBQdHkgTHRkMRYwFAYDVQQDEw1tYWVzdHJhbm8uY29tMSQwIgYJ\nKoZIhvcNAQkBFhVzdXBwb3J0QG1hZXN0cmFuby5jb20wHhcNMTQwMTA0MDUyMjM5\nWhcNMzMxMjMwMDUyMjM5WjCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEP\nMA0GA1UEBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQG\nA1UEAxMNbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVz\ndHJhbm8uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVkIqo5t5Paflu\nP2zbSbzxn29n6HxKnTcsubycLBEs0jkTkdG7seF1LPqnXl8jFM9NGPiBFkiaR15I\n5w482IW6mC7s8T2CbZEL3qqQEAzztEPnxQg0twswyIZWNyuHYzf9fw0AnohBhGu2\n28EZWaezzT2F333FOVGSsTn1+u6tFwIDAQABo4HuMIHrMB0GA1UdDgQWBBSvrNxo\neHDm9nhKnkdpe0lZjYD1GzCBuwYDVR0jBIGzMIGwgBSvrNxoeHDm9nhKnkdpe0lZ\njYD1G6GBjKSBiTCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE\nBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQGA1UEAxMN\nbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVzdHJhbm8u\nY29tggkA56EGv5giuGMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCc\nMPgV0CpumKRMulOeZwdpnyLQI/NTr3VVHhDDxxCzcB0zlZ2xyDACGnIG2cQJJxfc\n2GcsFnb0BMw48K6TEhAaV92Q7bt1/TYRvprvhxUNMX2N8PHaYELFG2nWfQ4vqxES\nRkjkjqy+H7vir/MOF3rlFjiv5twAbDKYHXDT7v1YCg==\n-----END CERTIFICATE-----"
104
+ },
105
+ production: {
106
+ api_host: 'https://maestrano.com',
107
+ api_base: '/api/v1/',
108
+ sso_name_id_format: Maestrano::Saml::Settings::NAMEID_PERSISTENT,
109
+ sso_x509_fingerprint: '2f:57:71:e4:40:19:57:37:a6:2c:f0:c5:82:52:2f:2e:41:b7:9d:7e',
110
+ sso_x509_certificate: "-----BEGIN CERTIFICATE-----\nMIIDezCCAuSgAwIBAgIJAPFpcH2rW0pyMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\nVQQGEwJBVTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxGjAYBgNVBAoT\nEU1hZXN0cmFubyBQdHkgTHRkMRYwFAYDVQQDEw1tYWVzdHJhbm8uY29tMSQwIgYJ\nKoZIhvcNAQkBFhVzdXBwb3J0QG1hZXN0cmFuby5jb20wHhcNMTQwMTA0MDUyNDEw\nWhcNMzMxMjMwMDUyNDEwWjCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEP\nMA0GA1UEBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQG\nA1UEAxMNbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVz\ndHJhbm8uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3feNNn2xfEz5/\nQvkBIu2keh9NNhobpre8U4r1qC7h7OeInTldmxGL4cLHw4ZAqKbJVrlFWqNevM5V\nZBkDe4mjuVkK6rYK1ZK7eVk59BicRksVKRmdhXbANk/C5sESUsQv1wLZyrF5Iq8m\na9Oy4oYrIsEF2uHzCouTKM5n+O4DkwIDAQABo4HuMIHrMB0GA1UdDgQWBBSd/X0L\n/Pq+ZkHvItMtLnxMCAMdhjCBuwYDVR0jBIGzMIGwgBSd/X0L/Pq+ZkHvItMtLnxM\nCAMdhqGBjKSBiTCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE\nBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQGA1UEAxMN\nbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVzdHJhbm8u\nY29tggkA8WlwfatbSnIwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQDE\nhe/18oRh8EqIhOl0bPk6BG49AkjhZZezrRJkCFp4dZxaBjwZTddwo8O5KHwkFGdy\nyLiPV326dtvXoKa9RFJvoJiSTQLEn5mO1NzWYnBMLtrDWojOe6Ltvn3x0HVo/iHh\nJShjAn6ZYX43Tjl1YXDd1H9O+7/VgEWAQQ32v8p5lA==\n-----END CERTIFICATE-----"
111
+ }
112
+ }
113
+ end
114
+ end
@@ -0,0 +1,14 @@
1
+ module Maestrano
2
+ module Account
3
+ class Bill < Maestrano::API::Resource
4
+ include Maestrano::API::Operation::List
5
+ include Maestrano::API::Operation::Create
6
+
7
+ def cancel
8
+ response, api_key = Maestrano::API::Operation::Base.request(:delete, url, @api_key)
9
+ refresh_from(response, api_key)
10
+ self
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module Maestrano
2
+ module API
3
+ module Error
4
+ class AuthenticationError < Maestrano::API::Error::BaseError
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ module Maestrano
2
+ module API
3
+ module Error
4
+ class BaseError < StandardError
5
+ attr_reader :message
6
+ attr_reader :http_status
7
+ attr_reader :http_body
8
+ attr_reader :json_body
9
+
10
+ def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
11
+ @message = message
12
+ @http_status = http_status
13
+ @http_body = http_body
14
+ @json_body = json_body
15
+ end
16
+
17
+ def to_s
18
+ status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
19
+ "#{status_string}#{@message}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ module Maestrano
2
+ module API
3
+ module Error
4
+ class ConnectionError < Maestrano::API::Error::BaseError
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ module Maestrano
2
+ module API
3
+ module Error
4
+ class InvalidRequestError < Maestrano::API::Error::BaseError
5
+ attr_accessor :param
6
+
7
+ def initialize(message, param, http_status=nil, http_body=nil, json_body=nil)
8
+ super(message, http_status, http_body, json_body)
9
+ @param = param
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ module Maestrano
2
+ module API
3
+ class ListObject < Maestrano::API::Object
4
+
5
+ def [](k)
6
+ case k
7
+ when String, Symbol
8
+ super
9
+ else
10
+ raise ArgumentError.new("You tried to access the #{k.inspect} index, but ListObject types only support String keys. (HINT: List calls return an object with a 'data' (which is the data array). You likely want to call #data[#{k.inspect}])")
11
+ end
12
+ end
13
+
14
+ def each(&blk)
15
+ self.data.each(&blk)
16
+ end
17
+
18
+ def retrieve(id, api_key=nil)
19
+ api_key ||= @api_key
20
+ response, api_key = Maestrano::API::Operation::Base.request(:get,"#{url}/#{CGI.escape(id)}", api_key)
21
+ Util.convert_to_maestrano_object(response, api_key)
22
+ end
23
+
24
+ def create(params={}, api_key=nil)
25
+ api_key ||= @api_key
26
+ response, api_key = Maestrano::API::Operation::Base.request(:post, url, api_key, params)
27
+ Util.convert_to_maestrano_object(response, api_key)
28
+ end
29
+
30
+ def all(params={}, api_key=nil)
31
+ api_key ||= @api_key
32
+ response, api_key = Maestrano::API::Operation::Base.request(:get, url, api_key, params)
33
+ Util.convert_to_maestrano_object(response, api_key)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,187 @@
1
+ module Maestrano
2
+ module API
3
+ class Object
4
+ include Enumerable
5
+
6
+ attr_accessor :api_key
7
+ @@permanent_attributes = Set.new([:api_key, :id])
8
+
9
+ # The default :id method is deprecated and isn't useful to us
10
+ if method_defined?(:id)
11
+ undef :id
12
+ end
13
+
14
+ def initialize(id=nil, api_key=nil)
15
+ # parameter overloading!
16
+ if id.kind_of?(Hash)
17
+ @retrieve_options = id.dup
18
+ @retrieve_options.delete(:id)
19
+ id = id[:id]
20
+ else
21
+ @retrieve_options = {}
22
+ end
23
+
24
+ @api_key = api_key
25
+ @values = {}
26
+ # This really belongs in API::Resource, but not putting it there allows us
27
+ # to have a unified inspect method
28
+ @unsaved_values = Set.new
29
+ @transient_values = Set.new
30
+ @values[:id] = id if id
31
+ end
32
+
33
+ def self.construct_from(values, api_key=nil)
34
+ obj = self.new(values[:id], api_key)
35
+ obj.refresh_from(values, api_key)
36
+ obj
37
+ end
38
+
39
+ def to_s(*args)
40
+ JSON.pretty_generate(@values)
41
+ end
42
+
43
+ def inspect()
44
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
45
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values)
46
+ end
47
+
48
+ def refresh_from(values, api_key, partial=false)
49
+ @api_key = api_key
50
+
51
+ @previous_metadata = values[:metadata]
52
+ removed = partial ? Set.new : Set.new(@values.keys - values.keys)
53
+ added = Set.new(values.keys - @values.keys)
54
+ # Wipe old state before setting new.
55
+
56
+ instance_eval do
57
+ remove_accessors(removed)
58
+ add_accessors(added)
59
+ end
60
+ removed.each do |k|
61
+ @values.delete(k)
62
+ @transient_values.add(k)
63
+ @unsaved_values.delete(k)
64
+ end
65
+ values.each do |k, v|
66
+ @values[k] = Util.convert_to_maestrano_object(v, api_key)
67
+ @transient_values.delete(k)
68
+ @unsaved_values.delete(k)
69
+ end
70
+ end
71
+
72
+ def [](k)
73
+ @values[k.to_sym]
74
+ end
75
+
76
+ def []=(k, v)
77
+ send(:"#{k}=", v)
78
+ end
79
+
80
+ def keys
81
+ @values.keys
82
+ end
83
+
84
+ def values
85
+ @values.values
86
+ end
87
+
88
+ def to_json(*a)
89
+ JSON.generate(@values)
90
+ end
91
+
92
+ def as_json(*a)
93
+ @values.as_json(*a)
94
+ end
95
+
96
+ def to_hash
97
+ @values
98
+ end
99
+
100
+ def each(&blk)
101
+ @values.each(&blk)
102
+ end
103
+
104
+ def _dump(level)
105
+ Marshal.dump([@values, @api_key])
106
+ end
107
+
108
+ def self._load(args)
109
+ values, api_key = Marshal.load(args)
110
+ construct_from(values, api_key)
111
+ end
112
+
113
+ if RUBY_VERSION < '1.9.2'
114
+ def respond_to?(symbol)
115
+ @values.has_key?(symbol) || super
116
+ end
117
+ end
118
+
119
+ protected
120
+
121
+ def metaclass
122
+ class << self; self; end
123
+ end
124
+
125
+ def remove_accessors(keys)
126
+ metaclass.instance_eval do
127
+ keys.each do |k|
128
+ next if @@permanent_attributes.include?(k)
129
+ k_eq = :"#{k}="
130
+ remove_method(k) if method_defined?(k)
131
+ remove_method(k_eq) if method_defined?(k_eq)
132
+ end
133
+ end
134
+ end
135
+
136
+ def add_accessors(keys)
137
+ metaclass.instance_eval do
138
+ keys.each do |k|
139
+ next if @@permanent_attributes.include?(k)
140
+ k_eq = :"#{k}="
141
+ define_method(k) { @values[k] }
142
+ define_method(k_eq) do |v|
143
+ if v == ""
144
+ raise ArgumentError.new(
145
+ "You cannot set #{k} to an empty string." +
146
+ "We interpret empty strings as nil in requests." +
147
+ "You may set #{self}.#{k} = nil to delete the property.")
148
+ end
149
+ @values[k] = v
150
+ @unsaved_values.add(k)
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ def method_missing(name, *args)
157
+ # TODO: only allow setting in updateable classes.
158
+ if name.to_s.end_with?('=')
159
+ attr = name.to_s[0...-1].to_sym
160
+ add_accessors([attr])
161
+ begin
162
+ mth = method(name)
163
+ rescue NameError
164
+ raise NoMethodError.new("Cannot set #{attr} on this object. HINT: you can't set: #{@@permanent_attributes.to_a.join(', ')}")
165
+ end
166
+ return mth.call(args[0])
167
+ else
168
+ return @values[name] if @values.has_key?(name)
169
+ end
170
+
171
+ begin
172
+ super
173
+ rescue NoMethodError => e
174
+ if @transient_values.include?(name)
175
+ raise NoMethodError.new(e.message + ". HINT: The '#{name}' attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Maestrano's API, probably as a result of a save(). The attributes currently available on this object are: #{@values.keys.join(', ')}")
176
+ else
177
+ raise
178
+ end
179
+ end
180
+ end
181
+
182
+ def respond_to_missing?(symbol, include_private = false)
183
+ @values.has_key?(symbol) || super
184
+ end
185
+ end
186
+ end
187
+ end