digicert 0.1.0 → 0.1.1

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.hound.yml +3 -0
  4. data/.rspec +0 -1
  5. data/.rubocop.yml +629 -0
  6. data/.sample.env +4 -0
  7. data/.sample.pryrc +3 -0
  8. data/.travis.yml +5 -2
  9. data/LICENSE.txt +21 -0
  10. data/README.md +812 -9
  11. data/bin/console +2 -5
  12. data/bin/rspec +17 -0
  13. data/digicert.gemspec +7 -14
  14. data/lib/digicert.rb +40 -2
  15. data/lib/digicert/actions.rb +9 -0
  16. data/lib/digicert/actions/all.rb +27 -0
  17. data/lib/digicert/actions/base.rb +11 -0
  18. data/lib/digicert/actions/create.rb +25 -0
  19. data/lib/digicert/actions/fetch.rb +21 -0
  20. data/lib/digicert/actions/update.rb +25 -0
  21. data/lib/digicert/base.rb +35 -0
  22. data/lib/digicert/base_order.rb +39 -0
  23. data/lib/digicert/certificate.rb +43 -0
  24. data/lib/digicert/certificate_downloader.rb +137 -0
  25. data/lib/digicert/certificate_request.rb +19 -0
  26. data/lib/digicert/client_certificate/base.rb +17 -0
  27. data/lib/digicert/client_certificate/digital_signature_plus.rb +13 -0
  28. data/lib/digicert/client_certificate/email_security_plus.rb +13 -0
  29. data/lib/digicert/client_certificate/premium.rb +17 -0
  30. data/lib/digicert/config.rb +21 -0
  31. data/lib/digicert/configuration.rb +26 -0
  32. data/lib/digicert/container.rb +35 -0
  33. data/lib/digicert/container_template.rb +32 -0
  34. data/lib/digicert/csr_generator.rb +43 -0
  35. data/lib/digicert/debugger.rb +34 -0
  36. data/lib/digicert/domain.rb +59 -0
  37. data/lib/digicert/duplicate_certificate.rb +21 -0
  38. data/lib/digicert/duplicate_certificate_finder.rb +42 -0
  39. data/lib/digicert/email_validation.rb +35 -0
  40. data/lib/digicert/errors.rb +30 -0
  41. data/lib/digicert/errors/forbidden.rb +9 -0
  42. data/lib/digicert/errors/request_error.rb +37 -0
  43. data/lib/digicert/errors/server_error.rb +9 -0
  44. data/lib/digicert/errors/unauthorized.rb +9 -0
  45. data/lib/digicert/expiring_order.rb +21 -0
  46. data/lib/digicert/findable.rb +33 -0
  47. data/lib/digicert/order.rb +81 -0
  48. data/lib/digicert/order_cancellation.rb +25 -0
  49. data/lib/digicert/order_duplicator.rb +11 -0
  50. data/lib/digicert/order_manager.rb +39 -0
  51. data/lib/digicert/order_reissuer.rb +11 -0
  52. data/lib/digicert/organization.rb +43 -0
  53. data/lib/digicert/product.rb +14 -0
  54. data/lib/digicert/request.rb +123 -0
  55. data/lib/digicert/response.rb +30 -0
  56. data/lib/digicert/ssl_certificate/base.rb +9 -0
  57. data/lib/digicert/ssl_certificate/ssl_ev_plus.rb +13 -0
  58. data/lib/digicert/ssl_certificate/ssl_plus.rb +13 -0
  59. data/lib/digicert/ssl_certificate/ssl_wildcard.rb +13 -0
  60. data/lib/digicert/version.rb +23 -1
  61. data/spec/acceptance/certificate_download_spec.rb +68 -0
  62. data/spec/acceptance/duplicating_certificate_spec.rb +86 -0
  63. data/spec/acceptance/reissuing_certificate_spec.rb +104 -0
  64. data/spec/digicert/actions/all_spec.rb +26 -0
  65. data/spec/digicert/actions/create_spec.rb +47 -0
  66. data/spec/digicert/actions/fetch_spec.rb +28 -0
  67. data/spec/digicert/actions/update_spec.rb +39 -0
  68. data/spec/digicert/certificate_downloader_spec.rb +89 -0
  69. data/spec/digicert/certificate_request_spec.rb +49 -0
  70. data/spec/digicert/certificate_spec.rb +93 -0
  71. data/spec/digicert/client_certificate/digital_signature_plus_spec.rb +32 -0
  72. data/spec/digicert/client_certificate/email_security_plus_spec.rb +36 -0
  73. data/spec/digicert/client_certificate/premium_spec.rb +34 -0
  74. data/spec/digicert/config_spec.rb +39 -0
  75. data/spec/digicert/container_spec.rb +44 -0
  76. data/spec/digicert/container_template_spec.rb +32 -0
  77. data/spec/digicert/csr_generator_spec.rb +31 -0
  78. data/spec/digicert/domain_spec.rb +89 -0
  79. data/spec/digicert/duplicate_certificate_finder_spec.rb +27 -0
  80. data/spec/digicert/duplicate_certificate_spec.rb +15 -0
  81. data/spec/digicert/email_validation_spec.rb +26 -0
  82. data/spec/digicert/expiring_order_spec.rb +16 -0
  83. data/spec/digicert/findable_spec.rb +19 -0
  84. data/spec/digicert/order_cancellation_spec.rb +24 -0
  85. data/spec/digicert/order_duplicator_spec.rb +35 -0
  86. data/spec/digicert/order_reissuer_spec.rb +35 -0
  87. data/spec/digicert/order_spec.rb +134 -0
  88. data/spec/digicert/organization_spec.rb +61 -0
  89. data/spec/digicert/product_spec.rb +28 -0
  90. data/spec/digicert/request_spec.rb +47 -0
  91. data/spec/digicert/ssl_certificate/ssl_ev_plus_spec.rb +35 -0
  92. data/spec/digicert/ssl_certificate/ssl_plus_spec.rb +36 -0
  93. data/spec/digicert/ssl_certificate/ssl_wildcard_spec.rb +35 -0
  94. data/spec/fixtures/certificate.pem +79 -0
  95. data/spec/fixtures/certificate.zip +0 -0
  96. data/spec/fixtures/certificate_request.json +116 -0
  97. data/spec/fixtures/certificate_requests.json +59 -0
  98. data/spec/fixtures/certificate_revoked.json +13 -0
  99. data/spec/fixtures/container.json +15 -0
  100. data/spec/fixtures/container_created.json +3 -0
  101. data/spec/fixtures/container_template.json +15 -0
  102. data/spec/fixtures/container_templates.json +14 -0
  103. data/spec/fixtures/containers.json +14 -0
  104. data/spec/fixtures/domain.json +71 -0
  105. data/spec/fixtures/domain_created.json +3 -0
  106. data/spec/fixtures/domains.json +49 -0
  107. data/spec/fixtures/email_validations.json +17 -0
  108. data/spec/fixtures/empty.json +0 -0
  109. data/spec/fixtures/errors.json +6 -0
  110. data/spec/fixtures/expiring_orders.json +20 -0
  111. data/spec/fixtures/order.json +107 -0
  112. data/spec/fixtures/order_created.json +9 -0
  113. data/spec/fixtures/order_duplicated.json +8 -0
  114. data/spec/fixtures/order_duplications.json +57 -0
  115. data/spec/fixtures/order_reissued.json +8 -0
  116. data/spec/fixtures/orders.json +93 -0
  117. data/spec/fixtures/organization.json +35 -0
  118. data/spec/fixtures/organization_created.json +3 -0
  119. data/spec/fixtures/organizations.json +84 -0
  120. data/spec/fixtures/ping.json +3 -0
  121. data/spec/fixtures/product.json +71 -0
  122. data/spec/fixtures/products.json +100 -0
  123. data/spec/fixtures/rsa4096.key +51 -0
  124. data/spec/requests/certificate_duplication_spec.rb +41 -0
  125. data/spec/requests/certificate_generation_spec.rb +93 -0
  126. data/spec/requests/certificate_reissuing_spec.rb +38 -0
  127. data/spec/requests/container_management_spec.rb +36 -0
  128. data/spec/requests/domain_management_spec.rb +64 -0
  129. data/spec/requests/order_client_email_security_plus_spec.rb +38 -0
  130. data/spec/requests/order_management_spec.rb +24 -0
  131. data/spec/requests/order_ssl_ev_plus_spec.rb +57 -0
  132. data/spec/requests/order_ssl_wildcard_spec.rb +57 -0
  133. data/spec/requests/organization_management_spec.rb +22 -0
  134. data/spec/requests/product_management_spec.rb +24 -0
  135. data/spec/requests/request_management_spec.rb +24 -0
  136. data/spec/spec_helper.rb +35 -0
  137. data/spec/support/fake_digicert_api.rb +324 -0
  138. metadata +162 -5
