buildpack-support 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/LICENSE +202 -0
  2. data/NOTICE +2 -0
  3. data/docs/cache.md +77 -0
  4. data/docs/component.md +1 -0
  5. data/docs/configuration.md +27 -0
  6. data/docs/logging.md +54 -0
  7. data/docs/other.md +1 -0
  8. data/docs/rake.md +1 -0
  9. data/docs/repository.md +116 -0
  10. data/docs/test.md +1 -0
  11. data/lib/buildpack_support.rb +18 -0
  12. data/lib/buildpack_support/base_buildpack.rb +166 -0
  13. data/lib/buildpack_support/buildpack_version.rb +124 -0
  14. data/lib/buildpack_support/cache.rb +24 -0
  15. data/lib/buildpack_support/cache/application_cache.rb +41 -0
  16. data/lib/buildpack_support/cache/cached_file.rb +103 -0
  17. data/lib/buildpack_support/cache/download_cache.rb +280 -0
  18. data/lib/buildpack_support/cache/inferred_network_failure.rb +26 -0
  19. data/lib/buildpack_support/cache/internet_availability.rb +64 -0
  20. data/lib/buildpack_support/component.rb +24 -0
  21. data/lib/buildpack_support/component/application.rb +76 -0
  22. data/lib/buildpack_support/component/base_component.rb +78 -0
  23. data/lib/buildpack_support/component/base_droplet.rb +96 -0
  24. data/lib/buildpack_support/component/downloads.rb +88 -0
  25. data/lib/buildpack_support/component/services.rb +84 -0
  26. data/lib/buildpack_support/component/versioned_dependency_component.rb +71 -0
  27. data/lib/buildpack_support/component/versioned_downloads.rb +57 -0
  28. data/lib/buildpack_support/component/with_timing.rb +40 -0
  29. data/lib/buildpack_support/configuration_utils.rb +58 -0
  30. data/lib/buildpack_support/constantize.rb +46 -0
  31. data/lib/buildpack_support/dash_case.rb +29 -0
  32. data/lib/buildpack_support/directory_finder.rb +45 -0
  33. data/lib/buildpack_support/filtering_pathname.rb +227 -0
  34. data/lib/buildpack_support/format_duration.rb +57 -0
  35. data/lib/buildpack_support/logging.rb +22 -0
  36. data/lib/buildpack_support/logging/delegating_logger.rb +48 -0
  37. data/lib/buildpack_support/logging/logger_factory.rb +148 -0
  38. data/lib/buildpack_support/qualify_path.rb +36 -0
  39. data/lib/buildpack_support/rake.rb +22 -0
  40. data/lib/buildpack_support/rake/buildpack_stage_task.rb +86 -0
  41. data/lib/buildpack_support/rake/cached_artifact_finder.rb +99 -0
  42. data/lib/buildpack_support/rake/check_api_doc_task.rb +70 -0
  43. data/lib/buildpack_support/rake/dependency_cache_task.rb +87 -0
  44. data/lib/buildpack_support/rake/disable_remote_downloads_task.rb +80 -0
  45. data/lib/buildpack_support/rake/package_task.rb +133 -0
  46. data/lib/buildpack_support/rake/package_zip_task.rb +80 -0
  47. data/lib/buildpack_support/rake/repository_configuration_finder.rb +66 -0
  48. data/lib/buildpack_support/rake/write_version_file_task.rb +82 -0
  49. data/lib/buildpack_support/repository.rb +24 -0
  50. data/lib/buildpack_support/repository/configured_item.rb +81 -0
  51. data/lib/buildpack_support/repository/repository_index.rb +98 -0
  52. data/lib/buildpack_support/repository/wildcard_version_resolver.rb +75 -0
  53. data/lib/buildpack_support/shell.rb +41 -0
  54. data/lib/buildpack_support/snake_case.rb +30 -0
  55. data/lib/buildpack_support/space_case.rb +29 -0
  56. data/lib/buildpack_support/test/application_helper.rb +41 -0
  57. data/lib/buildpack_support/test/base_component_helper.rb +59 -0
  58. data/lib/buildpack_support/test/base_droplet_helper.rb +36 -0
  59. data/lib/buildpack_support/test/console_helper.rb +57 -0
  60. data/lib/buildpack_support/test/environment_helper.rb +32 -0
  61. data/lib/buildpack_support/test/internet_availability_helper.rb +29 -0
  62. data/lib/buildpack_support/test/logging_helper.rb +50 -0
  63. data/lib/buildpack_support/test/scratch_helper.rb +32 -0
  64. data/lib/buildpack_support/test/versioned_dependency_component_helper.rb +32 -0
  65. data/lib/buildpack_support/test/with_load_path_helper.rb +27 -0
  66. data/lib/buildpack_support/to_b.rb +38 -0
  67. data/lib/buildpack_support/tokenized_version.rb +157 -0
  68. data/lib/buildpack_support/version.rb +23 -0
  69. data/spec/buildpack_support/base_buildpack_spec.rb +112 -0
  70. data/spec/buildpack_support/buildpack_version_spec.rb +122 -0
  71. data/spec/buildpack_support/cache/application_cache_spec.rb +56 -0
  72. data/spec/buildpack_support/cache/cached_file_spec.rb +94 -0
  73. data/spec/buildpack_support/cache/download_cache_spec.rb +293 -0
  74. data/spec/buildpack_support/cache/internet_availability_spec.rb +57 -0
  75. data/spec/buildpack_support/cache/yield_file_with_content.rb +30 -0
  76. data/spec/buildpack_support/component/application_spec.rb +81 -0
  77. data/spec/buildpack_support/component/base_component_spec.rb +81 -0
  78. data/spec/buildpack_support/component/base_droplet_spec.rb +72 -0
  79. data/spec/buildpack_support/component/downloads_spec.rb +63 -0
  80. data/spec/buildpack_support/component/services_spec.rb +80 -0
  81. data/spec/buildpack_support/component/versioned_dependency_component_spec.rb +58 -0
  82. data/spec/buildpack_support/component/versioned_downloads_spec.rb +58 -0
  83. data/spec/buildpack_support/component/with_timing_spec.rb +30 -0
  84. data/spec/buildpack_support/configuration_utils_spec.rb +39 -0
  85. data/spec/buildpack_support/constantize_spec.rb +34 -0
  86. data/spec/buildpack_support/dash_case_spec.rb +41 -0
  87. data/spec/buildpack_support/directory_finder_spec.rb +41 -0
  88. data/spec/buildpack_support/filtering_pathname_spec.rb +443 -0
  89. data/spec/buildpack_support/format_duration_spec.rb +60 -0
  90. data/spec/buildpack_support/logging/delegating_logger_spec.rb +62 -0
  91. data/spec/buildpack_support/logging/logger_factory_spec.rb +262 -0
  92. data/spec/buildpack_support/qualify_path_spec.rb +42 -0
  93. data/spec/buildpack_support/rake/buildpack_stage_task_spec.rb +88 -0
  94. data/spec/buildpack_support/rake/cached_artifact_finder_spec.rb +73 -0
  95. data/spec/buildpack_support/rake/check_api_doc_task_spec.rb +69 -0
  96. data/spec/buildpack_support/rake/dependency_cache_task_spec.rb +133 -0
  97. data/spec/buildpack_support/rake/disable_remote_downloads_task_spec.rb +91 -0
  98. data/spec/buildpack_support/rake/package_task_spec.rb +335 -0
  99. data/spec/buildpack_support/rake/package_zip_task_spec.rb +91 -0
  100. data/spec/buildpack_support/rake/repository_configuration_finder_spec.rb +61 -0
  101. data/spec/buildpack_support/rake/write_version_file_task_spec.rb +96 -0
  102. data/spec/buildpack_support/repository/configured_item_spec.rb +78 -0
  103. data/spec/buildpack_support/repository/repository_index_spec.rb +118 -0
  104. data/spec/buildpack_support/repository/wildcard_version_resolver_spec.rb +73 -0
  105. data/spec/buildpack_support/shell_spec.rb +32 -0
  106. data/spec/buildpack_support/snake_case_spec.rb +45 -0
  107. data/spec/buildpack_support/space_case_spec.rb +41 -0
  108. data/spec/buildpack_support/to_b_spec.rb +41 -0
  109. data/spec/buildpack_support/tokenized_version_spec.rb +132 -0
  110. data/spec/fixtures/application/test-file +0 -0
  111. data/spec/fixtures/config/found-config.yml +2 -0
  112. data/spec/fixtures/droplet-resources/droplet-resource +0 -0
  113. data/spec/fixtures/stub-download-with-top-level.zip +0 -0
  114. data/spec/fixtures/stub-download.tar.gz +0 -0
  115. data/spec/fixtures/stub-download.zip +0 -0
  116. data/spec/fixtures/test-cache.yml +18 -0
  117. data/spec/fixtures/test-index.yml +2 -0
  118. data/spec/fixtures/test_component.rb +0 -0
  119. data/spec/fixtures/zip-contents/test-directory/test-deep-file +0 -0
  120. data/spec/fixtures/zip-contents/test-file +0 -0
  121. data/spec/spec_helper.rb +30 -0
  122. metadata +416 -0
