maestrano 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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