utopia 2.30.2 → 2.31.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 (58) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/bake/utopia/server.rb +1 -1
  4. data/bake/utopia/site.rb +3 -3
  5. data/context/getting-started.md +93 -0
  6. data/context/index.yaml +32 -0
  7. data/context/integrating-with-javascript.md +75 -0
  8. data/context/middleware.md +157 -0
  9. data/context/server-setup.md +116 -0
  10. data/context/updating-utopia.md +69 -0
  11. data/context/what-is-xnode.md +41 -0
  12. data/lib/utopia/content/document.rb +39 -37
  13. data/lib/utopia/content/link.rb +1 -2
  14. data/lib/utopia/content/links.rb +2 -2
  15. data/lib/utopia/content/markup.rb +10 -10
  16. data/lib/utopia/content/middleware.rb +195 -0
  17. data/lib/utopia/content/namespace.rb +1 -1
  18. data/lib/utopia/content/node.rb +1 -1
  19. data/lib/utopia/content/response.rb +1 -1
  20. data/lib/utopia/content/tags.rb +1 -1
  21. data/lib/utopia/content.rb +4 -186
  22. data/lib/utopia/controller/actions.md +8 -8
  23. data/lib/utopia/controller/actions.rb +1 -1
  24. data/lib/utopia/controller/base.rb +4 -4
  25. data/lib/utopia/controller/middleware.rb +133 -0
  26. data/lib/utopia/controller/respond.rb +2 -46
  27. data/lib/utopia/controller/responder.rb +103 -0
  28. data/lib/utopia/controller/rewrite.md +2 -2
  29. data/lib/utopia/controller/rewrite.rb +1 -1
  30. data/lib/utopia/controller/variables.rb +11 -5
  31. data/lib/utopia/controller.rb +4 -126
  32. data/lib/utopia/exceptions/mailer.rb +4 -4
  33. data/lib/utopia/extensions/array_split.rb +2 -2
  34. data/lib/utopia/extensions/date_comparisons.rb +3 -3
  35. data/lib/utopia/import_map.rb +374 -0
  36. data/lib/utopia/localization/middleware.rb +173 -0
  37. data/lib/utopia/localization/wrapper.rb +52 -0
  38. data/lib/utopia/localization.rb +4 -202
  39. data/lib/utopia/path.rb +26 -11
  40. data/lib/utopia/redirection.rb +2 -2
  41. data/lib/utopia/session/lazy_hash.rb +1 -1
  42. data/lib/utopia/session/middleware.rb +218 -0
  43. data/lib/utopia/session/serialization.rb +1 -1
  44. data/lib/utopia/session.rb +4 -205
  45. data/lib/utopia/static/local_file.rb +19 -19
  46. data/lib/utopia/static/middleware.rb +120 -0
  47. data/lib/utopia/static/mime_types.rb +1 -1
  48. data/lib/utopia/static.rb +4 -108
  49. data/lib/utopia/version.rb +1 -1
  50. data/lib/utopia.rb +1 -0
  51. data/readme.md +7 -0
  52. data/releases.md +7 -0
  53. data/setup/site/config.ru +1 -1
  54. data.tar.gz.sig +0 -0
  55. metadata +31 -4
  56. metadata.gz.sig +0 -0
  57. data/lib/utopia/locale.rb +0 -29
  58. data/lib/utopia/responder.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c09511beeea999db3b560ea57654509370f0531b0e9ed624c79aa635c104a44c
4
- data.tar.gz: 88502bfd0c098a3105c8ce3265fe2f520b4639d29291e83aa45a6be9ee091667
3
+ metadata.gz: 3268818f2c2b09e4d80b6d74884adfd2e1cfd60762250a6d7077465cd4f4999d
4
+ data.tar.gz: f9bc6fb2a4e9548b2f8f47b8bf691877aa4e55cc866f044ddc2147d01077a3f2
5
5
  SHA512:
