schemata-staging 0.0.1.1 → 0.0.3.beta2

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.
@@ -0,0 +1,42 @@
1
+ require 'schemata/helpers/decamelize'
2
+
3
+ module Schemata
4
+ module ComponentBase
5
+
6
+ def message_types
7
+ self.constants.select { |x| x != :VERSION }
8
+ end
9
+
10
+ def component_name
11
+ self.name.split("::")[1]
12
+ end
13
+
14
+ def eigenclass
15
+ class << self; self; end
16
+ end
17
+
18
+ def register_mock_methods
19
+ message_types.each do |type|
20
+ message_type = self::const_get(type)
21
+ mock_method_name = "mock_#{Schemata::Helpers.decamelize(type.to_s)}"
22
+ eigenclass.send(:define_method, mock_method_name) do |*args|
23
+ version = args[0] || message_type.current_version
24
+ message_type::const_get("V#{version}").mock
25
+ end
26
+ end
27
+ end
28
+
29
+ def require_message_classes
30
+ path = "./lib/schemata/"
31
+ path << Schemata::Helpers.decamelize(component_name)
32
+ path << "/*.rb"
33
+ Dir.glob(path, &method(:require))
34
+ end
35
+
36
+ def self.extended(klass)
37
+ klass.require_message_classes
38
+ klass.register_mock_methods
39
+ end
40
+
41
+ end
42
+ end
@@ -1,12 +1,18 @@
1
1
  module Schemata
2
2
  class DecodeError < StandardError; end
3
3
  class EncodeError < StandardError; end
4
- class UpdateAttributeError < StandardError; end
4
+ class SchemaDefinitionError < StandardError; end
5
+
6
+ class UpdateAttributeError < StandardError
7
+ def initialize(key, message)
8
+ super("#{key}: #{message}")
9
+ end
10
+ end
11
+
5
12
  class IncompatibleVersionError < DecodeError
6
13
  def initialize(msg_version, component_version)
