schemata-staging 0.0.1.1 → 0.0.3.beta2

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