6
- metadata.gz: b82d2588867cce76b4cf735d8c9d4409cf1ddd8d2574a6106041e96b3e3db8de278c2049a146fc705ea35edc499527b471b33b534fe879b8976656a565029c03
7
- data.tar.gz: a714306c091623fb5879bb68120d6eb2e6f3848d5b756891e1c2bcb430b7a5ebff55255968ea99c22f450c1b09ed2c0aeafe5f8339be0082c6ee51ce1eb5a51f
6
+ metadata.gz: 26d27ec806a779ea012758473f2e9c72a66042a3db31bd1a7e66855f5ce60d81ba765f3aa5292f36abbe959f5dfb8b47e8f0211ec31a8475c22f656f3f4343f4
7
+ data.tar.gz: '069d1e930246601028c078d129bcdf11d8f44d8f441e6f7a11be37162e12e2676520e56d57ed4013974120f7c58f07e87c61f933d7c49ec3e99c564b408d8c46'
checksums.yaml.gz.sig CHANGED
Binary file
@@ -45,7 +45,7 @@ def update(root: context.root)
45
45
  # chmod -R g+w . # Change permissions
46
46
  # chmod g-w .git/objects/pack/* # Git pack files should be immutable
47
47
  # chmod g+s `find . -type d` # New files get group id of directory
48
-
48
+
49
49
  # Set some useful defaults for the environment.
50
50
  recipe = context.lookup("utopia:environment:update")
51
51
  recipe.instance.update("environment", root: root) do |store|
data/bake/utopia/site.rb CHANGED
@@ -57,7 +57,7 @@ def create(root: context.root)
57
57
 
58
58
  if File.exist?(destination_path)
59
59
  buffer = File.read(destination_path).gsub("$UTOPIA_VERSION", Utopia::VERSION)
60
- File.open(destination_path, "w") { |file| file.write(buffer) }
60
+ File.open(destination_path, "w") {|file| file.write(buffer)}
61
61
  else
62
62
  Console.warn(self) {"Could not open #{destination_path}, maybe it should be removed from CONFIGURATION_FILES?"}
63
63
  end
@@ -119,11 +119,11 @@ def upgrade(root: context.root)
119
119
 
120
120
  FileUtils.copy_entry(source_path, destination_path)
121
121
  buffer = File.read(destination_path).gsub("$UTOPIA_VERSION", Utopia::VERSION)
122
- File.open(destination_path, "w") { |file| file.write(buffer) }
122
+ File.open(destination_path, "w") {|file| file.write(buffer)}
123
123
  end
124
124
 
125
125
  context.lookup("utopia:environment:setup").call(root: root)
126
-
126
+
127
127
  # Stage any files that have been changed or removed:
128
128
  system("git", "add", "-u", chdir: root) or fail "could not add files"
129
129
 
