humidifier 0.7.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (198) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/humidifier/humidifier.c +1 -16
  4. data/ext/humidifier/humidifier.h +0 -1
  5. data/lib/humidifier/configuration.rb +2 -0
  6. data/lib/humidifier/fn.rb +2 -1
  7. data/lib/humidifier/humidifier.bundle +0 -0
  8. data/lib/humidifier/loader.rb +42 -19
  9. data/lib/humidifier/props/base.rb +49 -0
  10. data/lib/humidifier/props/boolean_prop.rb +21 -0
  11. data/lib/humidifier/props/double_prop.rb +17 -0
  12. data/lib/humidifier/props/integer_prop.rb +17 -0
  13. data/lib/humidifier/props/json_prop.rb +11 -0
  14. data/lib/humidifier/props/list_prop.rb +20 -0
  15. data/lib/humidifier/props/map_prop.rb +20 -0
  16. data/lib/humidifier/props/string_prop.rb +17 -0
  17. data/lib/humidifier/props/structure_prop.rb +26 -0
  18. data/lib/humidifier/props/timestamp_prop.rb +17 -0
  19. data/lib/humidifier/props.rb +13 -126
  20. data/lib/humidifier/sdk_payload.rb +3 -2
  21. data/lib/humidifier/serializer.rb +4 -2
  22. data/lib/humidifier/version.rb +1 -1
  23. data/lib/humidifier.rb +5 -0
  24. data/specs/CloudFormationResourceSpecification.json +14332 -0
  25. data/specs/fixer.rb +360 -0
  26. metadata +14 -174
  27. data/specs/ApiGateway-Account.cf +0 -6
  28. data/specs/ApiGateway-ApiKey.cf +0 -9
  29. data/specs/ApiGateway-Authorizer.cf +0 -14
  30. data/specs/ApiGateway-BasePathMapping.cf +0 -9
  31. data/specs/ApiGateway-ClientCertificate.cf +0 -6
  32. data/specs/ApiGateway-Deployment.cf +0 -9
  33. data/specs/ApiGateway-Method.cf +0 -15
  34. data/specs/ApiGateway-Model.cf +0 -10
  35. data/specs/ApiGateway-Resource.cf +0 -8
  36. data/specs/ApiGateway-RestApi.cf +0 -12
  37. data/specs/ApiGateway-Stage.cf +0 -14
  38. data/specs/ApiGateway-UsagePlan.cf +0 -10
  39. data/specs/ApplicationAutoScaling-ScalableTarget.cf +0 -11
  40. data/specs/ApplicationAutoScaling-ScalingPolicy.cf +0 -12
  41. data/specs/AutoScaling-AutoScalingGroup.cf +0 -22
  42. data/specs/AutoScaling-LaunchConfiguration.cf +0 -22
  43. data/specs/AutoScaling-LifecycleHook.cf +0 -12
  44. data/specs/AutoScaling-ScalingPolicy.cf +0 -14
  45. data/specs/AutoScaling-ScheduledAction.cf +0 -12
  46. data/specs/CertificateManager-Certificate.cf +0 -9
  47. data/specs/CloudFormation-Authentication.cf +0 -17
  48. data/specs/CloudFormation-CustomResource.cf +0 -8
  49. data/specs/CloudFormation-Interface.cf +0 -9
  50. data/specs/CloudFormation-Stack.cf +0 -10
  51. data/specs/CloudFormation-WaitCondition.cf +0 -8
  52. data/specs/CloudFormation-WaitConditionHandle.cf +0 -5
  53. data/specs/CloudFront-Distribution.cf +0 -6
  54. data/specs/CloudTrail-Trail.cf +0 -16
  55. data/specs/CloudWatch-Alarm.cf +0 -20
  56. data/specs/CodeCommit-Repository.cf +0 -8
  57. data/specs/CodeDeploy-Application.cf +0 -6
  58. data/specs/CodeDeploy-DeploymentConfig.cf +0 -7
  59. data/specs/CodeDeploy-DeploymentGroup.cf +0 -13
  60. data/specs/CodePipeline-CustomActionType.cf +0 -12
  61. data/specs/CodePipeline-Pipeline.cf +0 -11
  62. data/specs/Config-ConfigRule.cf +0 -11
  63. data/specs/Config-ConfigurationRecorder.cf +0 -8
  64. data/specs/Config-DeliveryChannel.cf +0 -10
  65. data/specs/DataPipeline-Pipeline.cf +0 -12
  66. data/specs/DirectoryService-MicrosoftAD.cf +0 -11
  67. data/specs/DirectoryService-SimpleAD.cf +0 -13
  68. data/specs/DynamoDB-Table.cf +0 -12
  69. data/specs/EC2-CustomerGateway.cf +0 -9
  70. data/specs/EC2-DHCPOptions.cf +0 -11
  71. data/specs/EC2-EIP.cf +0 -7
  72. data/specs/EC2-EIPAssociation.cf +0 -10
  73. data/specs/EC2-FlowLog.cf +0 -10
  74. data/specs/EC2-Host.cf +0 -8
  75. data/specs/EC2-Instance.cf +0 -32
  76. data/specs/EC2-InternetGateway.cf +0 -6
  77. data/specs/EC2-NatGateway.cf +0 -7
  78. data/specs/EC2-NetworkAcl.cf +0 -7
  79. data/specs/EC2-NetworkAclEntry.cf +0 -13
  80. data/specs/EC2-NetworkInterface.cf +0 -13
  81. data/specs/EC2-NetworkInterfaceAttachment.cf +0 -9
  82. data/specs/EC2-PlacementGroup.cf +0 -6
  83. data/specs/EC2-Route.cf +0 -12
  84. data/specs/EC2-RouteTable.cf +0 -7
  85. data/specs/EC2-SecurityGroup.cf +0 -10
  86. data/specs/EC2-SecurityGroupEgress.cf +0 -12
  87. data/specs/EC2-SecurityGroupIngress.cf +0 -14
  88. data/specs/EC2-SpotFleet.cf +0 -6
  89. data/specs/EC2-Subnet.cf +0 -10
  90. data/specs/EC2-SubnetNetworkAclAssociation.cf +0 -5
  91. data/specs/EC2-SubnetRouteTableAssociation.cf +0 -7
  92. data/specs/EC2-VPC.cf +0 -10
  93. data/specs/EC2-VPCDHCPOptionsAssociation.cf +0 -7
  94. data/specs/EC2-VPCEndpoint.cf +0 -9
  95. data/specs/EC2-VPCGatewayAttachment.cf +0 -8
  96. data/specs/EC2-VPCPeeringConnection.cf +0 -8
  97. data/specs/EC2-VPNConnection.cf +0 -10
  98. data/specs/EC2-VPNConnectionRoute.cf +0 -7
  99. data/specs/EC2-VPNGateway.cf +0 -7
  100. data/specs/EC2-VPNGatewayRoutePropagation.cf +0 -7
  101. data/specs/EC2-Volume.cf +0 -14
  102. data/specs/EC2-VolumeAttachment.cf +0 -8
  103. data/specs/ECR-Repository.cf +0 -7
  104. data/specs/ECS-Cluster.cf +0 -6
  105. data/specs/ECS-Service.cf +0 -11
  106. data/specs/ECS-TaskDefinition.cf +0 -9
  107. data/specs/EFS-FileSystem.cf +0 -7
  108. data/specs/EFS-MountTarget.cf +0 -9
  109. data/specs/EMR-Cluster.cf +0 -17
  110. data/specs/EMR-InstanceGroupConfig.cf +0 -14
  111. data/specs/EMR-Step.cf +0 -9
  112. data/specs/ElastiCache-CacheCluster.cf +0 -27
  113. data/specs/ElastiCache-ParameterGroup.cf +0 -8
  114. data/specs/ElastiCache-ReplicationGroup.cf +0 -31
  115. data/specs/ElastiCache-SecurityGroup.cf +0 -7
  116. data/specs/ElastiCache-SecurityGroupIngress.cf +0 -9
  117. data/specs/ElastiCache-SubnetGroup.cf +0 -8
  118. data/specs/ElasticBeanstalk-Application.cf +0 -7
  119. data/specs/ElasticBeanstalk-ApplicationVersion.cf +0 -8
  120. data/specs/ElasticBeanstalk-ConfigurationTemplate.cf +0 -11
  121. data/specs/ElasticBeanstalk-Environment.cf +0 -15
  122. data/specs/ElasticLoadBalancing-LoadBalancer.cf +0 -21
  123. data/specs/ElasticLoadBalancingV2-Listener.cf +0 -11
  124. data/specs/ElasticLoadBalancingV2-ListenerRule.cf +0 -9
  125. data/specs/ElasticLoadBalancingV2-LoadBalancer.cf +0 -11
  126. data/specs/ElasticLoadBalancingV2-TargetGroup.cf +0 -20
  127. data/specs/Elasticsearch-Domain.cf +0 -13
  128. data/specs/Events-Rule.cf +0 -12
  129. data/specs/GameLift-Alias.cf +0 -8
  130. data/specs/GameLift-Build.cf +0 -8
  131. data/specs/GameLift-Fleet.cf +0 -16
  132. data/specs/IAM-AccessKey.cf +0 -8
  133. data/specs/IAM-Group.cf +0 -9
  134. data/specs/IAM-InstanceProfile.cf +0 -7
  135. data/specs/IAM-ManagedPolicy.cf +0 -11
  136. data/specs/IAM-Policy.cf +0 -10
  137. data/specs/IAM-Role.cf +0 -10
  138. data/specs/IAM-User.cf +0 -11
  139. data/specs/IAM-UserToGroupAddition.cf +0 -7
  140. data/specs/IoT-Certificate.cf +0 -7
  141. data/specs/IoT-Policy.cf +0 -7
  142. data/specs/IoT-PolicyPrincipalAttachment.cf +0 -7
  143. data/specs/IoT-Thing.cf +0 -7
  144. data/specs/IoT-ThingPrincipalAttachment.cf +0 -7
  145. data/specs/IoT-TopicRule.cf +0 -7
  146. data/specs/KMS-Alias.cf +0 -7
  147. data/specs/KMS-Key.cf +0 -9
  148. data/specs/Kinesis-Stream.cf +0 -8
  149. data/specs/KinesisFirehose-DeliveryStream.cf +0 -9
  150. data/specs/Lambda-Alias.cf +0 -9
  151. data/specs/Lambda-EventSourceMapping.cf +0 -10
  152. data/specs/Lambda-Function.cf +0 -14
  153. data/specs/Lambda-Permission.cf +0 -10
  154. data/specs/Lambda-Version.cf +0 -8
  155. data/specs/Logs-Destination.cf +0 -9
  156. data/specs/Logs-LogGroup.cf +0 -7
  157. data/specs/Logs-LogStream.cf +0 -7
  158. data/specs/Logs-MetricFilter.cf +0 -8
  159. data/specs/Logs-SubscriptionFilter.cf +0 -9
  160. data/specs/OpsWorks-App.cf +0 -16
  161. data/specs/OpsWorks-ElasticLoadBalancerAttachment.cf +0 -7
  162. data/specs/OpsWorks-Instance.cf +0 -19
  163. data/specs/OpsWorks-Layer.cf +0 -21
  164. data/specs/OpsWorks-Stack.cf +0 -23
  165. data/specs/RDS-DBCluster.cf +0 -23
  166. data/specs/RDS-DBClusterParameterGroup.cf +0 -9
  167. data/specs/RDS-DBInstance.cf +0 -42
  168. data/specs/RDS-DBParameterGroup.cf +0 -9
  169. data/specs/RDS-DBSecurityGroup.cf +0 -10
  170. data/specs/RDS-DBSecurityGroupIngress.cf +0 -9
  171. data/specs/RDS-DBSubnetGroup.cf +0 -8
  172. data/specs/RDS-EventSubscription.cf +0 -10
  173. data/specs/RDS-OptionGroup.cf +0 -10
  174. data/specs/Redshift-Cluster.cf +0 -30
  175. data/specs/Redshift-ClusterParameterGroup.cf +0 -8
  176. data/specs/Redshift-ClusterSecurityGroup.cf +0 -6
  177. data/specs/Redshift-ClusterSecurityGroupIngress.cf +0 -9
  178. data/specs/Redshift-ClusterSubnetGroup.cf +0 -7
  179. data/specs/Route53-HealthCheck.cf +0 -7
  180. data/specs/Route53-HostedZone.cf +0 -9
  181. data/specs/Route53-RecordSet.cf +0 -19
  182. data/specs/Route53-RecordSetGroup.cf +0 -9
  183. data/specs/S3-Bucket.cf +0 -15
  184. data/specs/S3-BucketPolicy.cf +0 -7
  185. data/specs/SDB-Domain.cf +0 -6
  186. data/specs/SNS-Topic.cf +0 -8
  187. data/specs/SNS-TopicPolicy.cf +0 -8
  188. data/specs/SQS-Queue.cf +0 -12
  189. data/specs/SQS-QueuePolicy.cf +0 -7
  190. data/specs/SSM-Document.cf +0 -6
  191. data/specs/WAF-ByteMatchSet.cf +0 -7
  192. data/specs/WAF-IPSet.cf +0 -7
  193. data/specs/WAF-Rule.cf +0 -8
  194. data/specs/WAF-SizeConstraintSet.cf +0 -7
  195. data/specs/WAF-SqlInjectionMatchSet.cf +0 -7
  196. data/specs/WAF-WebACL.cf +0 -9
  197. data/specs/WAF-XssMatchSet.cf +0 -7
  198. data/specs/WorkSpaces-Workspace.cf +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed17317e4e90ba482a45ad606fe918e99cd5361d