7
14
  super("min message version #{msg_version} too high for component ver\
8
15
  sion #{component_version}")
9
16
  end
10
17
  end
11
- class SchemaDefinitionError < StandardError; end
12
18
  end
@@ -1,5 +1,8 @@
1
1
  require 'yajl'
2
+ require 'membrane'
2
3
  require 'schemata/common/error'
4
+ require 'schemata/helpers/stringify'
5
+ require 'membrane'
3
6
 
4
7
  module Schemata
5
8
  module MessageBase
@@ -11,16 +14,28 @@ module Schemata
11
14
  @contents = {}
12
15
 
13
16
  data.each do |key, field_value|
17
+ key = Schemata::Helpers.stringify(key)
14
18
  field_schema = @schema.schemas[key]
15
19
  next unless field_schema
16
20
 
21
+ # TODO This call to stringify should be removed when cc/dea stop using
22
+ # Symbols.
23
+ #
24
+ # Currently, some fields (for example, 'states' in requests sent
25
+ # on dea.find.droplet), are are symbols, During Yajl decoding, however,
26
+ # they become strings. Thus, on the encoding side, Schemata should expect
27
+ # symbols, but on the decoding side, it should expect strings. To allow
28
+ # for this in the schema definition, Schemata stringifies all symbols during
29
+ # construction of Schemata objects.
30
+ field_value = Schemata::Helpers.stringify(field_value)
31
+
17
32
  begin
18
33
  field_schema.validate(field_value)
19
34
  rescue Membrane::SchemaValidationError => e
20
- raise Schemata::UpdateAttributeError.new(e.message)
35
+ raise Schemata::UpdateAttributeError.new(key, e.message)
21
36
  end
22
37
 
23
- @contents[key] = Schemata::HashCopyHelpers.deep_copy(field_value)
38
+ @contents[key] = Schemata::Helpers.deep_copy(field_value)
24
39
  end
25
40
  end
26
41
 
@@ -29,19 +44,24 @@ module Schemata
29
44
  vc_klass.const_set(:SCHEMA, schema)
30
45
  schema.schemas.each do |key, field_schema|
31
46
  vc_klass.send(:define_method, key) do
32
- if @contents[key]
33
- return Schemata::HashCopyHelpers.deep_copy(@contents[key])
47
+ unless @contents[key].nil?
48
+ return Schemata::Helpers.deep_copy(@contents[key])
34
49
  end
35
50
  nil
36
51
  end
37
52
 
53
+ # TODO This call to stringify should be removed when cc/dea stops using
54
+ # symbols. See comment above for a better description.
38
55
  vc_klass.send(:define_method, "#{key}=") do |field_value|
39
- begin
40
- field_schema.validate(field_value)
41
- rescue Membrane::SchemaValidationError => e
42
- raise Schemata::UpdateAttributeError.new(e.message)
56
+ field_value = Schemata::Helpers.stringify(field_value)
57
+ unless schema.optional_keys.include?(key) && field_value == nil
58
+ begin
59
+ field_schema.validate(field_value)
60
+ rescue Membrane::SchemaValidationError => e
61
+ raise Schemata::UpdateAttributeError.new(key, e.message)
62
+ end
43
63
  end
44
- @contents[key] = Schemata::HashCopyHelpers.deep_copy(field_value)
64
+ @contents[key] = Schemata::Helpers.deep_copy(field_value)
45
65
  field_value
46
66
  end
47
67
  end
@@ -49,7 +69,7 @@ module Schemata
49
69
  end
50
70
 
51
71
  def contents
52
- Schemata::HashCopyHelpers.deep_copy(@contents)
72
+ Schemata::Helpers.deep_copy(@contents)
53
73
  end
54
74
 
55
75
  def empty?
@@ -130,6 +150,11 @@ module Schemata
130
150
  Schemata::const_get(component)::const_get(msg_type)
131
151
  end
132
152
 
153
+ def component
154
+ _, component, msg_type, version = self.class.name.split("::")
155
+ Schemata::const_get(component)
156
+ end
157
+
133
158
  def self.included(klass)
134
159
  klass.extend(Schemata::ClassMethods)
135
160
  klass.extend(Dsl)
@@ -1,5 +1,6 @@
1
1
  require 'schemata/common/msgbase'
2
2
  require 'schemata/common/parsed_msg'
3
+ require 'schemata/helpers/decamelize'
3
4
 
4
5
  module Schemata
5
6
  module MessageTypeBase
@@ -19,10 +20,14 @@ module Schemata
19
20
 
20
21
  def decode(json_msg)
21
22
  begin
22
- parsed = Schemata::ParsedMessage.new(json_msg)
23
+ if versions.size == 2
24
+ parsed = Schemata::ParsedMessage.new(cleanup(json_msg))
25
+ else
26
+ parsed = Schemata::ParsedMessage.new(json_msg)
27
+ end
23
28
  rescue Schemata::DecodeError => e
24
- raise e unless versions.size == 1
25
- return decode_raw_payload(json_msg)
29
+ return decode_raw_payload(json_msg) if versions.size == 1
30
+ raise e
26
31
  end
27
32
  message_version = parsed.version
28
33
 
@@ -61,8 +66,32 @@ module Schemata
61
66
  end
62
67
  end
63
68
 
69
+ def component
70
+ _, component, message_type = self.name.split("::")
71
+ Schemata::const_get(component)
72
+ end
73
+
74
+ def component_name
75
+ self.name.split("::")[1]
76
+ end
77
+
78
+ def message_type_name
79
+ self.name.split("::")[2]
80
+ end
81
+
82
+ def require_message_versions
83
+ path = "./lib/schemata/"
84
+ path << Schemata::Helpers.decamelize(component_name)
85
+ path << "/"
86
+ path << Schemata::Helpers.decamelize(message_type_name)
87
+ path << "/*.rb"
88
+
89
+ Dir.glob(path, &method(:require))
90
+ end
91
+
64
92
  def self.extended(o)
65
93
  o.extend Dsl
94
+ o.require_message_versions
66
95
  end
67
96
 
68
97
  module Dsl
@@ -118,5 +147,18 @@ module Schemata
118
147
  end
119
148
  end
120
149
 
150
+ def cleanup(json)
151
+ msg_contents = Yajl::Parser.parse(json)
152
+ clean_msg = {}
153
+
154
+ msg_contents.keys.each do |key|
155
+ if key == "min_version" || key =~ /^V[0-9]+$/
156
+ clean_msg[key] = msg_contents[key]
157
+ end
158
+ end
159
+
160
+ Yajl::Encoder.encode(clean_msg)
161
+ end
162
+
121
163
  end
122
164
  end
@@ -37,7 +37,7 @@ module Schemata
37
37
  end
38
38
 
39
39
  def contents
40
- Schemata::HashCopyHelpers.deep_copy(@contents)
40
+ Schemata::Helpers.deep_copy(@contents)
41
41
  end
42
42
  end
43
43
  end
@@ -0,0 +1,23 @@
1
+ module Schemata
2
+ module Helpers
3
+ def self.decamelize(str)
4
+ words = []
5
+ curr_word = ""
6
+ 0.upto(str.length - 1) do |i|
7
+ ch = str[i]
8
+ if ch =~ /[A-Z]/
9
+ words.push(curr_word)
10
+ curr_word = ""
11
+ end
12
+ curr_word += ch
13
+ end
14
+ words.push(curr_word)
15
+ words.map! { |x| x.downcase }
16
+
17
+ # If the first letter is capitalized, then the first word here is empty
18
+ words.shift if words[0] == ""
19
+
20
+ words.join('_')
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,5 @@
1
1
  module Schemata
2
- module HashCopyHelpers
2
+ module Helpers
3
3
  class CopyError < StandardError; end
4
4
 
5
5
  def self.deep_copy(node)
@@ -0,0 +1,26 @@
1
+ module Schemata
2
+ module Helpers
3
+
4
+ def self.stringify(node)
5
+ case node
6
+ when String
7
+ return node
8
+ when Numeric, TrueClass, FalseClass
9
+ return node
10
+ when Hash
11
+ copy = {}
12
+ node.each { |k, v| copy[k.to_s] = stringify(v) }
13
+ return copy
14
+ when Array
15
+ return node.map { |v| stringify(v) }
16
+ when NilClass
17
+ return nil
18
+ when Symbol
19
+ return node.to_s
20
+ else
21
+ raise CopyError.new("Unexpected class: #{node.class}")
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -30,7 +30,7 @@ module Schemata
30
30
  },