data/bin/console CHANGED
@@ -7,8 +7,5 @@ require "digicert"
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
9
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
10
+ require "pry"
11
+ Pry.start
data/bin/rspec ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'rspec' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("rspec-core", "rspec")
data/digicert.gemspec CHANGED
@@ -12,24 +12,17 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = %q{Digicert Ruby API.}
13
13
  spec.description = %q{Digicert Ruby API.}
14
14
  spec.homepage = "https://www.ribose.com"
15
+ spec.license = "MIT"
15
16
 
16
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
- # to allow pushing to a single host or delete this section to allow pushing to any host.
18
- if spec.respond_to?(:metadata)
19
- #spec.metadata['allowed_push_host'] = "https://gems.ribose.com"
20
- else
21
- raise "RubyGems 2.0 or newer is required to protect against " \
22
- "public gem pushes."
23
- end
24
-
25
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
- f.match(%r{^(test|spec|features)/})
27
- end
28
- spec.bindir = "exe"
29
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
17
  spec.require_paths = ["lib"]
18
+ spec.files = `git ls-files`.split("\n")
19
+ spec.test_files = `git ls-files -- {spec}/*`.split("\n")
20
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.1.9")
21
+
22
+ spec.add_dependency "r509", "~> 1.0"
31
23
 
32
24
  spec.add_development_dependency "pry"