4
- data.tar.gz: 3d7c79b5e6c4fd441480f8ce7b3f6a2a0d882875
3
+ metadata.gz: fe5b415f92136b9b0615485635bed3a6eaa1216c
4
+ data.tar.gz: 234763b8d4472cc9d50f3b63b222552e081bb522
5
5
  SHA512:
6
- metadata.gz: b41b275e02e29db2c24fdf0962a156b4f4c4c0865591bb90331d44707f8c6fa4cc912e122560997e993e22d7afe1ecc948baf1b6da46b0a3d160177400f0ba92
7
- data.tar.gz: ed3bdbeef33547d65878241963a6489830d7c8b1eb4abbc69a92ef687ef1e850183dd950f8b797bfc66004d976a82b3e91a2d9d18e3affb8011830ef798c8da2
6
+ metadata.gz: b8bffb418bb9316b042ed596a175a7a38f3b4e7cf494a5710a3509113f82b2cf0c02476be8a6673d6ddd301859b41c62201cdc4e021d66335384b8dde9ea02f5
7
+ data.tar.gz: fcf7020a824041b984bc221b279a806f7c67b93f7e73a14db570f8def9689b720c7ee0312ec8d3716f521c01f20ea04021abe1cc5f43711742829495b3503a44
data/README.md CHANGED
@@ -20,7 +20,7 @@ Stacks are represented by the `Humidifier::Stack` class. You can set any of the
20
20
  stack = Humidifier::Stack.new(name: 'Example-Stack', aws_template_format_version: '2010-09-09')