31
31
  "options" => any,
32
32
  "plan" => String,
33
- "plan_option" => any,
33
+ "plan_option" => enum(String, NilClass),
34
34
  "type" => String,
35
35
  "version" => String,
36
36
  "vendor" => String,
@@ -0,0 +1,148 @@
1
+ require 'membrane'
2
+ require 'schemata/helpers/hash_copy'
3
+ require 'schemata/common/msgtypebase'
4
+
5
+ module Schemata
6
+ module Staging
7
+ module Message
8
+
9
+ version 2 do
10
+ define_schema do
11
+ {
12
+ "app_id" => Integer,
13
+ "download_uri" => String,
14
+ "upload_uri" => String,
15
+ "properties" => {
16
+ "services" => [ {
17
+ "label" => String,
18
+ "tags" => [String],
19
+ "name" => String,
20
+ "credentials" => {
21
+ # XXX Does this schema vary by service?
22
+ "hostname" => String,
23
+ "host" => String,
24
+ "port" => Integer,
25
+ "password" => String,
26
+ "name" => String,
27
+
28
+ },
29
+ "options" => any,
30
+ "plan" => String,
31
+ "plan_option" => enum(String, NilClass),
32
+ "type" => String,
33
+ "version" => String,
34
+ "vendor" => String,
35
+ } ],
36
+ "environment" => [String],
37
+ "framework" => String,
38
+ "framework_info" => {
39
+ "name" => String,
40
+ optional("detection") => [Hash],
41
+ optional("runtimes") => [Hash],
42
+ },
43
+ "meta" => {
44
+ optional("debug") => any,
45
+ optional("console") => any,
46
+ optional("command") => String,
47
+ },
48
+ "resources" => {
49
+ "memory" => Integer,
50
+ "disk" => Integer,
51
+ "fds" => Integer,
52
+ },
53
+ "runtime" => String,
54
+ "runtime_info" => Hash,
55
+ },
56
+ }
57
+ end
58
+
59
+ define_min_version 1
60
+
61
+ define_upvert do |old_data|
62
+ old_data
63
+ end
64
+
65
+ define_generate_old_fields do |msg_obj|
66
+ {}
67
+ end
68
+
69
+ define_mock_values do
70
+ {
71
+ "app_id" => 1,
72
+ "download_uri" => "http://foobar@172.0.0.0:100/download",
73
+ "upload_uri" => "http://foobar@172.0.0.0:100/upload",
74
+ "properties" => {
75
+ "services" => [ {
76
+ "label" => "mongodb-1.8",
77
+ "tags" => ["mongodb"],
78
+ "name" => "mongodb-685a",
79
+ "credentials" => {
80
+ "hostname" => "172.20.208.40",
81
+ "host" => "172.20.208.40",
82
+ "port" => 25001,
83
+ "password" => "a2ee7245-cdee-4a4a-b426-a8258ff1b39a",
84
+ "name" => "2eaa7336-2696-43cd-bb96-a614740b3511",
85
+ "username" => "aaf31edf-b2bc-4f97-a033-7021c2528ce8",
86
+ "db" => "db",
87
+ "url" => "mongodb://aaf31edf-b2bc-4f97-a033-7021c2528ce8:a2ee7245-cdee-4a4a-b426-a8258ff1b39a@172.20.208.40:25001/db",
88
+ },
89
+ "options" => {},
90
+ "plan" => "free",
91
+ "plan_option" => nil,
92
+ "type" => "document",
93
+ "version" => "1.8",
94
+ "vendor" => "mongodb",
95
+ } ],
96
+ "environment" => [],
97
+ "framework" => "sinatra",
98
+ "framework_info" => {
99
+ "name" => "sinatra",
100
+ "runtimes" => [
101
+ {"ruby18" => {
102
+ "default" => true}},
103
+ {"ruby19" => {
104
+ "default" => false}},
105
+ ],
106
+ "detection" => [
107
+ {"*.rb" => "\\s*require[\\s\\(]*['\"]sinatra(/base)?['\"]"},
108
+ {"config/environment.rb" => false}
109
+ ],
110
+ },
111
+ "meta" => {
112
+ "debug" => nil,
113
+ "console" => nil,
114
+ },
115
+ "resources" => {
116
+ "memory" => 64,
117
+ "disk" => 2048,
118
+ "fds" => 256,
119
+ },
120
+ "runtime" => "ruby19",
121
+ "runtime_info" => {
122
+ "description" => "Ruby 1.9",
123
+ "version" => "1.9.2p180",
124
+ "executable" => "/var/vcap/packages/dea_ruby19/bin/ruby",
125
+ "staging" => "/var/vcap/packages/ruby/bin/ruby stage",
126
+ "version_output" => "1.9.2",
127
+ "version_flag" => "-e 'puts RUBY_VERSION'",
128
+ "additional_checks" => "-e 'puts RUBY_PATCHLEVEL == 180'",
129
+ "environment" => {
130
+ "LD_LIBRARY_PATH" => "/var/vcap/packages/mysqlclient/lib/mysql:/var/vcap/packages/sqlite/lib:/var/vcap/packages/libpq/lib:/var/vcap/packages/imagemagick/lib:$LD_LIBRARY_PATH",
131
+ "BUNDLE_GEMFILE" => nil,
132
+ "PATH" => "/var/vcap/packages/imagemagick/bin:/var/vcap/packages/dea_transition/rubygems/1.9.1/bin:/var/vcap/packages/dea_ruby19/bin:/var/vcap/packages/dea_node08/bin:$PATH",
133
+ "GEM_PATH" => "/var/vcap/packages/dea_transition/rubygems/1.9.1:$GEM_PATH",
134
+ },
135
+ "status" => {
136
+ "name" => "current",
137
+ },
138
+ "series" => "ruby19",
139
+ "category" => "ruby",
140
+ "name" => "ruby19",
141
+ }
142
+ }
143
+ }
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -7,7 +7,3 @@ module Schemata
7
7
  end
