utopia 0.12.6 → 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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -2
  3. data/Gemfile +6 -0
  4. data/README.md +48 -14
  5. data/Rakefile +5 -0
  6. data/bin/utopia +132 -15
  7. data/lib/utopia.rb +13 -10
  8. data/lib/utopia/content.rb +140 -0
  9. data/lib/utopia/content/link.rb +124 -0
  10. data/lib/utopia/content/links.rb +228 -0
  11. data/lib/utopia/content/node.rb +387 -0
  12. data/lib/utopia/content/processor.rb +128 -0
  13. data/lib/utopia/content/tag.rb +102 -0
  14. data/lib/utopia/controller.rb +137 -0
  15. data/lib/utopia/controller/action.rb +112 -0
  16. data/lib/utopia/controller/base.rb +174 -0
  17. data/lib/utopia/{middleware/controller → controller}/variables.rb +36 -38
  18. data/lib/utopia/exception_handler.rb +79 -0
  19. data/lib/utopia/extensions/array.rb +2 -2
  20. data/lib/utopia/localization.rb +143 -0
  21. data/lib/utopia/mail_exceptions.rb +136 -0
  22. data/lib/utopia/middleware.rb +7 -22
  23. data/lib/utopia/path.rb +150 -60
  24. data/lib/utopia/redirector.rb +152 -0
  25. data/lib/utopia/{extensions/hash.rb → session.rb} +4 -6
  26. data/lib/utopia/session/encrypted_cookie.rb +46 -48
  27. data/lib/utopia/{middleware/directory_index.rb → session/lazy_hash.rb} +44 -27
  28. data/lib/utopia/static.rb +255 -0
  29. data/lib/utopia/tags/deferred.rb +12 -8
  30. data/lib/utopia/tags/environment.rb +18 -6
  31. data/lib/utopia/tags/node.rb +12 -8
  32. data/lib/utopia/tags/override.rb +12 -12
  33. data/lib/utopia/version.rb +1 -1
  34. data/setup/.bowerrc +3 -0
  35. data/{lib/utopia/setup → setup}/Gemfile +1 -1
  36. data/setup/Rakefile +4 -0
  37. data/{lib/utopia/setup → setup}/cache/head/readme.txt +0 -0
  38. data/{lib/utopia/setup → setup}/cache/meta/readme.txt +0 -0
  39. data/setup/config.ru +64 -0
  40. data/{lib/utopia/setup → setup}/lib/readme.txt +0 -0
  41. data/{lib/utopia/setup → setup}/pages/_heading.xnode +0 -0
  42. data/{lib/utopia/setup → setup}/pages/_page.xnode +1 -1
  43. data/{lib/utopia/setup → setup}/pages/_static/icon.png +0 -0
  44. data/setup/pages/_static/site.css +70 -0
  45. data/{lib/utopia/setup → setup}/pages/errors/exception.xnode +0 -0
  46. data/{lib/utopia/setup → setup}/pages/errors/file-not-found.xnode +0 -0
  47. data/{lib/utopia/setup → setup}/pages/links.yaml +0 -0
  48. data/setup/pages/welcome/index.xnode +17 -0
  49. data/{lib/utopia/setup → setup}/public/readme.txt +0 -0
  50. data/spec/utopia/content/link_spec.rb +108 -0
  51. data/spec/utopia/content/links/foo/index.xnode +0 -0
  52. data/spec/utopia/content/links/foo/links.yaml +2 -0
  53. data/spec/utopia/content/links/foo/test.de.xnode +0 -0
  54. data/spec/utopia/content/links/foo/test.en.xnode +0 -0
  55. data/spec/utopia/content/links/links.yaml +9 -0
  56. data/spec/utopia/content/links/welcome.xnode +0 -0
  57. data/spec/utopia/content/localized/five/index.en.xnode +0 -0
  58. data/spec/utopia/content/localized/four/index.en.xnode +0 -0
  59. data/spec/utopia/content/localized/four/index.zh.xnode +0 -0
  60. data/spec/utopia/content/localized/four/links.yaml +4 -0
  61. data/spec/utopia/content/localized/links.yaml +16 -0
  62. data/spec/utopia/content/localized/one.xnode +0 -0
  63. data/spec/utopia/content/localized/three/index.xnode +0 -0
  64. data/spec/utopia/content/localized/two.en.xnode +0 -0
  65. data/spec/utopia/content/localized/two.zh.xnode +0 -0
  66. data/spec/utopia/content/node/ordered/first.xnode +0 -0
  67. data/spec/utopia/content/node/ordered/index.xnode +0 -0
  68. data/spec/utopia/content/node/ordered/links.yaml +4 -0
  69. data/spec/utopia/content/node/ordered/second.xnode +0 -0
  70. data/spec/utopia/content/node/related/foo.en.xnode +0 -0
  71. data/spec/utopia/content/node/related/foo.ja.xnode +0 -0
  72. data/spec/utopia/content/node/related/links.yaml +4 -0
  73. data/spec/utopia/content/node_spec.rb +63 -0
  74. data/spec/utopia/{middleware/content_spec.rb → content/processor_spec.rb} +34 -23
  75. data/spec/utopia/content_spec.rb +87 -0
  76. data/spec/utopia/content_spec.ru +10 -0
  77. data/spec/utopia/{middleware/controller_spec.rb → controller_spec.rb} +61 -16
  78. data/spec/utopia/controller_spec.ru +4 -0
  79. data/spec/utopia/extensions_spec.rb +6 -17
  80. data/spec/utopia/localization_spec.rb +60 -0
  81. data/spec/utopia/localization_spec.ru +11 -0
  82. data/{lib/utopia/tags.rb → spec/utopia/middleware_spec.rb} +8 -14
  83. data/spec/utopia/{middleware/content_root → pages}/_heading.xnode +0 -0
  84. data/spec/utopia/pages/content/_show-value.xnode +1 -0
  85. data/spec/utopia/pages/content/test-partial.xnode +1 -0
  86. data/spec/utopia/pages/controller/controller.rb +28 -0
  87. data/spec/utopia/pages/controller/index.xnode +1 -0
  88. data/spec/utopia/pages/controller/nested/controller.rb +4 -0
  89. data/spec/utopia/{middleware/content_root → pages}/index.xnode +0 -0
  90. data/spec/utopia/pages/localized.de.txt +1 -0
  91. data/spec/utopia/pages/localized.en.txt +1 -0
  92. data/spec/utopia/pages/localized.jp.txt +1 -0
  93. data/spec/utopia/pages/node/index.xnode +1 -0
  94. data/spec/utopia/pages/test.txt +1 -0
  95. data/spec/utopia/path_spec.rb +109 -0
  96. data/spec/utopia/rack_spec.rb +2 -0
  97. data/spec/utopia/session_spec.rb +82 -0
  98. data/spec/utopia/session_spec.ru +20 -0
  99. data/spec/utopia/spec_helper.rb +16 -0
  100. data/{lib/utopia/extensions/string.rb → spec/utopia/static_spec.rb} +24 -15
  101. data/spec/utopia/static_spec.ru +4 -0
  102. data/utopia.gemspec +3 -3
  103. metadata +138 -54
  104. data/lib/utopia/extensions/regexp.rb +0 -33
  105. data/lib/utopia/link.rb +0 -288
  106. data/lib/utopia/middleware/all.rb +0 -33
  107. data/lib/utopia/middleware/content.rb +0 -157
  108. data/lib/utopia/middleware/content/node.rb +0 -386
  109. data/lib/utopia/middleware/content/processor.rb +0 -123
  110. data/lib/utopia/middleware/controller.rb +0 -130
  111. data/lib/utopia/middleware/controller/action.rb +0 -121
  112. data/lib/utopia/middleware/controller/base.rb +0 -184
  113. data/lib/utopia/middleware/exception_handler.rb +0 -80
  114. data/lib/utopia/middleware/localization.rb +0 -147
  115. data/lib/utopia/middleware/localization/name.rb +0 -69
  116. data/lib/utopia/middleware/mail_exceptions.rb +0 -138
  117. data/lib/utopia/middleware/redirector.rb +0 -146
  118. data/lib/utopia/middleware/requester.rb +0 -126
  119. data/lib/utopia/middleware/static.rb +0 -295
  120. data/lib/utopia/setup.rb +0 -60
  121. data/lib/utopia/setup/config.ru +0 -47
  122. data/lib/utopia/setup/pages/_static/background.png +0 -0
  123. data/lib/utopia/setup/pages/_static/site.css +0 -48
  124. data/lib/utopia/setup/pages/welcome/index.xnode +0 -7
  125. data/lib/utopia/tag.rb +0 -105
  126. data/lib/utopia/tags/all.rb +0 -34
