utopia 2.12.4 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -4
  3. data/README.md +0 -1
  4. data/bake/utopia.rb +19 -0
  5. data/bake/utopia/shell.rb +16 -0
  6. data/bake/utopia/static.rb +43 -0
  7. data/bake/utopia/test.rb +10 -0
  8. data/bake/utopia/yarn.rb +28 -0
  9. data/{benchmarks → benchmark}/call_vs_check.rb +0 -0
  10. data/{benchmarks → benchmark}/const_vs_hash.rb +0 -0
  11. data/{benchmarks → benchmark}/hash_vs_openstruct.rb +0 -0
  12. data/{benchmarks → benchmark}/string_vs_symbol.rb +0 -0
  13. data/{benchmarks → benchmark}/struct_vs_class.rb +0 -0
  14. data/documentation/Gemfile +0 -7
  15. data/documentation/config.ru +1 -1
  16. data/lib/utopia/command/server.rb +2 -3
  17. data/lib/utopia/command/site.rb +13 -4
  18. data/lib/utopia/content/node.rb +1 -3
  19. data/lib/utopia/content/tags.rb +6 -2
  20. data/lib/utopia/logger.rb +1 -1
  21. data/lib/utopia/session.rb +1 -3
  22. data/lib/utopia/setup.rb +87 -46
  23. data/lib/utopia/shell.rb +51 -0
  24. data/lib/utopia/version.rb +1 -1
  25. data/setup/server/git/hooks/post-receive +4 -2
  26. data/setup/site/Gemfile +2 -3
  27. data/setup/site/bake.rb +16 -0
  28. data/setup/site/config.ru +8 -6
  29. data/setup/site/config/environment.rb +1 -3
  30. data/setup/site/pages/welcome/index.xnode +1 -1
  31. data/setup/site/spec/spec_helper.rb +3 -1
  32. data/setup/site/spec/website_spec.rb +2 -2
  33. data/spec/utopia/command_spec.rb +4 -4
  34. data/spec/utopia/content/tags_spec.rb +3 -3
  35. data/spec/utopia/performance_spec/config.ru +5 -3
  36. data/spec/utopia/redirection_spec.ru +3 -2
  37. data/spec/utopia/setup_spec.rb +17 -11
  38. data/utopia.gemspec +4 -1
  39. metadata +44 -16
  40. data/setup/site/Rakefile +0 -9
  41. data/setup/site/tasks/deploy.rake +0 -13
  42. data/setup/site/tasks/development.rake +0 -33
  43. data/setup/site/tasks/environment.rake +0 -18
  44. data/setup/site/tasks/log.rake +0 -20
  45. data/setup/site/tasks/static.rake +0 -44
  46. data/setup/site/tasks/yarn.rake +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9337b078fadd4ee1864f180fcf809aa1d0474dccf787d58bde6d1456250c53ea
4
- data.tar.gz: 8235f4bb0c2a7c80888a2db6aec97e9c4975037f8e2ef375884dd6ca790d3944
3
+ metadata.gz: b240d4136827fcc12241cfc1832cd83480dfee29799b2ebe88e6dd93e0e80285
4
+ data.tar.gz: 9adc393d85d0a5fa16e2821110a726432590cc284ff3a7d09f592a6e722a4e4b
5
5
  SHA512:
6
- metadata.gz: d54248e9cbaf387b609f076f7c1bdb4b2b4dee6a9759a64d6d519d31c53b034cc01349e3990e7cbe199aad47198bbca6d0e2a7ac8c381b56eca24dee71a63b9d
7
- data.tar.gz: e148586d9ac9ead60a77c0a539bef13ffa4ace1e5027311a82b9f4333b343e4ab7ee39db90313500133f76c3689227c37005507b68334ee7da3183017bcd244d
6
+ metadata.gz: 04e639d5368fd47d89d0cbd8f8c9702e9630a6993876fe25661245fac16c67e8185a2ced3da82b0a1957d1a018e3de7f6b41f7f452eb17f033400a8c8fe235a7
7
+ data.tar.gz: 535ab13548bb9a19865361af11d29bfe661a9a00545fa5417ae96f2f21ee02bd2e9c3bbdbc3a7c37c9a2056e2ad42eee704b74a659f70244b7973528f1d570b3
data/Gemfile CHANGED
@@ -6,9 +6,6 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
 
8
8
  group :development do
9
- gem 'pry'
10
- gem 'pry-coolline'
11
-
12
9
  gem 'yard'
13
10
 
14
11
  gem 'kramdown'
@@ -26,5 +23,6 @@ group :test do
26
23
  gem 'guard-falcon'
27
24
 
28
25
  gem 'rack-test'
29
- gem 'rack-freeze', '~> 1.2'
30
26
  end