8
8
  end
9
9
  end
10
-
11
- Dir[File.dirname(__FILE__) + '/message/*.rb'].each do |file|
12
- require file
13
- end
@@ -1,5 +1,5 @@
1
1
  module Schemata
2
2
  module Staging
3
- VERSION = "0.0.1.1"
3
+ VERSION = "0.0.3.beta2"
4
4
  end
5
5
  end
@@ -1,9 +1,7 @@
1
- require 'schemata/staging/message'
1
+ require 'schemata/common/componentbase'
2
2
 
3
3
  module Schemata
4
4
  module Staging
5
- def self.mock_message(version=Message.current_version)
6
- Message::const_get("V#{version}").mock
7
- end
5
+ extend Schemata::ComponentBase
8
6
  end
9
7
  end
@@ -0,0 +1,6 @@
1
+ require 'schemata/cloud_controller'
2
+ require 'spec_helper'
3
+
4
+ describe Schemata::CloudController do
5
+ it_behaves_like "a schemata component"
6
+ end
@@ -1,48 +1,49 @@
1
1
  require 'schemata/helpers/hash_copy'
2
+ require 'schemata/helpers/stringify'
2
3
 
3
- describe Schemata::HashCopyHelpers do
4
+ describe Schemata::Helpers do
4
5
  describe "#deep_copy" do
