humidifier 0.7.0 → 1.0.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 (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'