25
+ spec.add_development_dependency "dotenv"
33
26
  spec.add_development_dependency "bundler", "~> 1.14"
34
27
  spec.add_development_dependency "rake", "~> 12.0"
35
28
  spec.add_development_dependency "rspec", "~> 3.0"
data/lib/digicert.rb CHANGED
@@ -1,5 +1,43 @@
1
- require "digicert/version"
1
+ #--
2
+ # Copyright (c) 2017 Ribose Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ # ++
22
+
23
+ require "digicert/config"
24
+ require "digicert/product"
25
+ require "digicert/order"
26
+ require "digicert/csr_generator"
27
+ require "digicert/certificate_request"
28
+ require "digicert/organization"
29
+ require "digicert/container_template"
30
+ require "digicert/container"
31
+ require "digicert/domain"
32
+ require "digicert/certificate_downloader"
33
+ require "digicert/email_validation"
34
+ require "digicert/order_reissuer"
35
+ require "digicert/order_duplicator"
36
+ require "digicert/duplicate_certificate"
37
+ require "digicert/order_cancellation"
38
+ require "digicert/expiring_order"
39
+ require "digicert/duplicate_certificate_finder"
40
+ require "digicert/certificate"
2
41
 
3
42
  module Digicert
4
- # Your code goes here...
5
43
  end
