utopia 2.12.4 → 2.13.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 (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