@@ -1,126 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require 'utopia/middleware'
22
-
23
- require 'set'
24
-
25
- module Utopia
26
- module Middleware
27
-
28
- class Requester
29
- TTL_KEY = "utopia.requestor.ttl"
30
- REQUESTOR_KEY = "utopia.requestor"
31
- PATH_VARIABLES = Set.new
32
- MAXIMUM_DEPTH = 5
33
-
34
- class Response
35
- def initialize(status, headers, body)
36
- @status = status
37
- @headers = headers
38
- @body = body
39
- end
40
-
41
- attr :status
42
- attr :headers
43
-
44
- def body
45
- unless @body_string
46
- buffer = StringIO.new
47
-
48
- @body.each do |string|
49
- buffer.write(string)
50
- end
51
-
52
- @body_string = buffer.string
53
- end
54
-
55
- return @body_string
56
- end
57
-
58
- def [](key)
59
- return @headers[key]
60
- end
61
-
62
- def okay?
63
- @status == 200
64
- end
65
- end
66
-
67
- class NoRequesterError < ArgumentError
68
- end
69
-
70
- def self.[](env)
71
- requestor = env[REQUESTOR_KEY]
72
- if requestor
73
- return requestor
74
- else
75
- raise NoRequesterError
76
- end
77
- end
78
-
79
- def initialize(app, env = {})
80
- @app = app
81
- @env = env
82
- @env[TTL_KEY] = 0
83
- end
84
-
85
- attr :env, true
86
-
87
- def call(env)
88
- requestor = dup
89
- env[REQUESTOR_KEY] = requestor
90
- requestor.env = env.merge(@env)
91
- requestor.env.delete("rack.request")
92
-
93
- @app.call(env)
94
- end
95
-
96
- class RecursiveRequestError < StandardError
97
- end
98
-
99
- def request(env)
100
- env = @env.merge(env)
101
- env[TTL_KEY] += 1
102
-
103
- if env[TTL_KEY].to_i > MAXIMUM_DEPTH
104
- raise RecursiveRequestError, env["PATH_INFO"]
105
- end
106
-
107
- return Response.new(*@app.call(env))
108
- end
109
-
110
- def [](path, method = "GET", env = {})
111
- path_env = {
112
- "REQUEST_METHOD" => method,
113
- "PATH_INFO" => path.to_s
114
- }
115
- request(env.merge(path_env))
116
- end
117
-
118
- # Avoid a huge amount of junk being printed when inspecting +env+.
119
- def inspect
120
- to_s
121
- end
122
- end
123
-
124
- end
125
- end
126
-
@@ -1,295 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require 'utopia/middleware'
22
- require 'utopia/path'
23
-
24
- require 'time'
25
-
26
- require 'digest/sha1'
27
- require 'mime/types'
28
-
29
- module Utopia
30
- module Middleware
31
-
32
- class Static
33
- MIME_TYPES = {
34
- :xiph => {
35
- "ogx" => "application/ogg",
36
- "ogv" => "video/ogg",
37
- "oga" => "audio/ogg",
38
- "ogg" => "audio/ogg",
39
- "spx" => "audio/ogg",
40
- "flac" => "audio/flac",
41
- "anx" => "application/annodex",
42
- "axa" => "audio/annodex",
43
- "xspf" => "application/xspf+xml",
44
- },
45
- :media => [
46
- :xiph, "mp3", "mp4", "wav", "aiff", ["aac", "audio/x-aac"], "mov", "avi", "wmv", "mpg"
47
- ],
48
- :text => [
49
- "html", "css", "js", "txt", "rtf", "xml", "pdf"
50
- ],
51
- :fonts => [
52
- "otf", ["eot", "application/vnd.ms-fontobject"], "ttf", "woff"
53
- ],
54
- :archive => [
55
- "zip", "tar", "tgz", "tar.gz", "tar.bz2", ["dmg", "application/x-apple-diskimage"],
56
- ["torrent", "application/x-bittorrent"]
57
- ],
58
- :images => [
59
- "png", "gif", "jpeg", "tiff", "svg"
60
- ],
61
- :default => [
62
- :media, :text, :archive, :images, :fonts
63
- ]
64
- }
65
-
66
- private
67
-
68
- class LocalFile
69
- def initialize(root, path)
70
- @root = root
71
- @path = path
72
- @etag = Digest::SHA1.hexdigest("#{File.size(full_path)}#{mtime_date}")
73
-
74
- @range = nil
75
- end
76
-
77
- attr :root
78
- attr :path
79
- attr :etag
80
- attr :range
81
-
82
- # Fit in with Rack::Sendfile
83
- def to_path
84
- full_path
85
- end
86
-
87
- def full_path
88
- File.join(@root, @path.components)
89
- end
90
-
91
- def mtime_date
92
- File.mtime(full_path).httpdate
93
- end
94
-
95
- def size
96
- File.size(full_path)
97
- end
98
-
99
- def each
100
- File.open(full_path, "rb") do |file|
101
- file.seek(@range.begin)
102
- remaining = @range.end - @range.begin+1
103
-
104
- while remaining > 0
105
- break unless part = file.read([8192, remaining].min)
106
-
107
- remaining -= part.length
108
-
109
- yield part
110
- end
111
- end
112
- end
113
-
114
- def modified?(env)
115
- if modified_since = env['HTTP_IF_MODIFIED_SINCE']
116
- return false if File.mtime(full_path) <= Time.parse(modified_since)
117
- end
118
-
119
- if etags = env['HTTP_IF_NONE_MATCH']
120
- etags = etags.split(/\s*,\s*/)
121
- return false if etags.include?(etag) || etags.include?('*')
122
- end
123
-
124
- return true
125
- end
126
-
127
- def serve(env, response_headers)
128
- ranges = Rack::Utils.byte_ranges(env, size)
129
- response = [200, response_headers, self]
130
-
131
- # LOG.info("Requesting ranges: #{ranges.inspect} (#{size})")
132
-
133
- if ranges.nil? || ranges.length > 1
134
- # No ranges, or multiple ranges (which we don't support):
135
- # TODO: Support multiple byte-ranges
136
- response[0] = 200
137
- response[1]["Content-Length"] = size.to_s
138
- @range = 0..size-1
139
- elsif ranges.empty?
140
- # Unsatisfiable. Return error, and file size:
141
- response = Middleware::failure(416, "Invalid range specified.")
142
- response[1]["Content-Range"] = "bytes */#{size}"
143
- return response
144
- else
145
- # Partial content:
146
- @range = ranges[0]
147
- response[0] = 206
148
- response[1]["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}"
149
- response[1]["Content-Length"] = (@range.end - @range.begin+1).to_s
150
- size = @range.end - @range.begin + 1
151
- end
152
-
153
- # LOG.info("Response for #{self.full_path}: #{response.inspect}")
154
- LOG.info "Serving file #{full_path.inspect}, range #{@range.inspect}"
155
-
156
- return response
157
- end
158
- end
159
-
160
- def load_mime_types(types)
161
- result = {}
162
-
163
- extract_extensions = lambda do |mime_type|
164
- # LOG.info "Extracting #{mime_type.inspect}"
165
- mime_type.extensions.each{|ext| result["." + ext] = mime_type.content_type}
166
- end
167
-
168
- types.each do |type|
169
- current_count = result.size
170
- # LOG.info "Processing #{type.inspect}"
171
-
172
- begin
173
- case type
174
- when Symbol
175
- result = load_mime_types(MIME_TYPES[type]).merge(result)
176
- when Array
177
- result["." + type[0]] = type[1]
178
- when String
179
- mt = MIME::Types.of(type).select{|mt| !mt.obsolete?}.each do |mt|
180
- extract_extensions.call(mt)
181
- end
182
- when Regexp
183
- MIME::Types[type].select{|mt| !mt.obsolete?}.each do |mt|
184
- extract_extensions.call(mt)
185
- end
186
- when MIME::Type
187
- extract_extensions.call(type)
188
- end
189
- rescue
190
- LOG.error "#{self.class.name}: Error while processing #{type.inspect}!"
191
- raise $!
192
- end
193
-
194
- if result.size == current_count
195
- LOG.warn "#{self.class.name}: Could not find any mime type for #{type.inspect}"
196
- end
197
- end
198
-
199
- return result
200
- end
201
-
202
- public
203
-
204
- def initialize(app, options = {})
205
- @app = app
206
- @root = options[:root] || Utopia::Middleware::default_root
207
-
208
- if options[:types]
209
- @extensions = load_mime_types(options[:types])
210
- else
211
- @extensions = load_mime_types(MIME_TYPES[:default])
212
- end
213
-
214
- @cache_control = options[:cache_control] || "public, max-age=3600"
215
- end
216
-
217
- def fetch_file(path)
218
- # We need file_path to be an absolute path for X-Sendfile to work correctly.
219
- file_path = File.join(@root, path.components)
220
-
221
- if File.exist?(file_path)
222
- return LocalFile.new(@root, path)
223
- else
224
- return nil
225
- end
226
- end
227
-
228
- def lookup_relative_file(path)
229
- file = nil
230
- name = path.basename
231
-
232
- if split = path.split("@rel@")
233
- path = split[0]
234
- name = split[1].components
235
-
236
- # Fix a problem if the browser request has multiple @rel@
237
- # This normally indicates a browser bug.. :(
238
- name = name.dup
239
- name.delete("@rel@")
240
- else
241
- path = path.dirname
242
-
243
- # Relative lookups are not done unless explicitly required by @rel@
244
- # ... but they do work. This is a performance optimization.
245
- return nil
246
- end
247
-
248
- # LOG.debug("Searching for #{name.inspect} starting in #{path.components}")
249
-
250
- path.ascend do |parent_path|
251
- file_path = File.join(@root, parent_path.components, name)
252
- # LOG.debug("File path: #{file_path}")
253
- if File.exist?(file_path)
254
- return (parent_path + name).to_s
255
- end
256
- end
257
-
258
- return nil
259
- end
260
-
261
- attr :extensions
262
-
263
- def call(env)
264
- request = Rack::Request.new(env)
265
- ext = File.extname(request.path_info)
266
-
267
- if @extensions.key? ext.downcase
268
- path = Path.create(request.path_info).simplify
269
-
270
- if file = fetch_file(path)
271
- response_headers = {
272
- "Last-Modified" => file.mtime_date,
273
- "Content-Type" => @extensions[ext],
274
- "Cache-Control" => @cache_control,
275
- "ETag" => file.etag,
276
- "Accept-Ranges" => "bytes"
277
- }
278
-
279
- if file.modified?(env)
280
- return file.serve(env, response_headers)
281
- else
282
- return [304, response_headers, []]
283
- end
284
- elsif redirect = lookup_relative_file(path)
285
- return [307, {"Location" => redirect}, []]
286
- end
287
- end
288
-
289
- # else if no file was found:
290
- return @app.call(env)
291
- end
292
- end
293
-
294
- end
295
- end
@@ -1,60 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require 'utopia/version'
22
- require 'fileutils'
23
- require 'find'
24
- require 'rake'
25
-
26
- module Utopia
27
- module Setup
28
- ROOT = File.join(File.dirname(__FILE__), "setup", "")
29
- DIRECTORIES = ["access_log", "cache", "cache/meta", "cache/body", "lib", "pages", "public"]
30
-
31
- def self.copy(to, config = {})
32
- $stderr.puts "Copying files from #{ROOT} to #{to}..."
33
- Find.find(ROOT) do |src|
34
- dst = File.join(to, src[ROOT.size..-1])
35
-
36
- if File.directory?(src)
37
- FileUtils.mkdir_p(dst)
38
- else
39
- if File.exist? dst
40
- $stderr.puts "File already exists: #{dst}!"
41
- else
42
- $stderr.puts "Copying #{src} to #{dst}..."
43
- FileUtils.cp(src, dst)
44
- end
45
- end
46
- end
47
-
48
- DIRECTORIES.each do |path|
49
- FileUtils.mkdir_p(File.join(to, path))
50
- end
51
-
52
- ['config.ru', 'Gemfile'].each do |configuration_file|
53
- $stderr.puts "Updating #{configuration_file}..."
54
- path = File.join(to, configuration_file)
55
- buffer = File.read(path).gsub('$UTOPIA_VERSION', Utopia::VERSION)
56
- File.open(path, "w") { |file| file.write(buffer) }
57
- end
58
- end
59
- end
60
- end