@@ -0,0 +1,9 @@
1
+ require "digicert/actions/all"
2
+ require "digicert/actions/fetch"
3
+ require "digicert/actions/create"
4
+ require "digicert/actions/update"
5
+
6
+ module Digicert
7
+ module Actions
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ require "digicert/actions/base"
2
+
3
+ module Digicert
4
+ module Actions
5
+ module All
6
+ extend Digicert::Actions::Base
7
+
8
+ def all
9
+ response = Digicert::Request.new(
10
+ :get, resource_path, params: query_params,
11
+ ).parse
12
+
13
+ response[resources_key]
14
+ end
15
+
16
+ def resources_key
17
+ [resource_path, "s"].join
18
+ end
19
+
20
+ module ClassMethods
21
+ def all(filter_params = {})
22
+ new(params: filter_params).all
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ require "digicert/request"
2
+
3
+ module Digicert
4
+ module Actions
5
+ module Base
6
+ def included(base)
7
+ base.extend(const_get(:ClassMethods))
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ require "digicert/actions/base"
2
+
3
+ module Digicert
4
+ module Actions
5
+ module Create
6
+ extend Digicert::Actions::Base
7
+
8
+ def create
9
+ Digicert::Request.new(
10
+ :post, resource_creation_path, validate(attributes),
11
+ ).parse
12
+ end
13
+
14
+ def resource_creation_path
15
+ resource_path
16
+ end
17
+
18
+ module ClassMethods
19
+ def create(attributes)
20
+ new(attributes).create
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ require "digicert/actions/base"
2
+
3
+ module Digicert
4
+ module Actions
5
+ module Fetch
6
+ extend Digicert::Actions::Base
7
+
8
+ def fetch
9
+ Digicert::Request.new(
10
+ :get, [resource_path, resource_id].join("/"), params: query_params,
11
+ ).parse
12
+ end
13
+
14
+ module ClassMethods
15
+ def fetch(resource_id, filter_params = {})
16
+ new(resource_id: resource_id, params: filter_params).fetch
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ require "digicert/actions/base"
2
+
3
+ module Digicert
4
+ module Actions
5
+ module Update
6
+ extend Digicert::Actions::Base
7
+
8
+ def update
9
+ Digicert::Request.new(
10
+ :put, resource_update_path, attributes,
11
+ ).run
12
+ end
13
+
14
+ def resource_update_path
15
+ [resource_path, resource_id].join("/")
16
+ end
17
+
18
+ module ClassMethods
19
+ def update(resource_id, attributes)
20
+ new(resource_id: resource_id, **attributes).update
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ require "digicert/request"
2
+ require "digicert/actions"
3
+
4
+ module Digicert
5
+ class Base
6
+ def initialize(attributes = {})
7
+ @attributes = attributes
8
+ extract_base_attribute_ids
9
+
10
+ extract_local_attribute_ids
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :attributes, :resource_id, :query_params
16
+
17
+ # Override this method to extract ids that are specific
18
+ # to each of the specific sub classes, for example: if
19
+ # you want to extract `order_id` from the attributes
20
+ #
21
+ # @order_id = attributes.delete(:order_id)
22
+ #
23
+ def extract_local_attribute_ids
24
+ end
25
+
26
+ def extract_base_attribute_ids
27
+ @query_params = attributes.delete(:params)
28
+ @resource_id = attributes.delete(:resource_id)
29
+ end
30
+
31
+ def request_klass
32
+ Digicert::Request
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ require "digicert/base"
2
+
3
+ module Digicert
4
+ class BaseOrder < Digicert::Base
5
+ include Digicert::Actions::Create
6
+
7
+ private
8
+
9
+ def validate(certificate:, organization:, validity_years:, **attributes)
10
+ required_attributes = {
11
+ certificate: validate_certificate(certificate),
12
+ organization: validate_organization(organization),
13
+ validity_years: validity_years,
14
+ }
15
+
16
+ required_attributes.merge(attributes)
17
+ end
18
+
19
+ def resource_path
20
+ "order/certificate"
21
+ end
22
+
23
+ def resource_creation_path
24
+ [resource_path, certificate_type].join("/")
25
+ end
26
+
27
+ def validate_organization(id:)
28
+ { id: id }
29
+ end
30
+
31
+ def validate_certificate(common_name:, csr:, signature_hash:, **attrs)
32
+ attrs.merge(
33
+ csr: csr,
34
+ common_name: common_name,
35
+ signature_hash: signature_hash,
36
+ )
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,43 @@
1
+ require "digicert/base"
2
+
3
+ module Digicert
4
+ class Certificate < Digicert::Base
5
+ extend Digicert::Findable
6
+
7
+ def download(attributes = {})
8
+ certificate_downloader.fetch(resource_id, attributes)
9
+ end
10
+
11
+ def download_content
12
+ certificate_downloader.fetch_content(resource_id)
13
+ end
14
+
15
+ def revoke
16
+ request_klass.new(:put, revocation_path, attributes).parse
17
+ end
18
+
19
+ def self.revoke(certificate_id, attributes = {})
20
+ new(attributes.merge(resource_id: certificate_id)).revoke
21
+ end
22
+
23
+ def download_to_path(path:, ext: "zip", **attributes)
24
+ certificate_downloader.fetch_to_path(
25
+ resource_id, attributes.merge(path: path, ext: ext),
26
+ )
27
+ end
28
+
29
+ private
30
+
31
+ def resource_path
32
+ "certificate"
33
+ end
34
+
35
+ def revocation_path
36
+ [resource_path, resource_id, "revoke"].join("/")
37
+ end
38
+
39
+ def certificate_downloader
40
+ Digicert::CertificateDownloader
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,137 @@
1
+ require "digicert/base"
2
+
3
+ module Digicert
4
+ class CertificateDownloader < Digicert::Base
5
+ def fetch
6
+ request_klass.new(:get, certificate_download_path).run
7
+ end
8
+
9
+ def fetch_to_path(path:, extension: "zip")
10
+ download_to_path(path: path, extension: extension)
11
+ end
12
+
13
+ def fetch_content
14
+ extract_certificate_content
15
+ end
16
+
17
+ def self.fetch(certificate_id, attributes = {})
18
+ new(attributes.merge(resource_id: certificate_id)).fetch
19
+ end
20
+
21
+ def self.fetch_by_format(certificate_id, format:)
22
+ fetch(certificate_id, format: format)
23
+ end
24
+
25
+ def self.fetch_by_platform(certificate_id, platform:)
26
+ fetch(certificate_id, platform: platform)
27
+ end
28
+
29
+ def self.fetch_to_path(certificate_id, path:, ext: "zip", **attributes)
30
+ new(attributes.merge(resource_id: certificate_id)).
31
+ fetch_to_path(path: path, extension: ext)
32
+ end
33
+
34
+ def self.fetch_content(certificate_id)
35
+ new(resource_id: certificate_id, format: "pem_all").fetch_content
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :format, :platform
41
+
42
+ def extract_local_attribute_ids
43
+ @format = attributes.delete(:format)
44
+ @platform = attributes.delete(:platform)
45
+ end
46
+
47
+ def resource_path
48
+ ["certificate", resource_id, "download"].join("/")
49
+ end
50
+
51
+ def certificate_download_path
52
+ download_path_by_format ||
53
+ download_path_by_platform ||
54
+ download_path_by_order_specified_platform
55
+ end
56
+
57
+ def download_to_path(path:, extension:)
58
+ response = fetch
59
+
60
+ if response.code.to_i == 200
61
+ write_to_path(response.body, path: path, extension: extension)
62
+ end
63
+ end
64
+
65
+ def extract_certificate_content
66
+ convert_response_to_hash(fetch.body)
67
+ end
68
+
69
+ def download_path_by_format
70
+ if format
71
+ [resource_path, "format", format].join("/")
72
+ end
73
+ end
74
+
75
+ def download_path_by_platform
76
+ if platform
77
+ [resource_path, "platform", platform].join("/")
78
+ end
79
+ end
80
+
81
+ def download_path_by_order_specified_platform
82
+ [resource_path, "platform"].join("/")
83
+ end
84
+
85
+ def convert_response_to_hash(content)
86
+ contents = split_pem_certificates(content)
87
+
88
+ Hash.new.tap do |content_hash|
89
+ content_hash[:text] = content
90
+ content_hash[:certificate] = contents.first
91
+ content_hash[:root_certificate] = contents.last
92
+ content_hash[:intermediate_certificate] = extract_intermediate(contents)
93
+ end
94
+ end
95
+
96
+ # Spliting certificate content
97
+ #
98
+ # Digicert returns all of the certificates including `root` one when
99
+ # we specify `pem_all` as format. The format it returns the content
100
+ # has a pattern, which is it will have all of the three certificates.
101
+ # The sequance for the certificates are `certificate`, `intermediate`
102
+ # and `root` and each of them are separated by `END CERTIFICATE-----`
103
+ #
104
+ # This method will split those using the specified identifier and it
105
+ # will return an array in the same sequance.
106
+ #
107
+ def split_pem_certificates(content)
108
+ content.split(/(?<=END CERTIFICATE-----)\r?\n/)
109
+ end
110
+
111
+ # Extract intermediate certificate
112
+ #
113
+ # Normally the second certificate item is intermediate certificate
114
+ # but in some rare case digicert responds with four certificate, so
115
+ # this method will also check for the length of the responses and
116
+ # it will build an array if necessary.
117
+ #
118
+ def extract_intermediate(certificates)
119
+ certificate = certificates[1]
120
+
121
+ if certificates.length > 3
122
+ certificate = [certificate, certificates[2]]
123
+ end
124
+
125
+ certificate
126
+ end
127
+
128
+ def write_to_path(content, path:, extension:)
129
+ filename = ["certificate", extension].join(".")
130
+ file_with_path = [path, filename].join("/")
131
+
132
+ File.open(file_with_path, "w") do |file|
133
+ file.write(content)
134
+ end
135
+ end
136
+ end
137
+ end