21
21
 
22
22
  load_balancer = Humidifier::ElasticLoadBalancing::LoadBalancer.new(
23
- listeners: [{ LoadBalancerPort: 80, Protocol: 'http', InstancePort: 80, InstanceProtocol: 'http' }]
23
+ listeners: [{ load_balancer_port: 80, protocol: 'http', instance_port: 80, instance_protocol: 'http' }]
24
24
  )
25
25
  load_balancer.scheme = 'internal'
26
26
 
@@ -92,7 +92,7 @@ The default rake task runs the tests. Coverage is reported on the command line,
92
92
 
93
93
  ### Specs
94
94
 
95
- The specs pulled from the CFN docs live under `/specs`. You can update them by running `bin/get-specs`. This script will scrape the docs by going to the [listings page](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html), finding the list of CFN resources, and then downloading the spec for each resource by going to the individual page.
95
+ The specs pulled from the CFN docs lives in `specs/CloudFormationResourceSpecification.json`. You can update it by running `bundle exec rake specs`. This script will pull down the latest [resource specification](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-resource-specification.html) to be used with Humidifier.
96
96
 
97
97
  ### Extension
98
98
 
@@ -1,20 +1,5 @@
1
1
  #include <humidifier.h>
2
2
 
3
- // copies from the source string to the destination string after passing through a character whitelist filter
4
- // note that this is exclusively built for AWS::CloudFormation::Interface, so if AWS ever fixes their docs this can
5
- // be replaced by strcpy
6
- static void filter(char* dest, const char* source)
7
- {
8
- int source_idx, dest_idx;
9
-
10
- for (source_idx = 0, dest_idx = 0; source[source_idx] != '\0'; source_idx++) {
11
- if (source[source_idx] != ':') {
12
- dest[dest_idx++] = source[source_idx];
13
- }
14
- }
15
- dest[dest_idx] = '\0';
16
- }
17
-
18
3
  // takes a substring from underscore_preprocess like EC2T or AWST and converts it to Ec2T or AwsT