data/docs/test.md ADDED
@@ -0,0 +1 @@
1
+ TODO: Document Test support code
@@ -0,0 +1,18 @@
1
+ # Encoding: utf-8
2
+ # Copyright 2013-2014 the original author or authors.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ # A module encapsulating all of the code for the Buildpack Support utilities
17
+ module BuildpackSupport
18
+ end
@@ -0,0 +1,166 @@
1
+ # Encoding: utf-8
2
+ # # Copyright 2013-2014 the original author or authors.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'buildpack_support'
17
+ require 'buildpack_support/space_case'
18
+ require 'buildpack_support/buildpack_version'
19
+ require 'buildpack_support/component/application'
20
+ require 'buildpack_support/logging/logger_factory'
21
+ require 'buildpack_support/snake_case'
22
+
23
+ module BuildpackSupport
24
+
25
+ # A base class for all buildpack implementations to extend from. This implementation provides a number of utility methods for dealing with components.
26
+ class BaseBuildpack
27
+
28
+ # Iterates over all of the components to detect if this buildpack can be used to run an application
29
+ #
30
+ # @return [Array<String>] An array of strings that identify the components and versions that will be used to run
31
+ # this application. If no container can run the application, the array will be empty
32
+ # (+[]+).
33
+ def detect
34
+ fail "Method 'detect' must be defined"
35
+ end
36
+
37
+ # Transforms the application directory such that the engine, and frameworks can run the application
38
+ #
39
+ # @return [Void]
40
+ def compile
41
+ fail "Method 'compile' must be defined"
42
+ end
43
+
44
+ # Generates the payload required to run the application. The payload format is defined by the
45
+ # {Heroku Buildpack API}[https://devcenter.heroku.com/articles/buildpack-api#buildpack-api].
46
+ #
47
+ # @return [String] The payload required to run the application.
48
+ def release
49
+ fail "Method 'release' must be defined"
50
+ end
51
+
52
+ protected
53
+
54
+ # Returns the components that are detected for an application
55
+ #
56
+ # @param [String] type the type of components
57
+ # @param [String] components the candidate components
58
+ # @param [Boolean] unique whether there must be _exactly_ one component detected
59
+ # @return [Array<BuildpackSupport::Component::BaseComponent>] the collection of detected components
60
+ def component_detection(type, components, unique)
61
+ detected, _tags = detection type, components, unique
62
+ detected
63
+ end
64
+
65
+ # Returns the components, and the tags from those components that are detected for an application
66
+ #
67
+ # @param [String] type the type of components
68
+ # @param [Array<BuildpackSupport::Component::BaseComponent>] components the candidate components
69
+ # @param [Boolean] unique whether there must be _exactly_ one component detected
70
+ # @return [Array<BuildpackSupport::Component::BaseComponent>] the collection of detected components
71
+ # @return [Array<String>] the collection of tags from the detected components
72
+ def detection(type, components, unique)
73
+ detected = []
74
+ tags = []
75
+
76
+ components.each do |component|
77
+ result = component.detect
78
+
79
+ next unless result
80
+
81
+ detected << component
82
+ tags << result
83
+ end
84
+
85
+ fail "Application can be run by more than one #{type}: #{names detected}" if unique && detected.size > 1
86
+ [detected, tags]
87
+ end
88
+
89
+ # +require+ a component based on its id
90
+ #
91
+ # @param [Pathname] load_root the +$LOAD_ROOT+ to load the component from
92
+ # @param [String] component the name of the component to load
93
+ def require_component(load_root, component)
94
+ file = load_root + "#{component.snake_case}.rb"
95
+
96
+ if file.exist?
97
+ require component.snake_case
98
+ @logger.debug { "Successfully required #{component}" }
99
+ else
100
+ @logger.debug { "Cannot require #{component} because #{file} does not exist" }
101
+ end
102
+ end
103
+
104
+ # Returns the tags that are detected for an application
105
+ #
106
+ # @param [String] type the type of components
107
+ # @param [String] components the candidate components
108
+ # @param [Boolean] unique whether there must be _exactly_ one component detected
109
+ # @return [Array<String>] the collection of tags from the detected components
110
+ def tag_detection(type, components, unique)
111
+ _detected, tags = detection type, components, unique
112
+ tags
113
+ end
114
+
115
+ private_class_method :new
116
+
117
+ private
118
+
119
+ def initialize(_app_dir, _application)
120
+ @logger = Logging::LoggerFactory.instance.get_logger self.class
121
+ @buildpack_version = BuildpackVersion.new
122
+
123
+ @logger.debug { "Environment Variables: #{ENV.to_hash}" }
124
+ end
125
+
126
+ def names(components)
127
+ components.map { |component| component.class.to_s.space_case }.join(', ')
128
+ end
129
+
130
+ class << self
131
+
132
+ # Main entry to the buildpack. Initializes the buildpack and all of its dependencies and yields a new instance
133
+ # to any given block. Any exceptions thrown as part of the buildpack setup or execution are handled
134
+ #
135
+ # @param [String] app_dir the path of the application directory
136
+ # @param [String] message an error message with an insert for the reason for failure
137
+ # @yield [Buildpack] the buildpack to work with
138
+ # @return [Object] the return value from the given block
139
+ def with_buildpack(app_dir, message)
140
+ app_dir = Pathname.new(File.expand_path(app_dir))
141
+ application = Component::Application.new(app_dir)
142
+ Logging::LoggerFactory.instance.setup app_dir
143
+
144
+ yield new(app_dir, application) if block_given?
145
+ rescue => e
146
+ handle_error(e, message)
147
+ end
148
+
149
+ private
150
+
151
+ def handle_error(e, message)
152
+ if Logging::LoggerFactory.instance.initialized
153
+ logger = Logging::LoggerFactory.instance.get_logger BaseBuildpack
154
+
155
+ logger.error { message % e.inspect }
156
+ logger.debug { "Exception #{e.inspect} backtrace:\n#{e.backtrace.join("\n")}" }
157
+ end
158
+
159
+ abort e.message
160
+ end
161
+
162
+ end
163
+
164
+ end
165
+
166
+ end
@@ -0,0 +1,124 @@
1
+ # Encoding: utf-8
2
+ # # Copyright 2013-2014 the original author or authors.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'buildpack_support'
17
+ require 'buildpack_support/configuration_utils'
18
+ require 'buildpack_support/directory_finder'
19
+ require 'buildpack_support/to_b'
20
+
21
+ module BuildpackSupport
22
+
23
+ # A representation of the buildpack's version. The buildpack's version is determined using the following algorithm:
24
+ #
25
+ # 1. using the +config/version.yml+ file if it exists
26
+ # 2. using +git+ to determine the remote and hash if the buildpack is in a git repository
27
+ # 3. unknown
28
+ class BuildpackVersion
29
+ extend DirectoryFinder
30
+
31
+ # @!attribute [r] hash
32
+ # @return [String, nil] the Git hash of this version, or +nil+ if it cannot be determined
33
+ attr_reader :hash
34
+
35
+ # @!attribute [r] offline
36
+ # @return [Boolean] +true+ if the buildpack is offline, +false+ otherwise
37
+ attr_reader :offline
38
+
39
+ # @!attribute [r] remote
40
+ # @return [String, nil] the Git remote of this version, or +nil+ if it cannot be determined
41
+ attr_reader :remote
42
+
43
+ # @!attribute [r] version
44
+ # @return [String, nil] the version name of this version, or +nil+ if it cannot be determined
45
+ attr_reader :version
46
+
47
+ # Creates a new instance
48
+ def initialize(should_log = true)
49
+ configuration = ConfigurationUtils.new(should_log).load 'version'
50
+ @hash = configuration['hash'] || hash
51
+ @offline = configuration['offline'] || ENV['OFFLINE'].to_b
52
+ @remote = configuration['remote'] || remote
53
+ @version = configuration['version'] || ENV['VERSION'] || @hash
54
+
55
+ return unless should_log
56
+
57
+ logger = Logging::LoggerFactory.instance.get_logger BuildpackVersion
58
+ logger.debug { to_s }
59
+ end
60
+
61
+ # Returns a +Hash+ representation of the buildpack version.
62
+ #
63
+ # @return [Hash] a representation of the buildpack version
64
+ def to_hash
65
+ h = {}
66
+
67
+ h['hash'] = @hash if @hash
68
+ h['offline'] = @offline if @offline
69
+ h['remote'] = @remote if @remote
70
+ h['version'] = @version if @version
71
+
72
+ h
73
+ end
74
+
75
+ # Creates a string representation of the version. The string representation looks like the following:
76
+ # +[[<VERSION> [(offline)] | ] <REMOTE>#<HASH>] | [unknown]+. Some examples:
77
+ #
78
+ # +2.1.2 (offline) | https://github.com/cloudfoundry/java-buildpack.git#12345+ (custom version number, offline buildpack)
79
+ # +abcde | https://github.com/cloudfoundry/java-buildpack.git#abcde+ (default version number, online buildpack)
80
+ # +https://github.com/cloudfoundry/java-buildpack#12345+ (cloned buildpack)
81
+ # +unknown+ (un-packaged, un-cloned)
82
+ #
83
+ # @param [Boolean] human_readable whether the output should be human readable or machine readable
84
+ # @return [String] a +String+ representation of the version
85
+ def to_s(human_readable = true)
86
+ s = []
87
+ s << @version if @version
88
+ s << (human_readable ? '(offline)' : 'offline') if @offline
89
+ s << '|' if @version && human_readable
90
+ s << "#{@remote}##{@hash}" if @remote && @hash
91
+ s << 'unknown' if s.empty?
92
+
93
+ s.join(human_readable ? ' ' : '-')
94
+ end
95
+
96
+ private
97
+
98
+ GIT_DIR = BuildpackVersion.load_path_peer('.git').freeze
99
+
100
+ private_constant :GIT_DIR
101
+
102
+ def git(command)
103
+ `git --git-dir=#{GIT_DIR} #{command}`.chomp if git? && git_dir?
104
+ end
105
+
106
+ def git?
107
+ system 'which git > /dev/null'
108
+ end
109
+
110
+ def git_dir?
111
+ GIT_DIR.exist?
112
+ end
113
+
114
+ def hash
115
+ git 'rev-parse --short HEAD'
116
+ end
117
+
118
+ def remote
119
+ git 'config --get remote.origin.url'
120
+ end
121
+
122
+ end
123
+
124
+ end
@@ -0,0 +1,24 @@
1
+ # Encoding: utf-8
2
+ # Copyright 2013-2014 the original author or authors.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'buildpack_support'
17
+
18
+ module BuildpackSupport
19
+
20
+ # A module encapsulating all of the code for the cache utilities
21
+ module Cache
22
+ end
23
+
24
+ end
@@ -0,0 +1,41 @@
1
+ # Encoding: utf-8
2
+ # Copyright 2013-2014 the original author or authors.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'buildpack_support/cache'
17
+ require 'buildpack_support/cache/download_cache'
18
+
19
+ module BuildpackSupport
20
+ module Cache
21
+
22
+ # An extension of {DownloadCache} that is configured to use the application cache. The application
23
+ # cache location is defined by the second argument (<tt>ARGV[1]</tt>) to the +compile+ script.
24
+ #
25
+ # <b>WARNING: This cache should only by used by code run by the +compile+ script</b>
26
+ class ApplicationCache < DownloadCache
27
+
28
+ # Creates an instance that is configured to use the application cache. The application cache location is defined
29
+ # by the second argument (<tt>ARGV[1]</tt>) to the +compile+ script.
30
+ #
31
+ # @raise if the second argument (<tt>ARGV[1]</tt>) to the +compile+ script is +nil+
32
+ def initialize
33
+ application_cache_directory = ARGV[1]
34
+ fail 'Application cache directory is undefined' if application_cache_directory.nil?
35
+ super(Pathname.new(application_cache_directory), CACHED_RESOURCES_DIRECTORY)
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,103 @@
1
+ # Encoding: utf-8
2
+ # Copyright 2013-2014 the original author or authors.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'fileutils'
17
+ require 'buildpack_support/cache'
18
+
19
+ module BuildpackSupport
20
+ module Cache
21
+
22
+ # Represents a file cached on a filesystem
23
+ #
24
+ # Note: this class is thread-safe, however access to the cached files is not
25
+ class CachedFile
26
+
27
+ # Creates an instance of the cached file. Files created and expected by this class will all be rooted at
28
+ # +cache_root+.
29
+ #
30
+ # @param [Pathname] cache_root the filesystem root for the file created and expected by this class
31
+ # @param [String] uri a uri which uniquely identifies the file in the cache
32
+ def initialize(cache_root, uri)
33
+ FileUtils.mkdir_p cache_root
34
+
35
+ key = URI.escape(uri, '/')
36
+ @cached = cache_root + "#{key}.cached"
37
+ @etag = cache_root + "#{key}.etag"
38
+ @last_modified = cache_root + "#{key}.last_modified"
39
+ end
40
+
41
+ # Opens the cached file
42
+ #
43
+ # @param [String, integer] mode_enc the mode to open the file in. Can be a string like +"r"+ or an integer like
44
+ # +File::CREAT | File::WRONLY+.
45
+ # @param [Array] additional_args any additional arguments to be passed to the block
46
+ # @yield [file, additional_args] the cached file and any additional arguments passed in
47
+ # @return [Void]
48
+ def cached(mode_enc, *additional_args, &block)
49
+ @cached.open(mode_enc) { |f| block.call f, *additional_args }
50
+ end
51
+
52
+ # Returns whether or not data is cached.
53
+ #
54
+ # @return [Boolean] +true+ if and only if data is cached
55
+ def cached?
56
+ @cached.exist?
57
+ end
58
+
59
+ # Destroys the cached file
60
+ def destroy
61
+ [@cached, @etag, @last_modified].each { |f| f.delete if f.exist? }
62
+ end
63
+
64
+ # Opens the etag file
65
+ #
66
+ # @param [String, integer] mode_enc the mode to open the file in. Can be a string like +"r"+ or an integer like
67
+ # +File::CREAT | File::WRONLY+.
68
+ # @param [Array] additional_args any additional arguments to be passed to the block
69
+ # @yield [file] the etag file
70
+ # @return [Void]
71
+ def etag(mode_enc, *additional_args, &block)
72
+ @etag.open(mode_enc) { |f| block.call f, *additional_args }
73
+ end
74
+
75
+ # Returns whether or not an etag is stored.
76
+ #
77
+ # @return [Boolean] +true+ if and only if an etag is stored
78
+ def etag?
79
+ @etag.exist?
80
+ end
81
+
82
+ # Opens the last modified file
83
+ #
84
+ # @param [String, integer] mode_enc the mode to open the file in. Can be a string like +"r"+ or an integer like
85
+ # +File::CREAT | File::WRONLY+.
86
+ # @param [Array] additional_args any additional arguments to be passed to the block
87
+ # @yield [file] the last modified file
88
+ # @return [Void]
89
+ def last_modified(mode_enc, *additional_args, &block)
90
+ @last_modified.open(mode_enc) { |f| block.call f, *additional_args }
91
+ end
92
+
93
+ # Returns whether or not a last modified time stamp is stored.
94
+ #
95
+ # @return [Boolean] +true+ if and only if a last modified time stamp is stored
96
+ def last_modified?
97
+ @last_modified.exist?
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+ end