5
6
  it "should deep copy nil" do
6
- copy = Schemata::HashCopyHelpers.deep_copy(nil)
7
+ copy = Schemata::Helpers.deep_copy(nil)
7
8
  copy.should == nil
8
9
  end
9
10
 
10
11
  it "should deep copy a given string" do
11
12
  original = "foo"
12
- copy = Schemata::HashCopyHelpers.deep_copy(original)
13
+ copy = Schemata::Helpers.deep_copy(original)
13
14
  copy.should be_instance_of String
14
15
  copy.should == original
15
16
  copy.object_id.should_not == original.object_id
16
17
  end
17
18
 
18
19
  it "should deep copy a given boolean" do
19
- Schemata::HashCopyHelpers.deep_copy(true).
20
+ Schemata::Helpers.deep_copy(true).
20
21
  should be_an_instance_of TrueClass
21
- Schemata::HashCopyHelpers.deep_copy(false).
22
+ Schemata::Helpers.deep_copy(false).
22
23
  should be_an_instance_of FalseClass
23
24
  end
24
25
 
25
26
  it "should deep copy a given numeric type" do
26
27
  original = 0
27
- copy = Schemata::HashCopyHelpers.deep_copy(original)
28
+ copy = Schemata::Helpers.deep_copy(original)
28
29
  copy.should == original
29
30
  copy.should be_an_instance_of Fixnum
30
31
 
31
32
  # set original to be max fixnum + 1
32
- original = 2**(0.size * 8 -2)
33
- copy = Schemata::HashCopyHelpers.deep_copy(original)
33
+ original = 2**(0.size * 8 - 2)
34
+ copy = Schemata::Helpers.deep_copy(original)
34
35
  copy.should == original
35
36
  copy.should be_an_instance_of Bignum
36
37
 
37
38
  original = 0.0
38
- copy = Schemata::HashCopyHelpers.deep_copy(original)
39
+ copy = Schemata::Helpers.deep_copy(original)
39
40
  copy.should == original
40
41
  copy.should be_an_instance_of Float
41
42
  end
42
43
 
43
44
  it "should deep copy a given hash" do
44
45
  original = {"foo" => "bar"}
45
- copy = Schemata::HashCopyHelpers.deep_copy(original)
46
+ copy = Schemata::Helpers.deep_copy(original)
46
47
  copy.should be_instance_of Hash
47
48
  copy.should == original
48
49
 
@@ -52,7 +53,7 @@ describe Schemata::HashCopyHelpers do
52
53
 
53
54
  it "should deep copy a given array" do
54
55
  original = [1, 2, "hello"]
55
- copy = Schemata::HashCopyHelpers.deep_copy(original)
56
+ copy = Schemata::Helpers.deep_copy(original)
56
57
  copy.should be_instance_of Array
57
58
  copy.should == original
58
59
 
@@ -68,7 +69,7 @@ describe Schemata::HashCopyHelpers do
68
69
  "hello" => "goodbye",
69
70
  },
70
71
  }
71
- copy = Schemata::HashCopyHelpers.deep_copy(original)
72
+ copy = Schemata::Helpers.deep_copy(original)
72
73
  copy.should be_instance_of Hash
73
74
  copy.should == original
74
75
 
@@ -82,8 +83,33 @@ describe Schemata::HashCopyHelpers do
82
83
  it "should raise error for unknown type" do
83
84
  klass = Class.new
84
85
  expect do
85
- Schemata::HashCopyHelpers.deep_copy(klass.new)
86
+ Schemata::Helpers.deep_copy(klass.new)
86
87
  end.to raise_error(described_class::CopyError, /Unexpected class: /)
87
88
  end
88
89
  end
90
+
91
+ describe "#stringify" do
92
+ it "should stringify nil" do
93
+ str = Schemata::Helpers.stringify(nil)
94
+ str.should == nil
95
+ end
96
+
97
+ it "should stringify a string" do
98
+ original = "foo"
99
+ str = Schemata::Helpers.stringify(original)
100
+ str.should == "foo"
101
+ end
102
+
103
+ it "should stringify a symbol" do
104
+ original = :foo
105
+ str = Schemata::Helpers.stringify(original)
106
+ str.should == "foo"
107
+ end
108
+
109
+ it "should stringify a hash" do
110
+ original = { "foo" => :foo }
111
+ str = Schemata::Helpers.stringify(original)
112
+ str.should == { "foo" => "foo" }
113
+ end
114
+ end
89
115
  end