@@ -0,0 +1,93 @@
1
+ # Getting Started
2
+
3
+ This guide explains how to set up a `utopia` website for local development and deployment.
4
+
5
+ ## Installation
6
+
7
+ Utopia is built on Ruby and Rack. Therefore, Ruby (suggested 2.0+) should be installed and working. Then, to install `utopia` and all required dependencies, run:
8
+
9
+ ~~~ bash
10
+ $ gem install utopia
11
+ ~~~
12
+
13
+ ### Atom Integration
14
+
15
+ Utopia uses [Trenni](https://github.com/ioquatix/trenni) for templates and it has a syntax slightly different from ERB. However, there is a [package for Atom](https://atom.io/packages/language-trenni) which provides accurate syntax highlighting.
16
+
17
+ ## Your First Page
18
+
19
+ To setup the default site, create a directory (typically the hostname of the site you want to create) and use the `bake utopia:site:create` command:
20
+
21
+ ~~~ bash
22
+ $ mkdir www.example.com
23
+ $ cd www.example.com
24
+ $ bake --gem utopia utopia:site:create
25
+ $ bake utopia:development
26
+ ~~~
27
+
28
+ You will now have a basic template site running on `https://localhost:9292`.
29
+
30
+ ### Welcome Page
31
+
32
+ Utopia includes a redirection middleware to redirect all root-level requests to a given URI. The default being `/welcome/index`:
33
+
34
+ ```ruby
35
+ # in config.ru
36
+
37
+ use Utopia::Redirection::Rewrite,
38
+ "/" => "/welcome/index"
39
+ ```
40
+
41
+ The content for this page is stored in `pages/welcome/index.xnode`. The format of this page is a subset of HTML5 - open and close tags are strictly enforced.
42
+
43
+ There are several special tags which are used for creating modular content. The most common one is the outer `<content:page>` tag. Utopia uses the name `page` to lookup the file-system hierarchy. First, it looks for `/welcome/_page.xnode`, and then it looks for `/_page.xnode` which it finds. This page template includes a special `<utopia:content/>` tag which is replaced with the inner body of the `<content:page>` tag. This recursive lookup is the heart of Utopia.
44
+
45
+ ### Links
46
+
47
+ Utopia is a content-centric web application platform. It leverages the file-system to provide a mapping between logical resources and files on disk. The primary mode of mapping incoming requests to specific nodes (content) is done using the `links.yaml` file.
48
+
49
+ The links file associates metadata with node names for a given directory. This can include things like redirects, titles, descriptions, etc. You can add any metadata you like, to support your specific use-case. The primary use of the links files is to provide site structure, e.g. menus. In addition, they can function as a rudimentary data-store for static information, e.g. a list of applications (each with it's own page), a list of features, etc.
50
+
51
+ You'll notice that there is a file `/links.yaml`. This file contains important metadata relating to the `errors` subdirectory. As we don't want these nodes showing up in a top level menu, we mark them as `display: false`
52
+
53
+ ~~~ yaml
54
+ errors:
55
+ display: false
56
+ ~~~
57
+
58
+ ## Testing
59
+
60
+ Utopia websites include a default set of tests using `sus`. These specs can test against the actual running website.
61
+
62
+ ~~~ bash
63
+ $ sus
64
+
65
+ 1 samples: 1x 200. 3703.7 requests per second. S/D: 0.000µs.
66
+ 1 passed out of 1 total (2 assertions)
67
+ 🏁 Finished in 247.4ms; 8.085 assertions per second.
68
+ ~~~
69
+
70
+ The website test will spider all pages on your site and report any broken links as failures.
71
+
72
+ ### Coverage
73
+
74
+ The [covered](https://github.com/socketry/covered) gem is used for providing source code coverage information.
75
+
76
+ ~~~ bash
77
+ $ COVERAGE=BriefSummary rspec
78
+
79
+ website
80
+ 1 samples: 1x 200. 67.53 requests per second. S/D: 0.000µs.
81
+ should be responsive
82
+
83
+ * 5 files checked; 33/46 lines executed; 71.74% covered.
84
+
85
+ Least Coverage:
86
+ pages/_page.xnode: 6 lines not executed!
87
+ config.ru: 4 lines not executed!
88
+ pages/welcome/index.xnode: 2 lines not executed!
89
+ pages/_heading.xnode: 1 lines not executed!
90
+
91
+ Finished in 1.82 seconds (files took 0.51845 seconds to load)
92
+ 1 example, 0 failures
93
+ ~~~
@@ -0,0 +1,32 @@
1
+ # Automatically generated context index for Utopia::Project guides.
2
+ # Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
3
+ ---
4
+ description: Utopia is a framework for building dynamic content-driven websites.
5
+ metadata:
6
+ documentation_uri: https://socketry.github.io/utopia/
7
+ funding_uri: https://github.com/sponsors/ioquatix/
8
+ source_code_uri: https://github.com/socketry/utopia.git
9
+ files:
10
+ - path: getting-started.md
11
+ title: Getting Started
12
+ description: This guide explains how to set up a `utopia` website for local development
13
+ and deployment.
14
+ - path: middleware.md
15
+ title: Middleware
16
+ description: This guide gives an overview of the different Rack middleware used
17
+ by Utopia.
18
+ - path: server-setup.md
19
+ title: Server Setup
20
+ description: This guide explains how to deploy a `utopia` web application.
21
+ - path: integrating-with-javascript.md
22
+ title: Installing JavaScript Libraries
23
+ description: Utopia integrates with Yarn and provides a [bake task](https://github.com/ioquatix/bake)
24
+ to simplify deployment packages distributed using `yarn` that implement the `dist`
25
+ sub-directory convention.
26
+ - path: what-is-xnode.md
27
+ title: What is XNode?
28
+ description: This guide explains the `xnode` view layer and how it can be used to
29
+ build efficient websites.
30
+ - path: updating-utopia.md
31
+ title: Updating Utopia
32
+ description: This guide explains how to update existing `utopia` websites.
@@ -0,0 +1,75 @@
1
+ # Installing JavaScript Libraries
2
+
3
+ Utopia integrates with Yarn and provides a [bake task](https://github.com/ioquatix/bake) to simplify deployment packages distributed using `yarn` that implement the `dist` sub-directory convention.
4
+
5
+ ## Installing Yarn
6
+
7
+ If you don't already have yarn installed, make sure you have npm installed and then run the following command:
8
+
9
+ ```bash
10
+ $ sudo npm install -g yarn
11
+ ```
12
+
13
+ ## Installing jQuery
14
+
15
+ Firstly, ensure your project has a `package.json` file:
16
+
17
+ ```bash
18
+ $ yarn init
19
+ ```
20
+
21
+ Then install jquery using `yarn`:
22
+
23
+ ```bash
24
+ $ yarn add jquery
25
+ ```
26
+
27
+ Copy the distribution scripts to `public/_components`:
28
+
29
+ ```bash
30
+ $ bundle exec bake utopia:node:update
31
+ ```
32
+
33
+ Then add the appropriate `<script>` tags to `pages/_page.xnode`:
34
+
35
+ ```html
36
+ <script type="text/javascript" src="/_components/jquery/jquery.min.js"></script>
37
+ ```
38
+
39
+ ## Using JavaScript
40
+
41
+ You can use JavaScript by embedding it directly into your HTML, or by creating a JavaScript source file and referencing that.
42
+
43
+ ### Embedding Code
44
+
45
+ In your HTML view:
46
+
47
+ ```trenni
48
+ <html>
49
+ <body>
50
+ <script type="text/javascript">
51
+ //<![CDATA[
52
+ console.log("Hello World")
53
+ //]]>
54
+ </script>
55
+ </body>
56
+ </html>
57
+ ```
58
+
59
+ ### External Script
60
+
61
+ In `script.js`:
62
+
63
+ ```javascript
64
+ console.log("Hello World")
65
+ ```
66
+
67
+ In your HTML view:
68
+
69
+ ```trenni
70
+ <html>
71
+ <body>
72
+ <script type="text/javascript" src="script.js"></script>
73
+ </body>
74
+ </html>
75
+ ```
@@ -0,0 +1,157 @@
1
+ # Middleware
2
+
3
+ This guide gives an overview of the different Rack middleware used by Utopia.
4
+
5
+ ## Static
6
+
7
+ The {ruby Utopia::Static} middleware services static files efficiently. By default, it works with `Rack::Sendfile` and supports `ETag` based caching. Normally, you'd prefer to put static files into `public/_static` but it's also acceptable to put static content into `pages/` if it makes sense.
8
+
9
+ ~~~ ruby
10
+ use Utopia::Static,
11
+ # The root path to serve files from:
12
+ root: "path/to/root",
13
+ # The mime-types to recognize/serve:
14
+ types: [:default, :xiph],
15
+ # Cache-Control header for files:
16
+ cache_control: 'public, max-age=7200'
17
+ ~~~
18
+
19
+ ## Redirection
20
+
21
+ The {ruby Utopia::Redirection} middleware is used for redirecting requests based on patterns and status codes.
22
+
23
+ ~~~ ruby
24
+ # String (fast hash lookup) rewriting:
25
+ use Utopia::Redirection::Rewrite,
26
+ '/' => '/welcome/index'
27
+
28
+ # Redirect directories (e.g. /) to an index file (e.g. /index):
29
+ use Utopia::Redirection::DirectoryIndex,
30
+ index: 'index.html'
31
+
32
+ # Redirect (error) status codes to actual pages:
33
+ use Utopia::Redirection::Errors,
34
+ 404 => '/errors/file-not-found'
35
+ ~~~
36
+
37
+ ## Localization
38
+
39
+ The {ruby Utopia::Localization} middleware provides non-intrusive localization on top of the controller and view layers. The middleware uses the `accept-language` header to guess the preferred locale out of the given options. If a request path maps to a resource, that resource is returned. Otherwise, a non-localized request is made.
40
+
41
+ ~~~ ruby
42
+ use Utopia::Localization,
43
+ :default_locale => 'en',
44
+ :locales => ['en', 'de', 'ja', 'zh']
45
+ ~~~
46
+
47
+ To localize a specific `xnode`, append the locale as a postfix:
48
+
49
+ ~~~
50
+ pages/index.xnode
51
+ pages/index.de.xnode
52
+ pages/index.ja.xnode
53
+ pages/index.zh.xnode
54
+ ~~~
55
+
56
+ You can also access the current locale in the view via {ruby Utopia::Content::Node::Context#localization}.
57
+
58
+ ## Controller
59
+
60
+ The {ruby Utopia::Controller} middleware provides flexible nested controllers with efficient behaviour. Controllers are nested in the `pages` directory and are matched against the incoming request path recursively, from outer most to inner most.
61
+
62
+ ```ruby
63
+ use Utopia::Controller,
64
+ # The root directory where `controller.rb` files can be found.
65
+ root: "path/to/root",
66
+ # The base class to use for all controllers:
67
+ base: Utopia::Controller::Base
68
+ ```
69
+
70
+ A controller is a file within the specified root directory (typically `pages`) with the name `controller.rb`. This code is dynamically loaded into an anonymous class and executed. The default controller has only a single function:
71
+
72
+ ```ruby
73
+ def passthrough(request, path)
74
+ # Call one of:
75
+
76
+ # This will cause the middleware to generate a response.
77
+ # def respond!(response)
78
+
79
+ # This will cause the controller to skip the request.
80
+ # def ignore!
81
+
82
+ # Request relative redirect. Respond with a redirect to the given target.
83
+ # def redirect! (target, status = 302)
84
+
85
+ # Controller relative redirect.
86
+ # def goto!(target, status = 302)
87
+
88
+ # Respond with an error which indiciates some kind of failure.
89
+ # def fail!(error = 400, message = nil)
90
+
91
+ # Succeed the request and immediately respond.
92
+ # def succeed!(status: 200, headers: {}, **options)
93
+ # options may include content: string or body: Enumerable (as per Rack specifications
94
+
95
+ suceed!
96
+ end
97
+ ```
98
+
99
+ The controller layer can do more complex operations by prepending modules into it.
100
+
101
+ ```ruby
102
+ prepend Rewrite, Actions
103
+
104
+ # Extracts an Integer
105
+ rewrite.extract_prefix id: Integer do
106
+ @user = User.find_by_id(@id)
107
+ end
108
+
109
+ on "edit" do |request, path|
110
+ if request.post?
111
+ @user.update_attributes(request[:user])
112
+ end
113
+ end
114
+
115
+ otherwise do |request, path|
116
+ # Executed if no specific named actions were executed.
117
+ succeed!
118
+ end
119
+ ```
120
+
121
+ The incoming path is relative to the path of the controller itself.
122
+
123
+ ## Content
124
+
125
+ The {ruby Utopia::Content} middleware parses XML-style templates with using attributes provided by the controller layer. Dynamic tags can be used to build modular content.
126
+
127
+ ~~~ ruby
128
+ use Utopia::Content
129
+ ~~~
130
+
131
+ A basic template `create.xnode` looks something like:
132
+
133
+ ~~~trenni
134
+ <content:page>
135
+ <content:heading>Create User</content:heading>
136
+ <form action="#">
137
+ <input name="name" />
138
+ <input type="submit" />
139
+ </form>
140
+ </content:page>
141
+ ~~~
142
+
143
+ This template would typically be designed with supporting `_page.xnode` and `_heading.xnode` in the same directory or, more typically, somewhere further up the directory hierarchy.
144
+
145
+ ## Session
146
+
147
+ The {ruby Utopia::Session} middleware provides session storage using encrypted client-side cookies. The session management uses symmetric private key encryption to store data on the client and avoid tampering.
148
+
149
+ ```ruby
150
+ use Utopia::Session,
151
+ expires_after: 3600 * 24,
152
+ # The private key is retried from the `environment.yaml` file:
153
+ secret: UTOPIA.secret_for(:session),
154
+ secure: true
155
+ ```
156
+
157
+ All session data is stored on the client, but it's encrypted with a salt and the secret key. It is impossible for the client to decrypt the data without the secret stored on the server.
@@ -0,0 +1,116 @@
1
+ # Server Setup
2
+
3
+ This guide explains how to deploy a `utopia` web application.
4
+
5
+ ## Deployment
6
+
7
+ The preferred method of deployment to a production server is via git. The `utopia` command assists with setup of a remote git repository on the server. It will setup a `git` `post-update` hook which will deploy the site correctly and restart the application server for that site.
8
+
9
+ To setup a server for deployment:
10
+
11
+ ~~~ bash
12
+ $ mkdir /srv/http/www.example.com
13
+ $ cd /srv/http/www.example.com
14
+ $ sudo -u http utopia server create
15
+ ~~~
16
+
17
+ On your development machine, you should setup the git remote:
18
+
19
+ ~~~ bash
20
+ $ git remote add production ssh://remote/srv/http/www.example.com
21
+ $ git push --set-upstream production main
22
+ ~~~
23
+
24
+ ### Default Environment
25
+
26
+ Utopia will load `config/environment.yaml` and update `ENV` before executing any code. By default, [variant](https://github.com/socketry/variant) is used for handling different environments. You can set default environment values using the `utopia` command:
27
+
28
+ ~~~ bash
29
+ $ utopia environment VARIANT=staging DATABASE_VARIANT=staging1
30
+ ENV["VARIANT"] will default to "staging" unless otherwise specified.
31
+ ENV["DATABASE_VARIANT"] will default to "staging1" unless otherwise specified.
32
+ ~~~
33
+
34
+ To set a value, write `KEY=VALUE`. To unset a key, write `KEY`.
35
+
36
+ When you run `bake` tasks or spawn a server, the values in `config/environment.yaml` will be the defaults. You can override them by manually specifying them, e.g. `DATABASE_ENV=development bake db:info`.
37
+
38
+ ## Platform
39
+
40
+ The best deployment platform for Utopia is Linux, using [falcon](https://github.com/socketry/falcon).
41
+
42
+ ### Sudo Setup
43
+
44
+ Create a file `/etc/sudoers.d/http` with the following contents:
45
+
46
+ ```sudoers
47
+ # Allow user samuel to check out code as user http using git:
48
+ %wheel ALL=(http) NOPASSWD: ALL
49
+ ```
50
+
51
+ This allows the deploy task to correctly checkout code as user `http`.
52
+
53
+ ## Automatic Deployment
54
+
55
+ Automatic deployment allows you to deploy updates to your site when they are committed to a specific branch.
56
+
57
+ ### GitHub Actions
58
+
59
+ Here is a basic workflow, adapted from the [www.codeotaku.com workflow](https://github.com/ioquatix/www.codeotaku.com/blob/main/.github/workflows/development.yml).
60
+
61
+ ~~~ yaml
62
+ name: Development
63
+
64
+ on: [push]
65
+
66
+ jobs:
67
+ test:
68
+ strategy:
69
+ matrix:
70
+ os:
71
+ - ubuntu
72
+
73
+ ruby:
74
+ - "3.2"
75
+
76
+ runs-on: ${{matrix.os}}-latest
77
+
78
+ steps:
79
+ - uses: actions/checkout@v1
80
+ - uses: ruby/setup-ruby@v1
81
+ with:
82
+ ruby-version: ${{matrix.ruby}}
83
+ - uses: actions/cache@v1
84
+ with:
85
+ path: vendor/bundle
86
+ key: ${{runner.os}}-${{matrix.ruby}}-${{hashFiles('**/Gemfile.lock')}}
87
+ restore-keys: |
88
+ ${{runner.os}}-${{matrix.ruby}}-
89
+ - name: Bundle install
90
+ run: |
91
+ sudo apt-get install pkg-config
92
+ gem install bundler:2.1.4
93
+ bundle config path vendor/bundle
94
+ bundle install --jobs 4 --retry 3
95
+ - name: Run tests
96
+ run: ${{matrix.env}} bundle exec sus
97
+
98
+ deploy:
99
+ needs: test
100
+ runs-on: ubuntu-latest
101
+ if: github.ref == 'refs/heads/main'
102
+
103
+ steps:
104
+ - uses: actions/checkout@v1
105
+ - name: Push to remote system
106
+ env:
107
+ DEPLOY_KEY: ${{secrets.deploy_key}}
108
+ run: |
109
+ eval "$(ssh-agent -s)"
110
+ ssh-add - <<< $DEPLOY_KEY
111
+ mkdir ~/.ssh
112
+ ssh-keyscan -H www.oriontransfer.net >> ~/.ssh/known_hosts
113
+ git push -f ssh://http@www.oriontransfer.net/srv/http/www.codeotaku.com/ HEAD:main
114
+ ~~~
115
+
116
+ You will need to add your own DEPLOY_KEY to the GitHub Secrets of your repository andupdate the hostnames and directories to suit your own setup.
@@ -0,0 +1,69 @@
1
+ # Updating Utopia
2
+
3
+ This guide explains how to update existing `utopia` websites.
4
+
5
+ ## Overview
6
+
7
+ Utopia provides a model for both local development (`utopia:site:create`) and deployment (`utopia:server:create`). In addition, Utopia provides a basic upgrade path for existing sites when things within the framework change. These are not always automatic, so below are some recipes for how to update your site.
8
+
9
+ ## Site Update
10
+
11
+ Utopia as a framework introduces changes and versions change according to semantic versioning.
12
+
13
+ ### Controller Update 1.9.x to 2.x
14
+
15
+ The controller layer no longer automatically prepends the `Actions` layer. The following program does a best effort attempt to update existing controllers:
16
+
17
+ ```ruby
18
+ #!/usr/bin/env ruby
19
+
20
+ paths = Dir.glob("**/controller.rb")
21
+
22
+ paths.each do |path|
23
+ lines = File.readlines(path)
24
+
25
+ prepend_line_index = lines.first(5).find_index{|line| line =~ /prepend/}
26
+
27
+ unless prepend_line_index
28
+ puts "Updating #{path}.."
29
+ File.open(path, "w") do |file|
30
+ file.puts "\nprepend Actions"
31
+ file.write lines.join
32
+ end
33
+ else
34
+ prepend_line = lines[prepend_line_index]
35
+
36
+ unless prepend_line =~ /Actions/
37
+ if lines.any?{|line| line =~ /on/}
38
+ lines[prepend_line_index] = "#{prepend_line.chomp}, Actions\n"
39
+
40
+ puts "Updating #{path}.."
41
+ File.open(path, "w") do |file|
42
+ file.write lines.join
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ ```
49
+
50
+ ### View Update 1.9.x to 2.x
51
+
52
+ Dynamic tags in 2.x require namespaces. This affects all `.xnode` files, in particular the following 3 cases:
53
+
54
+ 1. Rewrite `<(/?)(NAME)(\W)` to `<$1content:$2$3` where NAME is a tag which would expand using a `_NAME.xnode` file.
55
+ 2. Rewrite `<content/>` to `<utopia:content/>`. This affects `<node>`, `<deferred>`, `<environment>` tags.
56
+ 3. Rewrite `partial 'NAME'` to be `partial 'content:NAME'`.
57
+
58
+ ## Server Update
59
+
60
+ The utopia server git hooks are updated occasionally to improve the deployment process or to handle changes in the underlying process.
61
+
62
+ You can run the update process on the server to bring the git hooks up to the latest version.
63
+
64
+ ```bash
65
+ $ cd /srv/http/website
66
+ $ utopia server update
67
+ ```
68
+
69
+ You should keep your client and server deployment hooks in sync.
@@ -0,0 +1,41 @@
1
+ # What is XNode?
2
+
3
+ This guide explains the `xnode` view layer and how it can be used to build efficient websites.
4
+
5
+ ## Overview
6
+
7
+ `.xnode` is the file extension for files which are used to render HTML content. The extension is formed from `XML` document fragment which forms a `node` in a tree. These templates are designed to maximise the ratio of content to markup. They improve separation of concerns and semantic organisation because repeated markup can be reused easily.
8
+
9
+ Here is a example of a blog post:
10
+
11
+ ~~~ xml
12
+ <content:entry title="My day as a fish">
13
+ <p>It was not very exciting</p>
14
+ </content:entry>
15
+ ~~~
16
+
17
+ The {Utopia::Content} middleware is built on top of the [Trenni](https://github.com/ioquatix/trenni) template language which uses two-phase evaluation.
18
+
19
+ ## Phase 1: Evaluation
20
+
21
+ Trenni processes the view content by evaluation `#{expressions}` and `<?r statements ?>`. This generates an output buffer. The output buffer should contain valid markup (i.e. balanced tags, no invalid characters).
22
+
23
+ ## Phase 2: Markup
24
+
25
+ Once the template is evaluated to text, it is parsed again into an event stream which is used to generate the final output. The event stream contains things like "open tag", "attribute", "close tag", and so on, and these are fed into the `Utopia::Content` middleware which generates the actual content. Tags without namespaces are output verbatim, while tags with namespaces invoke the tag lookup machinery. This uses the tag name to invoke further behaviour, e.g. inserting more content. Here is a simple example of a basic page:
26
+
27
+ ```xml
28
+ <content:page>
29
+ <content:heading>Welcome to my page</content:heading>
30
+
31
+ <p>This page is so awesome</p>
32
+ </content:page>
33
+ ```
34
+
35
+ In order to render this, you will need two additional files, `_page.xnode` and `_heading.xnode`. As a short example, `_heading.xnode` might look like this:
36
+
37
+ ```xml
38
+ <h1><utopia:content/></h1>
39
+ ```
40
+
41
+ When the parser encounters `<content:heading>...` in the main page, it would evaluate the above template. `<utopia:content/>` is a special tag that evaluates to the content that the parent tag provided, so in this case: `"Welcome to my page"`. Thus, the final output is `<h1>Welcome to my page</h1>`.