19
4
  static void format_substring(char* substr, const int substr_idx, const int capitalize)
20
5
  {
@@ -64,7 +49,7 @@ static VALUE underscore(VALUE self, VALUE str)
64
49
  char *str_value = rb_string_value_cstr(&str);
65
50
  char orig_str[strlen(str_value) + 1];
66
51
 
67
- filter(orig_str, str_value);
52
+ strcpy(orig_str, str_value);
68
53
  preprocess(orig_str);
69
54
 
70
55
  // manually null-terminating the string because on Fedora for strings of length 16 this breaks otherwise
@@ -4,7 +4,6 @@
4
4
  #include <ctype.h>
5
5
  #include <ruby.h>
6
6
 
7
- static void filter(char* dest, const char* source);
8
7
  static void format_substring(char* substr, const int substr_idx, const int capitalize);
9
8
  static void preprocess(char* str);
10
9
  static VALUE underscore(VALUE self, VALUE str);
@@ -3,6 +3,8 @@ module Humidifier
3
3
  # a container for user params
4
4
  class Configuration
5
5
 
6
+ # The message that gets displayed when the stack body is too large to use
7
+ # the template_body option
6
8
  UPLOAD_MESSAGE = <<-MSG.freeze
7
9
  The %{identifier} stack's body is too large to be use the template_body option, and therefore must use the
8
10
  template_url option instead. You can configure Humidifier to do this automatically by setting up the s3 config
data/lib/humidifier/fn.rb CHANGED
@@ -5,7 +5,8 @@ module Humidifier
5
5
 
6
6
  # The list of all internal functions provided by AWS from
7
7
  # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
8
- FUNCTIONS = Utils.underscored(%w[And Base64 Equals FindInMap GetAtt GetAZs If Join Not Or Select])
8
+ FUNCTIONS = Utils.underscored(%w[And Base64 Equals FindInMap GetAtt GetAZs
9
+ If ImportValue Join Not Or Select Sub])
9
10
 
10
11
  attr_accessor :name, :value
11
12
 
Binary file
@@ -6,13 +6,44 @@ module Humidifier
6
6
  module Config
7
7
  end
8
8
 
9
- # Reads each of the files under /specs/ and loads them each as a class
9
+ # Reads the specs/CloudFormationResourceSpecification.json file and load each resource as a class
10
10
  class Loader
11
+ # The path to the specification file
12
+ SPECPATH = File.expand_path(
13
+ File.join('..', '..', '..', 'specs', 'CloudFormationResourceSpecification.json'),
14
+ __FILE__
15
+ )
16
+
17
+ # Handles searching the PropertyTypes specifications for a specific resource type
18
+ class StructureContainer
19
+ attr_reader :structs
20
+
21
+ def initialize(structs)
22
+ @structs = structs
23
+ end
24
+
25
+ # find the substructures necessary for the given resource key
26
+ def search(key)
27
+ results = structs.keys.grep(/#{key}/)
28
+ Hash[results.map { |result| result.gsub("#{key}.", '') }.zip(structs.values_at(*results))].merge(global)
29
+ end
30
+
31
+ private
32
+
33
+ def global
34
+ @global ||= structs.select { |key, _| !key.match(/AWS/) }
35
+ end
36
+ end
11
37
 
12
38
  # loop through the specs and register each class
13
39
  def load
14
- spec_directory = File.expand_path(File.join('..', '..', '..', 'specs', '*'), __FILE__)
15
- Dir[spec_directory].each { |filepath| load_from(filepath) }
40
+ parsed = JSON.parse(File.read(SPECPATH))
41
+ structs = StructureContainer.new(parsed['PropertyTypes'])
42
+
43
+ parsed['ResourceTypes'].each do |key, spec|
44
+ match = key.match(/\AAWS::(\w+)::(\w+)\z/)
45
+ register(match[1], match[2], spec, structs.search(key))
46
+ end
16
47
  end
17
48
 
18
49
  # convenience class method to build a new loader and call load
@@ -22,28 +53,20 @@ module Humidifier
22
53
 
23
54
  private
24
55
 
25
- def build_class(aws_name, spec)
56
+ def build_class(aws_name, spec, substructs)
26
57
  Class.new(Resource) do
27
58
  self.aws_name = aws_name
28
- self.props = spec.each_with_object({}) do |spec_line, props|
29
- prop = Props.from(spec_line)
30
- props[prop.name] = prop unless prop.name.nil?
31
- end
32
- end
33
- end
34
-
35
- def load_from(filepath)
36
- group, resource = Pathname.new(filepath).basename('.cf').to_s.split('-')
37
- spec = File.readlines(filepath).select do |line|
38
- # flipflop operator (http://stackoverflow.com/questions/14456634)
39
- true if line.include?('Properties')...(line.strip == '}')
59
+ self.props =
60
+ Utils.enumerable_to_h(spec['Properties']) do |(key, config)|
61
+ prop = Props.from(key, config, substructs)
62
+ [prop.name, prop]
63
+ end
40
64
  end
41
- register(group, resource, spec[1..-2])
42
65
  end
43
66
 
44
- def register(group, resource, spec)
67
+ def register(group, resource, spec, substructs)
45
68
  aws_name = "AWS::#{group}::#{resource}"
46
- resource_class = build_class(aws_name, spec)
69
+ resource_class = build_class(aws_name, spec, substructs)
47
70
 
48
71
  Humidifier.const_set(group, Module.new) unless Humidifier.const_defined?(group)
49
72
  Humidifier.const_get(group).const_set(resource, resource_class)
@@ -0,0 +1,49 @@
1
+ module Humidifier
2
+ module Props
3
+ # Superclass for all CFN properties
4
+ class Base
5
+
6
+ # The list of classes that are valid beyond the normal values for each prop
7
+ WHITELIST = [Fn, Ref].freeze
8
+
9
+ attr_reader :key, :name, :spec, :substructs
10
+
11
+ def initialize(key, spec = {}, substructs = {})
12
+ @key = key
13
+ @name = Utils.underscore(key)
14
+ @spec = spec
15
+ after_initialize(substructs) if respond_to?(:after_initialize, true)
16
+ end
17
+
18
+ # true if the property type knows how to convert its values
19
+ def convertable?
20
+ respond_to?(:convert)
21
+ end
22
+
23
+ # the link to the AWS docs
24
+ def documentation
25
+ spec['Documentation']
26
+ end
27
+
28
+ # true if this property is required by the resource
29
+ def required?
30
+ spec['Required']
31
+ end
32
+
33
+ # CFN stack syntax
34
+ def to_cf(value)
35
+ [key, Serializer.dump(value)]
36
+ end
37
+
38
+ # the type of update that occurs when this property is updated on its associated resource
39
+ def update_type
40
+ spec['UpdateType']
41
+ end
42
+
43
+ # true if the given value is of a type contained in the whitelist
44
+ def whitelisted_value?(value)
45
+ WHITELIST.any? { |clazz| value.is_a?(clazz) }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,21 @@
1
+ module Humidifier
2
+ module Props
3
+ # A boolean property
4
+ class BooleanProp < Base
5
+ # converts through value == 'true'
6
+ def convert(value)
7
+ if %w[true false].include?(value)
8
+ puts "WARNING: Property #{name} should be a boolean, not a string"
9
+ value == 'true'
10
+ else
11
+ value
12
+ end
13
+ end
14
+
15
+ # true if it is a boolean
16
+ def valid?(value)
17
+ value.is_a?(TrueClass) || value.is_a?(FalseClass)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Humidifier
2
+ module Props
3
+ # A double property
4
+ class DoubleProp < Base
5
+ # converts the value through #to_f unless it is whitelisted
6
+ def convert(value)
7
+ puts "WARNING: Property #{name} should be a double" unless valid?(value)
8
+ value.to_f
9
+ end
10
+
11
+ # true if it is whitelisted, an Integer, or a Float
12
+ def valid?(value)
13
+ whitelisted_value?(value) || value.is_a?(Integer) || value.is_a?(Float)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Humidifier
2
+ module Props
3
+ # An integer property
4
+ class IntegerProp < Base
5
+ # converts the value through #to_i unless it is whitelisted
6
+ def convert(value)
7
+ puts "WARNING: Property #{name} should be an integer" unless valid?(value)
8
+ value.to_i
9
+ end
10
+
11
+ # true if it is whitelisted or a Integer
12
+ def valid?(value)
13
+ whitelisted_value?(value) || value.is_a?(Integer)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Humidifier
2
+ module Props
3
+ # A Json property
4
+ class JsonProp < Base
5
+ # true if the value is whitelisted, a Hash, or an Array
6
+ def valid?(value)
7
+ whitelisted_value?(value) || value.is_a?(Hash) || value.is_a?(Array)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module Humidifier
2
+ module Props
3
+ # A property that is contained in a list
4
+ class ListProp < Base
5
+ attr_reader :subprop
6
+
7
+ # Valid if the value is whitelisted or every value in the list is valid on the subprop
8
+ def valid?(list)
9
+ whitelisted_value?(list) || (list.is_a?(Enumerable) && list.all? { |value| subprop.valid?(value) })
10
+ end
11
+
12
+ private
13
+
14
+ # Finds the subprop that's specified in the spec
15
+ def after_initialize(substructs)
16
+ @subprop = Props.singular_from(key, spec, substructs)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Humidifier
2
+ module Props
3
+ # A property that is contained in a Map
4
+ class MapProp < Base
5
+ attr_reader :subprop
6
+
7
+ # Valid if the value is whitelisted or every value in the map is valid on the subprop
8
+ def valid?(map)
9
+ whitelisted_value?(map) || (map.is_a?(Hash) && map.values.all? { |value| subprop.valid?(value) })
10
+ end
11
+
12
+ private
13
+
14
+ # Finds the subprop that's specified in the spec
15
+ def after_initialize(substructs)
16
+ @subprop = Props.singular_from(key, spec, substructs)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ module Humidifier
2
+ module Props
3
+ # A string property
4
+ class StringProp < Base
5
+ # converts the value through #to_s unless it is whitelisted
6
+ def convert(value)
7
+ puts "WARNING: Property #{name} should be a string" unless valid?(value)
8
+ whitelisted_value?(value) ? value : value.to_s
9
+ end
10
+
11
+ # true if it is whitelisted or a String
12
+ def valid?(value)
13
+ whitelisted_value?(value) || value.is_a?(String)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ module Humidifier
2
+ module Props
3
+ # A structure property that references a structure from the specification
4
+ class StructureProp < Base
5
+ attr_reader :subprops
6
+
7
+ # true if the value is whitelisted or Hash and all keys are valid for their corresponding props
8
+ def valid?(struct)
9
+ whitelisted_value?(struct) ||
10
+ (struct.is_a?(Hash) &&
11
+ struct.all? { |key, value| subprops.key?(key.to_s) && subprops[key.to_s].valid?(value) })
12
+ end
13
+
14
+ private
15
+
16
+ def after_initialize(substructs)
17
+ type = spec['ItemType'] || spec['Type']
18
+ @subprops =
19
+ Utils.enumerable_to_h(substructs[type]['Properties']) do |(key, config)|
20
+ subprop = config['ItemType'] == type ? self : Props.from(nil, config, substructs)
21
+ [Utils.underscore(key), subprop]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ module Humidifier
2
+ module Props
3
+ # A timestamp (ISO 8601) property
4
+ class TimestampProp < Base
5
+ # converts the value through #to_s unless it is whitelisted
6
+ def convert(value)
7
+ puts "WARNING: Property #{name} should be a Date or Time" unless valid?(value)
8
+ valid?(value) ? value : DateTime.parse(value)
9
+ end
10
+
11
+ # true if it is whitelisted, a Time, or a Date
12
+ def valid?(value)
13
+ whitelisted_value?(value) || value.is_a?(Time) || value.is_a?(Date)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,139 +2,26 @@ module Humidifier
2
2
 
3
3
  # Container for property of CFN resources
4
4
  module Props
5
-
6
- # Superclass for all CFN properties
7
- class Base
8
-
9
- # The list of classes that are valid beyond the normal values for each prop
10
- WHITELIST = [Fn, Ref].freeze
11
-
12
- attr_accessor :key
13
- attr_reader :value
14
-
15
- def initialize(key = nil)
16
- self.key = key
17
- end
18
-
19
- # true if the property type knows how to convert its values
20
- def convertable?
21
- respond_to?(:convert)
22
- end
23
-
24
- # the name of the property
25
- def name
26
- @name ||= Utils.underscore(key)
27
- end
28
-
29
- # CFN stack syntax
30
- def to_cf(value)
31
- [key, Serializer.dump(value)]
32
- end
33
-
34
- # true if the given value is of a type contained in the whitelist
35
- def whitelisted_value?(value)
36
- WHITELIST.any? { |clazz| value.is_a?(clazz) }
37
- end
38
- end
39
-
40
- # An array property
41
- class ArrayProp < Base
42
- def valid?(value)
43
- value.is_a?(Array)
44
- end
45
- end
46
-
47
- # A boolean property
48
- class BooleanProp < Base
49
- # converts through value == 'true'
50
- def convert(value)
51
- if %w[true false].include?(value)
52
- puts "WARNING: Property #{name} should be a boolean, not a string"
53
- value == 'true'
54
- else
55
- value
56
- end
57
- end
58
-
59
- # true if it is a boolean
60
- def valid?(value)
61
- value.is_a?(TrueClass) || value.is_a?(FalseClass)
62
- end
63
- end
64
-
65
- # A JSON property (and the default)
66
- class JSONProp < Base
67
- def valid?(value)
68
- whitelisted_value?(value) || value.is_a?(Hash) || value.is_a?(Array)
69
- end
70
- end
71
-
72
- # An integer property
73
- class IntegerProp < Base
74
- # converts the value through #to_i unless it is whitelisted
75
- def convert(value)
76
- puts "WARNING: Property #{name} should be an integer" unless valid?(value)
77
- value.to_i
78
- end
79
-
80
- # true if it is whitelisted or a Integer
81
- def valid?(value)
82
- whitelisted_value?(value) || value.is_a?(Integer)
83
- end
84
- end
85
-
86
- # A string property
87
- class StringProp < Base
88
- # converts the value through #to_s unless it is whitelisted
89
- def convert(value)
90
- puts "WARNING: Property #{name} should be a string" unless valid?(value)
91
- whitelisted_value?(value) ? value : value.to_s
92
- end
93
-
94
- # true if it is whitelisted or a String
95
- def valid?(value)
96
- whitelisted_value?(value) || value.is_a?(String)
97
- end
98
- end
99
-
100
- # An number property
101
- class NumberProp < Base
102
- # converts the value through #to_i unless it is whitelisted
103
- def convert(value)
104
- puts "WARNING: Property #{name} should be a number" unless valid?(value)
105
- value.to_i
106
- end
107
-
108
- # true if it is whitelisted or a Integer
109
- def valid?(value)
110
- whitelisted_value?(value) || value.is_a?(Integer)
111
- end
112
- end
113
-
114
5
  class << self
115
6
  # builds the appropriate prop object from the given spec line
116
- def from(spec_line)
117
- key, type = parse(spec_line)
118
-
119
- case type
120
- when 'Boolean' then BooleanProp.new(key)
121
- when 'Integer' then IntegerProp.new(key)
122
- when 'String' then StringProp.new(key)
123
- when 'Number' then NumberProp.new(key)
124
- when /\[.*?\]/ then ArrayProp.new(key)
125
- else JSONProp.new(key)
7
+ def from(key, spec, substructs = {})
8
+ case spec['Type']
9
+ when 'List' then ListProp.new(key, spec, substructs)
10
+ when 'Map' then MapProp.new(key, spec, substructs)
11
+ else singular_from(key, spec, substructs)
126
12
  end
127
13
  end
128
14
 
129
- # parses a spec line to return a key and type
130
- def parse(spec_line)
131
- spec_line.strip!
15
+ # builds a prop that is not a List or Map type
16
+ # PrimitiveType is one of Boolean, Double, Integer, Json, String, or Timestamp
17
+ def singular_from(key, spec, substructs)
18
+ primitive = spec['PrimitiveItemType'] || spec['PrimitiveType']
132
19
 
133
- if spec_line.include?(':')
134
- key, type = spec_line.gsub(/,\z/, '').split(': ').map(&:strip)
135
- [key[1..-2], type]
20
+ if primitive
21
+ primitive = 'Integer' if primitive == 'Long'
22
+ const_get(:"#{primitive}Prop").new(key, spec)
136
23
  else
137
- [nil, spec_line]
24
+ StructureProp.new(key, spec, substructs)
138
25
  end
139
26
  end
140
27
  end
@@ -82,9 +82,10 @@ MSG
82
82
 
83
83
  def template_param
84
84
  @template_param ||= begin
85
- raise TemplateTooLargeError, template_body.bytesize if template_body.bytesize > MAX_TEMPLATE_URL_SIZE
85
+ bytesize = template_body.bytesize
86
+ raise TemplateTooLargeError, bytesize if bytesize > MAX_TEMPLATE_URL_SIZE
86
87
 
87
- if template_body.bytesize > MAX_TEMPLATE_BODY_SIZE
88
+ if bytesize > MAX_TEMPLATE_BODY_SIZE
88
89
  { template_url: AwsShim.upload(self) }
89
90
  else
90
91
  { template_body: template_body }
@@ -7,9 +7,11 @@ module Humidifier
7
7
  # dumps the given object out to CFN syntax recursively
8
8
  def dump(node)
9
9
  case node
10
- when Hash then Utils.enumerable_to_h(node) { |(key, value)| [key, dump(value)] }
11
- when Array then node.map { |value| dump(value) }
10
+ when Hash then Utils.enumerable_to_h(node) { |(key, value)| [key, dump(value)] }
11
+ when Array then node.map { |value| dump(value) }
12
12
  when Ref, Fn then dump(node.to_cf)
13
+ when Date then node.iso8601
14
+ when Time then node.to_datetime.iso8601
13
15
  else node
14
16
  end
15
17
  end
@@ -1,4 +1,4 @@
1
1
  module Humidifier
2
2
  # Gem version
3
- VERSION = '0.7.0'.freeze
3
+ VERSION = '1.0.0'.freeze
4
4
  end
data/lib/humidifier.rb CHANGED
@@ -10,6 +10,11 @@ require 'humidifier/fn'
10
10
  require 'humidifier/ref'
11
11
  require 'humidifier/props'
12
12
 
13
+ require 'humidifier/props/base'
14
+ %w[boolean double integer json list map string structure timestamp].each do |type|
15
+ require "humidifier/props/#{type}_prop"
16
+ end
17
+
13
18
  require 'humidifier/aws_shim'
14
19
  require 'humidifier/condition'
15
20
  require 'humidifier/configuration'