27
+
28
+ gem "thread-local", "~> 1.0"
data/README.md CHANGED
@@ -57,7 +57,6 @@ There is an excellent [documentation wiki](https://ioquatix.github.io/utopia/) i
57
57
  - [Trenni::Formatters](https://github.com/ioquatix/trenni-formatters) — Helpers for HTML generation including views and forms.
58
58
  - [Utopia::Gallery](https://github.com/ioquatix/utopia-gallery) — A fast photo gallery based on [libvips](https://github.com/jcupitt/libvips).
59
59
  - [Utopia::Analytics](https://github.com/ioquatix/utopia-analytics) — Simple integration with Google Analytics.
60
- - [Rack::Freeze](https://github.com/ioquatix/rack-freeze) — Multi-thread safety in Rack.
61
60
  - [HTTP::Accept](https://github.com/ioquatix/http-accept) — RFC compliant header parser.
62
61
  - [Samovar](https://github.com/ioquatix/samovar) — Command line parser used by Utopia.
63
62
  - [Mapping](https://github.com/ioquatix/mapping) — Provide structured conversions for web interfaces.
data/bake/utopia.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Set up the environment for the web application.
4
+ def environment(name: nil)
5
+ require_relative '../lib/utopia/logger'
6
+
7
+ if name
8
+ ENV['UTOPIA_ENV'] = name
9
+ end
10
+
11
+ require File.expand_path('config/environment', context.root)
12
+ end
13
+
14
+ # Start the development server.
15
+ def development
16
+ self.environment
17
+
18
+ exec('guard', '-g', 'development')
19
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Start an interactive console for the web application.
4
+ def shell
5
+ call 'utopia:environment'
6
+
7
+ require 'utopia/shell'
8
+
9
+ binding = Utopia::Shell.new(self.context).binding
10
+
11
+ IRB.setup(binding.source_location[0], argv: [])
12
+ workspace = IRB::WorkSpace.new(binding)
13
+
14
+ irb = IRB::Irb.new(workspace)
15
+ irb.run(IRB.conf)
16
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generate a static copy of the site.
4
+ # @param output_path [String] the path where the files for the static site will be created.
5
+ def generate(output_path: 'static')
6
+ require 'falcon/server'
7
+ require 'async/io'
8
+ require 'async/http/endpoint'
9
+ require 'async/container'
10
+
11
+ config_path = File.join(Dir.pwd, 'config.ru')
12
+ container_class = Async::Container::Threaded
13
+ server_port = 9090
14
+
15
+ app, options = Rack::Builder.parse_file(config_path)
16
+
17
+ container = container_class.run(count: 2) do
18
+ Async do
19
+ server = Falcon::Server.new(
20
+ Falcon::Server.middleware(app),
21
+ Async::HTTP::Endpoint.parse("http://localhost:#{server_port}")
22
+ )
23
+
24
+ server.run
25
+ end
26
+ end
27
+
28
+ output_path = File.expand_path(output_path, Dir.pwd)
29
+
30
+ # Delete any existing stuff:
31
+ FileUtils.rm_rf(output_path)
32
+
33
+ # Copy all public assets:
34
+ FileUtils::Verbose.mkpath(output_path)
35
+ Dir.glob(File.join(Dir.pwd, 'public/*')) do |path|
36
+ FileUtils::Verbose.cp_r(path, output_path)
37
+ end
38
+
39
+ # Generate HTML pages:
40
+ system("wget", "--mirror", "--recursive", "--continue", "--convert-links", "--adjust-extension", "--no-host-directories", "--directory-prefix", output_path.to_s, "http://localhost:#{server_port}")
41
+
42
+ container.stop
43
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Enable coverage
4
+ def coverage
5
+ ENV['COVERAGE'] = 'PartialSummary'
6
+ end
7
+
8
+ def test
9
+ system("rspec", exception: true)
10
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copy all installed JavaScript components to the public root.
4
+ def update
5
+ require 'fileutils'
6
+ require 'utopia/path'
7
+
8
+ yarn_package_root = self.root + "lib/components"
9
+ yarn_install_root = self.root + "public/_components"
10
+
11
+ yarn_package_root.children.select(&:directory?).collect(&:basename).each do |package_directory|
12
+ install_path = @yarn_install_root + package_directory
13
+ package_path = @yarn_package_root + package_directory
14
+ dist_path = package_path + 'dist'
15
+
16
+ FileUtils::Verbose.rm_rf(install_path)
17
+ FileUtils::Verbose.mkpath(install_path.dirname)
18
+
19
+ # If a package has a dist directory, we only symlink that... otherwise we have to do the entire package, and hope that bower's ignore was setup correctly:
20
+ if dist_path.exist?
21
+ link_path = Utopia::Path.shortest_path(dist_path, install_path)
22
+ else
23
+ link_path = Utopia::Path.shortest_path(package_path, install_path)
24
+ end
25
+
26
+ FileUtils::Verbose.cp_r File.expand_path(link_path, install_path), install_path
27
+ end
28
+ end
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -4,8 +4,6 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "utopia", path: File.expand_path("../", __dir__)
6
6
 
7
- gem "rack-freeze", "~> 1.2"
8
-
9
7
  gem "rake"
10
8
  gem "bundler"
11
9
 
@@ -13,14 +11,9 @@ gem "kramdown"
13
11
  gem "kramdown-parser-gfm"
14
12
 
15
13
  group :development do
16
- # For `rake server`:
17
14
  gem "guard-falcon"
18
15
  gem "falcon"
19
16
 
20
- # For `rake console`:
21
- gem "pry"
22
17
  gem "rack-test"
23
-
24
- # For `rspec` testing:
25
18
  gem "rspec"
26
19
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  require_relative 'config/environment'
5
5
 
6
- require 'rack/freeze'
6
+ self.freeze_app
7
7
 
8
8
  if RACK_ENV == :production
9
9
  # Handle exceptions in production with a error page and send an email notification:
@@ -69,7 +69,7 @@ module Utopia
69
69
  system("git", "config", "core.worktree", destination_root) or fail "could not set configuration"
70
70
 
71
71
  system("bundle", "config", "set", "--local", "deployment", "true")
72
- system("bundle", "config", "set", "--local", "without", "development test")
72
+ system("bundle", "config", "set", "--local", "without", "development")
73
73
 
74
74
  # In theory, to convert from non-shared to shared:
75
75
  # chgrp -R <group-name> . # Change files and directories' group
@@ -81,8 +81,7 @@ module Utopia
81
81
  # Set some useful defaults for the environment.
82
82
  environment = Environment[]
83
83
  environment.update_environment(destination_root) do |store|
84
- store['RACK_ENV'] ||= 'production'
85
- store['DATABASE_ENV'] ||= 'production'
84
+ store['VARIANT'] ||= 'production'
86
85
  store['UTOPIA_SESSION_SECRET'] ||= SecureRandom.hex(40)
87
86
  end
88
87
 
@@ -33,7 +33,7 @@ module Utopia
33
33
  # Local site setup commands.
34
34
  class Site < Samovar::Command
35
35
  # Configuration files which should be installed/updated:
36
- CONFIGURATION_FILES = ['.yarnrc', '.gitignore', 'config.ru', 'config/environment.rb', 'falcon.rb', 'Gemfile', 'Guardfile', 'Rakefile', 'tasks/yarn.rake', 'tasks/deploy.rake', 'tasks/development.rake', 'tasks/environment.rake', 'tasks/log.rake', 'spec/spec_helper.rb', 'spec/website_context.rb', 'spec/website_spec.rb']
36
+ CONFIGURATION_FILES = ['.yarnrc', '.gitignore', 'config.ru', 'config/environment.rb', 'falcon.rb', 'Gemfile', 'Guardfile', 'bake.rb', 'spec/spec_helper.rb', 'spec/website_context.rb', 'spec/website_spec.rb']
37
37
 
38
38
  # Directories that should exist:
39
39
  DIRECTORIES = ["config", "lib", "pages", "public", "tasks", "spec"]
@@ -73,9 +73,12 @@ module Utopia
73
73
  CONFIGURATION_FILES.each do |configuration_file|
74
74
  destination_path = File.join(destination_root, configuration_file)
75
75
 
76
- buffer = File.read(destination_path).gsub('$UTOPIA_VERSION', Utopia::VERSION)
77
-
78
- File.open(destination_path, "w") { |file| file.write(buffer) }
76
+ if File.exist?(destination_path)
77
+ buffer = File.read(destination_path).gsub('$UTOPIA_VERSION', Utopia::VERSION)
78
+ File.open(destination_path, "w") { |file| file.write(buffer) }
79
+ else
80
+ warn "Could not open #{destination_path}, maybe it should be removed from CONFIGURATION_FILES?"
81
+ end
79
82
  end
80
83
 
81
84
  Dir.chdir(destination_root) do
@@ -96,6 +99,12 @@ module Utopia
96
99
  end
97
100
  end
98
101
 
102
+ # Set some useful defaults for the environment.
103
+ environment = Environment[]
104
+ environment.update_environment(destination_root, :testing) do |store|
105
+ store['UTOPIA_SESSION_SECRET'] ||= SecureRandom.hex(40)
106
+ end
107
+
99
108
  name = `git config user.name || whoami`.chomp
100
109
 
101
110
  puts
@@ -92,9 +92,7 @@ module Utopia
92
92
  end
93
93
 
94
94
  def related_links
95
- name = @uri_path.basename
96
-
97
- return @controller.links(@uri_path.dirname, :name => @uri_path.basename, :indices => true)
95
+ @controller.links(@uri_path.dirname, :name => @uri_path.basename, :indices => true)
98
96
  end
99
97
 
100
98
  def siblings_path
@@ -22,6 +22,8 @@
22
22
 
23
23
  require_relative 'namespace'
24
24
 
25
+ require 'variant'
26
+
25
27
  module Utopia
26
28
  class Content
27
29
  # Tags which provide intrinsic behaviour within the content middleware.
@@ -57,9 +59,11 @@ module Utopia
57
59
  # Render the contents only if in the correct environment.
58
60
  # @param only [String] A comma separated list of environments to check.
59
61
  tag('environment') do |document, state|
60
- environment = document.attributes.fetch(:environment){RACK_ENV}.to_s
62
+ variant = document.attributes.fetch(:variant) do
63
+ Variant.for(:utopia)
64
+ end.to_s
61
65
 
62
- if state[:only].split(',').include?(environment)
66
+ if state[:only].split(',').include?(variant)
63
67
  document.parse_markup(state.content)
64
68
  end
65
69
  end
data/lib/utopia/logger.rb CHANGED
@@ -24,4 +24,4 @@ require 'console'
24
24
 
25
25
  module Utopia
26
26
  extend Console
27
- end
27
+ end
@@ -21,7 +21,6 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  require 'openssl'
24
- require 'securerandom'
25
24
  require 'digest/sha2'
26
25
 
27
26
  require 'json'
@@ -60,8 +59,7 @@ module Utopia
60
59
  @cookie_name = @session_name + ".encrypted"
61
60
 
62
61
  if secret.nil? or secret.empty?
63
- secret = SecureRandom.hex(32)
64
- warn "#{self.class} secret is #{secret.inspect}, generating transient secret key!" if $VERBOSE
62
+ raise ArgumentError, "invalid session secret: #{secret.inspect}"
65
63
  end
66
64
 
67
65
  # This generates a 32-byte key suitable for aes.
data/lib/utopia/setup.rb CHANGED
@@ -21,93 +21,134 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  require 'yaml'
24
+ require 'securerandom'
25
+
26
+ require 'variant'
27
+
28
+ require_relative 'logger'
24
29
 
25
30
  module Utopia
26
31
  # Used for setting up a Utopia web application, typically via `config/environment.rb`
27
32
  class Setup
28
- def initialize(config_root, external_encoding: Encoding::UTF_8)
29
- @config_root = config_root
30
-
31
- @external_encoding = external_encoding
33
+ def initialize(root, *arguments, **options)
34
+ @root = root
32
35
 
33
- @environment = nil
36
+ super(*arguments, **options)
34
37
  end
35
38
 
36
- attr :config_root
37
- attr :external_encoding
38
- attr :environment
39
+ attr :root
39
40
 
40
- ENVIRONMENT_KEY = 'UTOPIA_ENV'.freeze
41
- DEFAULT_ENVIRONMENT = 'environment'.freeze
42
-
43
- def explicit_environment_name
44
- ENV[ENVIRONMENT_KEY]
41
+ def config_root
42
+ File.expand_path("config", @root)
45
43
  end
46
44
 
47
45
  def site_root
48
- File.dirname(@config_root)
46
+ @root
47
+ end
48
+
49
+ def production?
50
+ Variant.for(:utopia) == :production
51
+ end
52
+
53
+ def staging?
54
+ Variant.for(:utopia) == :staging
55
+ end
56
+
57
+ def development?
58
+ Variant.for(:utopia) == :development
59
+ end
60
+
61
+ def testing?
62
+ Variant.for(:utopia) == :testing
49
63
  end
50
64
 
51
- def apply
52
- set_external_encoding
65
+ def secret_for(key)
66
+ secret = ENV["UTOPIA_#{key.upcase}_SECRET"]
53
67
 
54
- apply_environment
68
+ if secret.nil? || secret.empty?
69
+ secret = SecureRandom.hex(32)
70
+
71
+ Utopia.logger.warn(self) do
72
+ "Generating transient #{key} secret: #{secret.inspect}"
73
+ end
74
+ end
55
75
 
76
+ return secret
77
+ end
78
+
79
+ def apply!
56
80
  add_load_path('lib')
57
81
 
82
+ apply_environment
83
+
58
84
  require_relative '../utopia'
59
85
  end
60
86
 
87
+ private
88
+
61
89
  def apply_environment
62
- if name = explicit_environment_name
63
- load_environment(name)
64
- else
65
- load_environment(DEFAULT_ENVIRONMENT)
66
- end
90
+ # First try to load `config/environment.yaml` if it exists:
91
+ load_environment(:environment)
92
+
93
+ # NOTE: loading the environment above MAY set `VARIANT`. So, it's important to look up the variant AFTER loading that environment.
94
+
95
+ # Then, try to load the variant specific environment:
96
+ variant = Variant.for(:utopia)
97
+ load_environment(variant)
67
98
  end
68
99
 
69
- def environment_path(name, root = @config_root)
70
- File.expand_path("#{name}.yaml", root)
100
+ # Add the given path to $LOAD_PATH. If it's relative, make it absolute relative to `site_path`.
101
+ def add_load_path(path)
102
+ # Allow loading library code from lib directory:
103
+ $LOAD_PATH << File.expand_path(path, site_root)
71
104
  end
72
105
 
73
- # If you don't specify these, it's possible to have issues when encodings mismatch on the server.
74
- def set_external_encoding(encoding = Encoding::UTF_8)
75
- # TODO: Deprecate and remove this setup - it should be the responsibility of the server to set this correctly.
76
- if Encoding.default_external != encoding
77
- warn "Updating Encoding.default_external from #{Encoding.default_external} to #{encoding}" if $VERBOSE
78
- Encoding.default_external = encoding
79
- end
106
+ def environment_path(variant)
107
+ File.expand_path("config/#{variant}.yaml", @root)
80
108
  end
81
109
 
82
110
  # Load the named configuration file from the `config_root` directory.
83
- def load_environment(*args)
84
- path = environment_path(*args)
111
+ def load_environment(variant)
112
+ path = environment_path(variant)
85
113
 
86
114
  if File.exist?(path)
115
+ Utopia.logger.debug(self) {"Loading environment at path: #{path.inspect}"}
116
+
87
117
  # Load the YAML environment file:
88
- @environment = YAML.load_file(path)
118
+ environment = YAML.load_file(path)
89
119
 
90
120
  # We update ENV but only when it's not already set to something:
91
- ENV.update(@environment) do |name, old_value, new_value|
121
+ ENV.update(environment) do |name, old_value, new_value|
92
122
  old_value || new_value
93
123
  end
124
+
125
+ return true
126
+ else
127
+ Utopia.logger.debug(self) {"Ignoring environment at path: #{path.inspect} (file not found)"}
128
+
129
+ return false
94
130
  end
95
131
  end
96
-
97
- # Add the given path to $LOAD_PATH. If it's relative, make it absolute relative to `site_path`.
98
- def add_load_path(path)
99
- # Allow loading library code from lib directory:
100
- $LOAD_PATH << File.expand_path(path, site_root)
101
- end
102
132
  end
103
133
 
104
- # The main entry point for `config/environment.rb` for setting up the site.
105
- def self.setup(config_root = nil, **options)
106
- # We extract the directory of the caller to get the path to $root/config
107
- if config_root.nil?
134
+ @setup = nil
135
+
136
+ # You can call this method exactly once per process.
137
+ def self.setup(root = nil, **options)
138
+ if @setup
139
+ raise RuntimeError, "Utopia already setup!"
140
+ end
141
+
142
+ # We extract the root from the caller of this method:
143
+ if root.nil?
108
144
  config_root = File.dirname(caller[0])
145
+ root = File.dirname(config_root)
109
146
  end
110
147
 
111
- Setup.new(config_root, **options).tap(&:apply)
148
+ @setup = Setup.new(root, **options)
149
+
150
+ @setup.apply!
151
+
152
+ return @setup
112
153
  end
113
154
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'rack/builder'
24
+ require 'rack/test'
25
+ require 'irb'
26
+
27
+ module Utopia
28
+ # This is designed to be used with the corresponding bake task.
29
+ class Shell
30
+ include Rack::Test::Methods
31
+
32
+ def initialize(context)
33
+ @context = context
34
+ @app = nil
35
+ end
36
+
37
+ def app
38
+ @app ||= Rack::Builder.parse_file(
39
+ File.expand_path('config.ru', @context.root)
40
+ ).first
41
+ end
42
+
43
+ def to_s
44
+ self.class.name
45
+ end
46
+
47
+ def binding
48
+ super
49
+ end
50
+ end
51
+ end
@@ -21,5 +21,5 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  module Utopia
24
- VERSION = "2.12.4"
24
+ VERSION = "2.13.0"
25
25
  end
@@ -63,10 +63,12 @@ Dir.chdir(GIT_WORK_TREE) do
63
63
  sudo %W{git submodule update -i}
64
64
 
65
65
  if File.exist? 'Gemfile'
66
- sudo %W{bundle install --jobs=4 --retry=2}
66
+ sudo %W{bundle install}
67
67
  end
68
68
 
69
- if File.exist? 'Rakefile'
69
+ if File.exist? 'bake.rb'
70
+ sudo %W{bundle exec bake deploy restart}
71
+ elsif File.exist? 'Rakefile'
70
72
  sudo %W{bundle exec rake deploy restart}
71
73
  end
72
74
  end
data/setup/site/Gemfile CHANGED
@@ -7,17 +7,16 @@ gem 'utopia', '~> $UTOPIA_VERSION'
7
7
  # gem 'utopia-analytics'
8
8
 
9
9
  gem 'rake'
10
+ gem 'bake'
11
+ gem 'variant'
10
12
  gem 'bundler'
11
13
 
12
- gem 'rack-freeze', '~> 1.2'
13
14
  gem 'rack-test'
14
15
 
15
16
  group :development do
16
- # For `rake server`:
17
17
  gem 'guard-falcon', require: false
18
18
  gem 'guard-rspec', require: false
19
19
 
20
- # For `rspec` testing:
21
20
  gem 'rspec'
22
21
  gem 'covered'
23
22
 
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Prepare the application for start/restart.
4
+ def deploy
5
+ # This task is typiclly run after the site is updated but before the server is restarted.
6
+ end
7
+
8
+ # Restart the application server.
9
+ def restart
10
+ call 'falcon:supervisor:restart'
11
+ end
12
+
13
+ # Start the development server.
14
+ def default
15
+ call 'utopia:development'
16
+ end
data/setup/site/config.ru CHANGED
@@ -3,26 +3,28 @@
3
3
 
4
4
  require_relative 'config/environment'
5
5
 
6
- require 'rack/freeze'
6
+ self.freeze_app
7
7
 
8
- if RACK_ENV == :production
8
+ if UTOPIA.production?
9
9
  # Handle exceptions in production with a error page and send an email notification:
10
10
  use Utopia::Exceptions::Handler
11
11
  use Utopia::Exceptions::Mailer
12
12
  else
13
13
  # We want to propate exceptions up when running tests:
14
- use Rack::ShowExceptions unless RACK_ENV == :test
14
+ use Rack::ShowExceptions unless UTOPIA.testing?
15
15
  end
16
16
 
17
17
  use Utopia::Static, root: 'public'
18
18
 
19
- use Utopia::Redirection::Rewrite,
19
+ use Utopia::Redirection::Rewrite, {
20
20
  '/' => '/welcome/index'
21
+ }
21
22
 
22
23
  use Utopia::Redirection::DirectoryIndex
23
24
 
24
- use Utopia::Redirection::Errors,
25
+ use Utopia::Redirection::Errors, {
25
26
  404 => '/errors/file-not-found'
27
+ }
26
28
 
27
29
  require 'utopia/localization'
28
30
  use Utopia::Localization,
@@ -32,7 +34,7 @@ use Utopia::Localization,
32
34
  require 'utopia/session'
33
35
  use Utopia::Session,
34
36
  expires_after: 3600 * 24,
35
- secret: ENV['UTOPIA_SESSION_SECRET'],
37
+ secret: UTOPIA.secret_for(:session),
36
38
  secure: true
37
39
 
38
40
  use Utopia::Controller
@@ -4,6 +4,4 @@ require 'bundler/setup'
4
4
  Bundler.setup
5
5
 
6
6
  require 'utopia/setup'
7
- Utopia.setup
8
-
9
- RACK_ENV = ENV.fetch('RACK_ENV', :development).to_sym unless defined? RACK_ENV
7
+ UTOPIA ||= Utopia.setup
@@ -11,7 +11,7 @@
11
11
  <div>
12
12
  <i class="fa fa-cubes"></i>
13
13
  <h2>Modular code and structure</h2>
14
- <p>Utopia provides independently useful <a href="https://github.com/rack/rack">Rack</a> middleware and has been designed with simplicify in mind. Several fully-featured webapps and a ton of commercial websites have guided the development of the Utopia stack. It is capable of handling a diverse range of requirements.</p>
14
+ <p>Utopia provides independently useful <a href="https://github.com/rack/rack">Rack</a> middleware and has been designed with simplicity in mind. Several fully-featured webapps and a ton of commercial websites have guided the development of the Utopia stack. It is capable of handling a diverse range of requirements.</p>
15
15
  </div>
16
16
 
17
17
  <div>
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'bundler/setup'
4
4
  require 'covered/rspec'
5
- require 'utopia'
5
+ require 'variant'
6
+
7
+ Variant.force!(:testing)
6
8
 
7
9
  RSpec.configure do |config|
8
10
  # Enable flags like --only-failures and --next-failure
@@ -12,9 +12,9 @@ RSpec.describe "website", timeout: 120 do
12
12
  it "should be responsive" do
13
13
  Async::HTTP::Client.open(endpoint, connection_limit: 8) do |client|
14
14
  spider.fetch(statistics, client, endpoint.url) do |method, uri, response|
15
- #if response.failure?
15
+ if response.failure?
16
16
  Async.logger.error{"#{method} #{uri} -> #{response.status}"}
17
- #end
17
+ end
18
18
  end.wait
19
19
  end
20
20
 
@@ -87,10 +87,10 @@ RSpec.describe "utopia command" do
87
87
  result = sh_status(utopia, "--in", dir, "site", "create")
88
88
  expect(result).to be == 0
89
89
 
90
- expect(Dir.entries(dir)).to include(".yarnrc", ".git", "Gemfile", "Gemfile.lock", "README.md", "Rakefile", "config.ru", "lib", "pages", "public", "spec")
90
+ expect(Dir.entries(dir)).to include(".yarnrc", ".git", "Gemfile", "Gemfile.lock", "README.md", "bake.rb", "config.ru", "lib", "pages", "public", "spec")
91
91
 
92
92
  Dir.chdir(dir) do
93
- result = sh_status("rake", "test")
93
+ result = sh_status("bundle", "exec", "bake", "utopia:test")
94
94
  expect(result).to be == 0
95
95
  end
96
96
  end
@@ -113,7 +113,7 @@ RSpec.describe "utopia command" do
113
113
  end
114
114
 
115
115
  environment = YAML.load_file(File.join(dir, 'config/environment.yaml'))
116
- expect(environment).to include('RACK_ENV', 'UTOPIA_SESSION_SECRET')
116
+ expect(environment).to include('VARIANT', 'UTOPIA_SESSION_SECRET')
117
117
  end
118
118
  end
119
119
 
@@ -161,7 +161,7 @@ RSpec.describe "utopia command" do
161
161
  expect(result).to be == 0
162
162
  end
163
163
 
164
- files = %W[.yarnrc .git Gemfile Gemfile.lock README.md Rakefile config.ru lib pages public]
164
+ files = %W[.yarnrc .git Gemfile Gemfile.lock README.md bake.rb config.ru lib pages public]
165
165
 
166
166
  expect(Dir.entries(server_path)).to include(*files)
167
167
 
@@ -31,7 +31,7 @@ RSpec.describe Utopia::Content::Tags do
31
31
  describe '<utopia:environment>' do
32
32
  let(:node) do
33
33
  MockNode.new('utopia' => subject) do |document, state|
34
- document.tag("utopia:environment", only: 'test') do
34
+ document.tag("utopia:environment", only: 'testing') do
35
35
  document.text("Hello World")
36
36
  end
37
37
 
@@ -42,7 +42,7 @@ RSpec.describe Utopia::Content::Tags do
42
42
  end
43
43
 
44
44
  it "it should render correct content for test" do
45
- document[:environment] = "test"
45
+ document[:variant] = "testing"
46
46
 
47
47
  result = document.render_node(node)
48
48
 
@@ -50,7 +50,7 @@ RSpec.describe Utopia::Content::Tags do
50
50
  end
51
51
 
52
52
  it "it should render correct content for production" do
53
- document[:environment] = "production"
53
+ document[:variant] = "production"
54
54
 
55
55
  result = document.render_node(node)
56
56
 
@@ -4,17 +4,19 @@
4
4
  require 'utopia'
5
5
  require 'json'
6
6
 
7
- require 'rack/freeze'
7
+ self.freeze_app
8
8
 
9
9
  use Utopia::ContentLength
10
10
 
11
- use Utopia::Redirection::Rewrite,
11
+ use Utopia::Redirection::Rewrite, {
12
12
  '/' => '/welcome/index'
13
+ }
13
14
 
14
15
  use Utopia::Redirection::DirectoryIndex
15
16
 
16
- use Utopia::Redirection::Errors,
17
+ use Utopia::Redirection::Errors, {
17
18
  404 => '/errors/file-not-found'
19
+ }
18
20
 
19
21
  # use Utopia::Localization,
20
22
  # :default_locale => 'en',
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- use Utopia::Redirection::Rewrite, "/" => "/welcome/index"
3
+ use Utopia::Redirection::Rewrite, {"/" => "/welcome/index"}
4
4
 
5
5
  use Utopia::Redirection::DirectoryIndex
6
6
 
7
- use Utopia::Redirection::Errors,
7
+ use Utopia::Redirection::Errors, {
8
8
  404 => '/error',
9
9
  418 => '/teapot'
10
+ }
10
11
 
11
12
  use Utopia::Redirection::Moved, "/a", "/b"
12
13
  use Utopia::Redirection::Moved, "/hierarchy/", "/hierarchy", flatten: true
@@ -26,25 +26,31 @@ RSpec.describe Utopia::Setup do
26
26
  let(:config_root) {File.expand_path('setup_spec/config', __dir__)}
27
27
  subject{Utopia::Setup.new(config_root)}
28
28
 
29
+ let(:environment) {Variant::Environment.instance}
30
+
29
31
  it "should load specified environment" do
30
- stub_const("Utopia::Setup::ENV", {'UTOPIA_ENV' => 'production'})
31
-
32
- expect(subject).to receive(:load_environment).with('production')
33
-
34
- subject.apply_environment
32
+ environment.with({'VARIANT' => 'production'}) do
33
+ expect(subject).to receive(:load_environment).with(:environment).ordered
34
+ expect(subject).to receive(:load_environment).with(:production).ordered
35
+ subject.send(:apply_environment)
36
+ end
35
37
  end
36
38
 
37
39
  it "should load default environment" do
38
- stub_const("Utopia::Setup::ENV", {})
39
-
40
- expect(subject).to receive(:load_environment).with('environment')
40
+ environment.with({}) do
41
+ stub_const("ENV", {})
42
+ expect(subject).to receive(:load_environment).with(:environment).ordered
43
+ expect(subject).to receive(:load_environment).with(:development).ordered
44
+ end
41
45
 
42
- subject.apply_environment
46
+ subject.send(:apply_environment)
43
47
  end
44
48
 
45
49
  it "should add load path" do
46
- subject.add_load_path('lib')
50
+ expect($LOAD_PATH).to receive(:<<).with(
51
+ File.expand_path('lib', subject.site_root)
52
+ )
47
53
 
48
- expect($LOAD_PATH).to include(File.expand_path('lib', subject.site_root))
54
+ subject.send(:add_load_path, 'lib')
49
55
  end
50
56
  end
data/utopia.gemspec CHANGED
@@ -29,8 +29,9 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_dependency 'samovar', '~> 2.1'
31
31
  spec.add_dependency 'console', '~> 1.0'
32
+ spec.add_dependency 'variant', '~> 0.1'
32
33
 
33
- spec.add_dependency 'rack', '~> 2.0'
34
+ spec.add_dependency 'rack', '~> 2.2'
34
35
 
35
36
  spec.add_dependency 'http-accept', '~> 2.1'
36
37
 
@@ -38,6 +39,8 @@ Gem::Specification.new do |spec|
38
39
 
39
40
  spec.add_dependency 'concurrent-ruby', '~> 1.0'
40
41
 
42
+ spec.add_development_dependency 'bake'
43
+
41
44
  spec.add_development_dependency 'falcon'
42
45
  spec.add_development_dependency 'async-rspec'
43
46
  spec.add_development_dependency 'async-websocket'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: utopia
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.12.4
4
+ version: 2.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-02 00:00:00.000000000 Z
11
+ date: 2020-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trenni
@@ -80,20 +80,34 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: variant
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.1'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.1'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rack
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
101
  - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: '2.0'
103
+ version: '2.2'
90
104
  type: :runtime
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
- version: '2.0'
110
+ version: '2.2'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: http-accept
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +150,20 @@ dependencies:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
152
  version: '1.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: bake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
139
167
  - !ruby/object:Gem::Dependency
140
168
  name: falcon
141
169
  requirement: !ruby/object:Gem::Requirement
@@ -253,11 +281,16 @@ files:
253
281
  - Gemfile
254
282
  - README.md
255
283
  - Rakefile
256
- - benchmarks/call_vs_check.rb
257
- - benchmarks/const_vs_hash.rb
258
- - benchmarks/hash_vs_openstruct.rb
259
- - benchmarks/string_vs_symbol.rb
260
- - benchmarks/struct_vs_class.rb
284
+ - bake/utopia.rb
285
+ - bake/utopia/shell.rb
286
+ - bake/utopia/static.rb
287
+ - bake/utopia/test.rb
288
+ - bake/utopia/yarn.rb
289
+ - benchmark/call_vs_check.rb
290
+ - benchmark/const_vs_hash.rb
291
+ - benchmark/hash_vs_openstruct.rb
292
+ - benchmark/string_vs_symbol.rb
293
+ - benchmark/struct_vs_class.rb
261
294
  - bin/utopia
262
295
  - docs/.nojekyll
263
296
  - docs/_components/jquery-syntax/base/jquery.syntax.brush.apache.css
@@ -628,6 +661,7 @@ files:
628
661
  - lib/utopia/session/lazy_hash.rb
629
662
  - lib/utopia/session/serialization.rb
630
663
  - lib/utopia/setup.rb
664
+ - lib/utopia/shell.rb
631
665
  - lib/utopia/static.rb
632
666
  - lib/utopia/static/local_file.rb
633
667
  - lib/utopia/static/mime_types.rb
@@ -641,7 +675,7 @@ files:
641
675
  - setup/site/Gemfile
642
676
  - setup/site/Guardfile
643
677
  - setup/site/README.md
644
- - setup/site/Rakefile
678
+ - setup/site/bake.rb
645
679
  - setup/site/config.ru
646
680
  - setup/site/config/README.md
647
681
  - setup/site/config/environment.rb
@@ -661,12 +695,6 @@ files:
661
695
  - setup/site/spec/spec_helper.rb
662
696
  - setup/site/spec/website_context.rb
663
697
  - setup/site/spec/website_spec.rb
664
- - setup/site/tasks/deploy.rake
665
- - setup/site/tasks/development.rake
666
- - setup/site/tasks/environment.rake
667
- - setup/site/tasks/log.rake
668
- - setup/site/tasks/static.rake
669
- - setup/site/tasks/yarn.rake
670
698
  - spec/mock_node.rb
671
699
  - spec/spec_helper.rb
672
700
  - spec/utopia/command_spec.rb
data/setup/site/Rakefile DELETED
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'pathname'
4
- SITE_ROOT = Pathname.new(__dir__).realpath
5
-
6
- # Load all rake tasks:
7
- import(*Dir.glob('tasks/**/*.rake'))
8
-
9
- task :default => :development
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- desc 'Run by git post-update hook when deployed to a web server'
4
- task :deploy do
5
- # This task is typiclly run after the site is updated but before the server is restarted.
6
- end
7
-
8
- desc 'Restart the application server'
9
- task :restart do
10
- if falcon = `which falcon`.chomp! and File.exist?("supervisor.ipc")
11
- sh(falcon, 'supervisor', 'restart')
12
- end
13
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rspec/core/rake_task"
4
-
5
- RSpec::Core::RakeTask.new(:test)
6
-
7
- task :coverage do
8
- ENV['COVERAGE'] = 'PartialSummary'
9
- end
10
-
11
- desc 'Start the development server.'
12
- task :server => :environment do
13
- exec('guard', '-g', 'development')
14
- end
15
-
16
- desc 'Start the development environment which includes web server and tests.'
17
- task :development => :environment do
18
- exec('guard', '-g', 'development,test')
19
- end
20
-
21
- desc 'Start an interactive console for your web application'
22
- task :console => :environment do
23
- require 'irb'
24
- require 'rack/test'
25
-
26
- include Rack::Test::Methods
27
-
28
- def app
29
- @app ||= Rack::Builder.parse_file(SITE_ROOT + 'config.ru').first
30
- end
31
-
32
- IRB.start
33
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- desc 'Set up the environment for running your web application'
4
- task :environment => :log do |task|
5
- require SITE_ROOT + 'config/environment'
6
-
7
- # We ensure this is part of the shell environment so if other commands are invoked they will work correctly.
8
- ENV['RACK_ENV'] = RACK_ENV.to_s if defined?(RACK_ENV)
9
- ENV['DATABASE_ENV'] = DATABASE_ENV.to_s if defined?(DATABASE_ENV)
10
-
11
- # This generates a consistent session secret if one was not already provided:
12
- if ENV['UTOPIA_SESSION_SECRET'].nil?
13
- require 'securerandom'
14
-
15
- Utopia.logger.warn 'Generating transient session key for development...'
16
- ENV['UTOPIA_SESSION_SECRET'] = SecureRandom.hex(32)
17
- end
18
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- task :log do
4
- require 'utopia/logger'
5
-
6
- # This is deprecated, prefer to use `Utopia.logger` or `Async.logger`:
7
- LOGGER = Utopia.logger
8
- end
9
-
10
- namespace :log do
11
- desc "Increase verbosity of logger to info."
12
- task :info => :log do
13
- Utopia.logger.info!
14
- end
15
-
16
- desc "Increase verbosity of global debug."
17
- task :debug => :log do
18
- Utopia.logger.debug!
19
- end
20
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :static do
4
- task :static_environment do
5
- RACK_ENV ||= :static
6
- DATABASE_ENV ||= :production
7
- SERVER_PORT ||= 9291
8
- end
9
-
10
- desc "Generate a static copy of the site."
11
- task :generate => [:static_environment, :environment] do
12
- require 'falcon/server'
13
- require 'async/io'
14
- require 'async/container'
15
-
16
- config_path = SITE_ROOT + 'config.ru'
17
- container_class = Async::Container::Threaded
18
-
19
- app, options = Rack::Builder.parse_file(config_path.to_s)
20
-
21
- container = container_class.new(concurrency: 2) do
22
- server = Falcon::Server.new(app, [
23
- Async::IO::Endpoint.parse("tcp://localhost:#{SERVER_PORT}", reuse_port: true)
24
- ])
25
-
26
- server.run
27
- end
28
-
29
- output_path = ENV.fetch('OUTPUT_PATH') {SITE_ROOT + 'static'}
30
-
31
- # Delete any existing stuff:
32
- FileUtils.rm_rf(output_path)
33
-
34
- # Copy all public assets:
35
- Dir.glob(SITE_ROOT + 'public/*').each do |path|
36
- FileUtils.cp_r(path, output_path)
37
- end
38
-
39
- # Generate HTML pages:
40
- system("wget", "--mirror", "--recursive", "--continue", "--convert-links", "--adjust-extension", "--no-host-directories", "--directory-prefix", output_path.to_s, "http://localhost:#{SERVER_PORT}")
41
-
42
- container.stop
43
- end
44
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :yarn do
4
- desc 'Load the .bowerrc file and setup the environment for other tasks.'
5
- task :environment do
6
- @yarn_package_root = SITE_ROOT + "lib/components"
7
- @yarn_install_root = SITE_ROOT + "public/_components"
8
- end
9
-
10
- desc 'Update the bower packages and link into the public directory.'
11
- task :update => :environment do
12
- require 'fileutils'
13
- require 'utopia/path'
14
-
15
- @yarn_package_root.children.select(&:directory?).collect(&:basename).each do |package_directory|
16
- install_path = @yarn_install_root + package_directory
17
- package_path = @yarn_package_root + package_directory
18
- dist_path = package_path + 'dist'
19
-
20
- FileUtils::Verbose.rm_rf install_path
21
- FileUtils::Verbose.mkpath(install_path.dirname)
22
-
23
- # If a package has a dist directory, we only symlink that... otherwise we have to do the entire package, and hope that bower's ignore was setup correctly:
24
- if File.exist? dist_path
25
- link_path = Utopia::Path.shortest_path(dist_path, install_path)
26
- else
27
- link_path = Utopia::Path.shortest_path(package_path, install_path)
28
- end
29
-
30
- FileUtils::Verbose.cp_r File.expand_path(link_path, install_path), install_path
31
- end
32
- end
33
- end