sitepress-rails 2.0.0 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -19
  3. data/bin/rails +2 -2
  4. data/lib/sitepress/engine.rb +2 -0
  5. data/lib/sitepress/model.rb +69 -0
  6. data/lib/sitepress/models/collection.rb +34 -0
  7. data/lib/sitepress/rails.rb +14 -1
  8. data/lib/sitepress/rails_configuration.rb +1 -4
  9. data/lib/sitepress/route_constraint.rb +3 -1
  10. data/lib/sitepress/routing_mapper.rb +31 -0
  11. data/rails/app/controllers/concerns/sitepress/site_pages.rb +2 -1
  12. data/rails/lib/generators/sitepress/controller/USAGE +8 -0
  13. data/rails/lib/generators/sitepress/controller/controller_generator.rb +19 -0
  14. data/rails/lib/generators/sitepress/controller/templates/controllers/site_controller.rb +17 -0
  15. data/rails/lib/generators/sitepress/install/USAGE +10 -0
  16. data/rails/lib/generators/sitepress/install/install_generator.rb +14 -0
  17. data/rails/lib/generators/sitepress/install/templates/helpers/page_helper.rb +29 -0
  18. data/rails/lib/generators/sitepress/install/templates/models/page_model.rb +4 -0
  19. data/rails/lib/generators/sitepress/install/templates/pages/index.html.erb +43 -0
  20. data/rails/test/lib/generators/sitepress/controller_generator_test.rb +14 -0
  21. data/rails/test/lib/generators/sitepress/install_generator_test.rb +14 -0
  22. data/sitepress-rails.gemspec +3 -2
  23. data/spec/dummy/app/content/models/page_model.rb +4 -0
  24. data/spec/dummy/config/routes.rb +1 -0
  25. data/spec/dummy/log/test.log +175 -81066
  26. data/spec/sitepress/model_spec.rb +35 -0
  27. data/spec/sitepress/routes_spec.rb +7 -22
  28. data/spec/sitepress-rails_spec.rb +7 -22
  29. data/spec/spec_helper.rb +15 -0
  30. metadata +24 -12
  31. data/rails/config/routes.rb +0 -12
  32. data/spec/dummy/db/test.sqlite3 +0 -0
  33. data/spec/dummy/log/production.log +0 -1976
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e02f24add74ef6493bdea2486f8d9e2eb4b24f6070ba72bfd3c0b15dc5ad09da
4
- data.tar.gz: 149b6a137086b0c65dd5fe3c6d91bb1ff3b5c0acc6fddcf45d9a39c4583cf183
3
+ metadata.gz: 6c58152c6114a8b56271090b027096f9eb411e4d2e6eedb4811352b831d4be73
4
+ data.tar.gz: b6535af5b029475d05e4e2fc9a3354a41b6f8d33d2533eddd1119f8f080e7409
5
5
  SHA512:
6
- metadata.gz: e9990fc71151cba0e82e4c9b2745dd57c91dcbd18fb42b5cd815bbd6aa852c60847fedf63bd720199e2e3ddab2f2cc77096a3b96efc4c56b40bfda41ec7193be
7
- data.tar.gz: a8737c1757a5da80edcbdf2dea6cb0c48a204577c083c3bbd219124870ca69eb12d1a6fdfa79d55be12b3f1ff3dd0db18d909e6deb03727e03ae664a49192bf6
6
+ metadata.gz: 75ebd2f408e8510263741b33052db92da2b91510bad929cc5f1770134666dca9ee08bccf0b0ae4bad668f8683209a4ee148e5fd68ce5240c73b46ac7a8b48338
7
+ data.tar.gz: 9aa98bcba2c6f0332e1c5e7ef71986c40d3cb3a84cd8e97efc32fe811a6e9ff557384fe3e89fa4dbcbe4fc1852a97ad5cd26945b8178bcd4de0bba03469683ac
data/README.md CHANGED
@@ -4,37 +4,27 @@ Sitepress is a file-backed website content manager that can be embedded in popul
4
4
 
5
5
  ## Installation
6
6
 
7
- Add this line to your application's Gemfile:
7
+ Add `sitepress-rails` to a Rails application by running:
8
8
 
9
9
  ```ruby
10
- gem 'sitepress-rails'
10
+ bundle add sitepress-rails
11
11
  ```
12
12
 
13
- And then execute:
13
+ The install content pages by running:
14
14
 
15
15
  ```bash
16
- $ bundle
16
+ $ ./bin/rails generate sitepress:install
17
17
  ```
18
18
 
19
- Then mount the engine into your `config/routes.rb` file:
19
+ This command creates a few content pages and adds the following to the `config/routes.rb` file:
20
20
 
21
21
  ```ruby
22
- mount Sitepress::Engine => "/"
22
+ sitepress_pages
23
+ sitepress_root # Delete if you don't want your app's root page to be a content page.
23
24
  ```
24
25
 
25
- Create the `app/content/pages` in your rails project:
26
-
27
- ```bash
28
- $ mkdir -p app/content/pages
29
- ```
30
-
31
- Then add pages to the `app/content/pages` directory:
32
-
33
- ```bash
34
- $ echo "<h1>Hello</h1><p>It is <%= Time.now %> o'clock</p>" > app/content/pages/hello.html.erb
35
- ```
36
-
37
- Point your browser to `http://127.0.0.1:3000/hello` and if all went well you should see the page you just created.
26
+ Restart the Rails application server and point your browser to `http://127.0.0.1:3000/` and if all went well you should see a sitepress page.
38
27
 
39
28
  ## License
29
+
40
30
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/bin/rails CHANGED
@@ -2,8 +2,8 @@
2
2
  # This command will automatically be run when you run "rails" with Rails gems
3
3
  # installed from the root of your application.
4
4
 
5
- ENGINE_ROOT = File.expand_path('../..', __FILE__)
6
- ENGINE_PATH = File.expand_path('../../lib/sitepress-rails/engine', __FILE__)
5
+ ENGINE_ROOT = File.expand_path('../../rails', __FILE__)
6
+ ENGINE_PATH = File.expand_path('../../lib/sitepress/engine', __FILE__)
7
7
 
8
8
  # Set up gems listed in the Gemfile.
9
9
  ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
@@ -1,4 +1,5 @@
1
1
  require "rails/engine"
2
+ require "sitepress/routing_mapper"
2
3
 
3
4
  module Sitepress
4
5
  class Engine < ::Rails::Engine
@@ -23,6 +24,7 @@ module Sitepress
23
24
  app.paths["app/assets"].push site.assets_path.expand_path
24
25
  app.paths["app/views"].push site.root_path.expand_path
25
26
  app.paths["app/views"].push site.pages_path.expand_path
27
+ app.paths["app/models"].push site.models_path.expand_path
26
28
  end
27
29
 
28
30
  # Configure sprockets paths for the site.
@@ -0,0 +1,69 @@
1
+ module Sitepress
2
+ # Wraps a page in a class, which makes it much easier to decorate and validate.
3
+ class Model
4
+ attr_reader :page
5
+
6
+ delegate \
7
+ :request_path,
8
+ :data,
9
+ :body,
10
+ to: :page
11
+
12
+ def initialize(page)
13
+ @page = page
14
+ end
15
+
16
+ # Treat as equal if the resource and model class are the same.
17
+ def ==(model)
18
+ self.page == model.page and self.class == model.class
19
+ end
20
+
21
+ class << self
22
+ delegate \
23
+ :first,
24
+ to: :all
25
+
26
+ # Defines a class method that may be called later to return a
27
+ # collection of objects. The default glob, for example, is named `:all`,
28
+ # which defines `MyModel.all` on the class.
29
+ def collection(name = Models::Collection::DEFAULT_NAME, glob:, **kwargs)
30
+ define_singleton_method name do
31
+ self.glob glob, **kwargs
32
+ end
33
+ end
34
+
35
+ # Adhoc querying of models via `Model.glob("foo/bar").all`
36
+ def glob(glob, **kwargs)
37
+ Models::Collection.new model: self, site: site, glob: glob, **kwargs
38
+ end
39
+
40
+ # Wraps a page in a class if given a string that represents the path or
41
+ # a page object itself.
42
+ def get(page)
43
+ case page
44
+ when Model
45
+ page
46
+ when String
47
+ new site.get page
48
+ when Sitepress::Resource
49
+ new page
50
+ else
51
+ raise ModelNotFoundError, "#{self.inspect} could not find #{page.inspect}"
52
+ end
53
+ end
54
+ alias :find :get
55
+
56
+ def data(*keys, default: nil)
57
+ keys.each do |key|
58
+ define_method key do
59
+ self.data.fetch key.to_s, default
60
+ end
61
+ end
62
+ end
63
+
64
+ def site
65
+ Sitepress.site
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,34 @@
1
+ module Sitepress
2
+ module Models
3
+ # Everything needed to iterate over a set of resources from a glob and wrap
4
+ # them in a model so they are returned as a sensible enumerable.
5
+ class Collection
6
+ include Enumerable
7
+
8
+ # Page models will have `PageModel.all` method defined by default.
9
+ DEFAULT_NAME = :all
10
+
11
+ # Iterate over all resources in the site by default.
12
+ DEFAULT_GLOB = "**/*.*".freeze
13
+
14
+ attr_reader :model, :glob, :site
15
+
16
+ def initialize(model:, site:, glob: DEFAULT_GLOB)
17
+ @model = model
18
+ @glob = glob
19
+ @site = site
20
+ end
21
+
22
+ def resources
23
+ site.glob glob
24
+ end
25
+
26
+ # Wraps each resource in a model object.
27
+ def each
28
+ resources.each do |resource|
29
+ yield model.new resource
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -2,6 +2,10 @@ require "sitepress-core"
2
2
 
3
3
  module Sitepress
4
4
  autoload :Compiler, "sitepress/compiler"
5
+ autoload :Model, "sitepress/model"
6
+ module Models
7
+ autoload :Collection, "sitepress/models/collection"
8
+ end
5
9
  autoload :RailsConfiguration, "sitepress/rails_configuration"
6
10
  module Renderers
7
11
  autoload :Controller, "sitepress/renderers/controller"
@@ -15,8 +19,17 @@ module Sitepress
15
19
  autoload :DirectoryIndexPath, "sitepress/build_paths/directory_index_path"
16
20
  end
17
21
 
22
+ # Base class for errors if Sitepress can't find a resource, model, etc.
23
+ NotFoundError = Class.new(StandardError)
24
+
18
25
  # Rescued by ActionController to display page not found error.
19
- ResourceNotFound = Class.new(StandardError)
26
+ ResourceNotFoundError = Class.new(NotFoundError)
27
+ # Accidentally left out `Error` in the constant name, so I'm setting
28
+ # that up here for backwards compatability.
29
+ ResourceNotFound = ResourceNotFoundError
30
+
31
+ # Raised if a model isn't found.
32
+ ModelNotFoundError = Class.new(NotFoundError)
20
33
 
21
34
  # Raised when any of the Render subclasses can't render a page.
22
35
  RenderingError = Class.new(RuntimeError)
@@ -6,13 +6,10 @@ module Sitepress
6
6
  # Store in ./app/content by default.
7
7
  DEFAULT_SITE_ROOT = "app/content".freeze
8
8
 
9
- attr_accessor :routes, :cache_resources
9
+ attr_accessor :cache_resources
10
10
  attr_writer :site, :parent_engine
11
11
 
12
12
  def initialize
13
- # Injects routes into parent apps routes when set to true. Set to false
14
- # to inject routes manually.
15
- self.routes = true
16
13
  # Caches sites between requests. Set to `false` for development environments.
17
14
  self.cache_resources = true
18
15
  end
@@ -1,12 +1,14 @@
1
1
  module Sitepress
2
2
  # Route constraint for rails routes.rb file.
3
3
  class RouteConstraint
4
+ attr_reader :site
5
+
4
6
  def initialize(site: Sitepress.site)
5
7
  @site = site
6
8
  end
7
9
 
8
10
  def matches?(request)
9
- !!@site.resources.get(request.path)
11
+ !!site.resources.get(request.path)
10
12
  end
11
13
  end
12
14
  end
@@ -0,0 +1,31 @@
1
+ module ActionDispatch::Routing
2
+ # I have no idea how or why this works this way, I lifted the pattern from Devise, which came with even
3
+ # more weird stuff. Rails could use an API for adding route helpers to decrease the brittleness of this
4
+ # approach. For now, deal with this helper.
5
+ class Mapper
6
+ DEFAULT_CONTROLLER = "sitepress/site".freeze
7
+ DEFAULT_ACTION = "show".freeze
8
+ ROUTE_GLOB_KEY = "/*resource_path".freeze
9
+
10
+ # Hook up all the Sitepress pages
11
+ def sitepress_pages(controller: DEFAULT_CONTROLLER, action: DEFAULT_ACTION, root: false, constraints: Sitepress::RouteConstraint.new)
12
+ get ROUTE_GLOB_KEY,
13
+ controller: controller,
14
+ action: action,
15
+ as: :page,
16
+ format: false,
17
+ constraints: constraints
18
+
19
+ sitepress_root controller: controller, action: action if root
20
+ end
21
+
22
+ # Hook sitepress root up to the index of rails.
23
+ def sitepress_root(controller: DEFAULT_CONTROLLER, action: DEFAULT_ACTION)
24
+ if has_named_route? :root
25
+ Rails.logger.warn "Sitepress tried to configured the 'root' route, but it was already defined. Check the 'routes.rb' file for a 'root' route or call 'sitepress_pages(root: false)'."
26
+ else
27
+ root controller: controller, action: action
28
+ end
29
+ end
30
+ end
31
+ end
@@ -71,7 +71,7 @@ module Sitepress
71
71
  # Send the inline rendered, post-processed string into the Rails rendering method that actually sends
72
72
  # the output to the end-user as a web response.
73
73
  def post_render(rendition)
74
- render inline: rendition.output, content_type: rendition.mime_type
74
+ render body: rendition.output, content_type: rendition.mime_type
75
75
  end
76
76
 
77
77
  # A reference to the current resource that's being requested.
@@ -116,6 +116,7 @@ module Sitepress
116
116
  if resource.nil?
117
117
  raise Sitepress::ResourceNotFound, "No such page: #{path}"
118
118
  else
119
+ Rails.logger.info "Sitepress resolved asset #{resource.asset.path}"
119
120
  resource
120
121
  end
121
122
  end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Installs a controller that can be used to manipulate Sitepress responses.
3
+
4
+ Example:
5
+ bin/rails generate sitepress:controller SiteController
6
+
7
+ This will create:
8
+ app/controllers/site_controller.rb
@@ -0,0 +1,19 @@
1
+ class Sitepress::ControllerGenerator < Rails::Generators::Base
2
+ source_root File.expand_path("templates", __dir__)
3
+
4
+ def copy_files
5
+ directory ".", "app"
6
+ end
7
+
8
+ def append_controller_to_sitepress_root_route
9
+ inject_into_file "config/routes.rb", after: "sitepress_root" do
10
+ " controller: :site"
11
+ end
12
+ end
13
+
14
+ def append_controller_to_sitepress_pages_route
15
+ inject_into_file "config/routes.rb", after: "sitepress_pages" do
16
+ " controller: :site"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ class SiteController < Sitepress::SiteController
2
+ # Override this method to implement your processing logic for Sitepress pages.
3
+ def show
4
+ render_resource current_resource
5
+ end
6
+
7
+ protected
8
+
9
+ # This is to be used by end users if they need to do any post-processing on the rendering page.
10
+ # For example, the user may use Nokogiri to parse static HTML pages and hook it into the asset pipeline.
11
+ # They may also use tools like `HTMLPipeline` to process links from a markdown renderer.
12
+ #
13
+ # For example, the rendition could be modified via `Nokogiri::HTML5::DocumentFragment(rendition)`.
14
+ def process_rendition(rendition)
15
+ # Do nothing unless the user extends this method.
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ Description:
2
+ Installs Sitepress in a Rails appliation
3
+
4
+ Example:
5
+ bin/rails generate sitepress:install
6
+
7
+ This will create:
8
+ app/content/models/page_model.rb
9
+ app/content/helpers/page_helpers.rb
10
+ app/content/pages/index.html.erb
@@ -0,0 +1,14 @@
1
+ module Sitepress
2
+ class InstallGenerator < Rails::Generators::Base
3
+ source_root File.expand_path("templates", __dir__)
4
+
5
+ def copy_files
6
+ directory ".", "app/content"
7
+ end
8
+
9
+ def add_sitepress_routes
10
+ route "sitepress_root"
11
+ route "sitepress_pages"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ module PageHelper
2
+ # Creates a hyperlink to a page using the `title` key. Change the default in the args
3
+ # below if you use a different key for page titles.
4
+ def link_to_page(page, title_key: "title")
5
+ link_to page.data.fetch(title_key, page.request_path), page.request_path
6
+ end
7
+
8
+ # Quick and easy way to change the class of a page if its current. Useful for
9
+ # navigation menus.
10
+ def link_to_if_current(text, page, active_class: "active")
11
+ if page == current_page
12
+ link_to text, page.request_path, class: active_class
13
+ else
14
+ link_to text, page.request_path
15
+ end
16
+ end
17
+
18
+ # Conditionally renders the block if an arg is present. If all the args are nil,
19
+ # the block is not rendered. Handy for laconic templating languages like slim, haml, etc.
20
+ def with(*args, &block)
21
+ block.call(*args) unless args.all?(&:nil?)
22
+ end
23
+
24
+ # Render a block within a layout. This is a useful, and prefered way, to handle
25
+ # nesting layouts, within Sitepress.
26
+ def render_layout(layout, **kwargs, &block)
27
+ render html: capture(&block), layout: "layouts/#{layout}", **kwargs
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ class PageModel < Sitepress::Model
2
+ collection glob: "**/*.html*"
3
+ data :title
4
+ end
@@ -0,0 +1,43 @@
1
+ ---
2
+ title: Getting started with Sitepress in Rails
3
+ ---
4
+
5
+ <style>
6
+ .sitepress {
7
+ max-width: 90%;
8
+ }
9
+ .sitepress h1 {
10
+ font-weight: bold;
11
+ font-size: 3rem;
12
+ margin-bottom: 4rem;
13
+ }
14
+ .sitepress h2 {
15
+ font-weight: bold;
16
+ font-size: 1.5rem;
17
+ }
18
+ .sitepress * {
19
+ margin-top: 1rem;
20
+ }
21
+ .sitepress code {
22
+ font-family: monospace;
23
+ font-size: 1rem;
24
+ }
25
+ .sitepress a {
26
+ text-decoration: underline;
27
+ }
28
+ </style>
29
+
30
+ <article class="sitepress">
31
+ <h1><%= current_page.data.fetch("title") %></h1>
32
+
33
+ <p>This was generated by the <code>rails generate sitepress:install</code> command.</p>
34
+
35
+ <h2>Content</h2>
36
+ <p>Sitepress content files are in <code><%= site.root_path.expand_path %></code>. There you can edit site content, including this file at <code><%= current_page.asset.path %></code>.
37
+
38
+ <h2>Routes</h2>
39
+ <p>Sitepress defined a root route in <code><%= Rails.root.join("config/routes.rb") %></code>. If that conflicts with your routes configuration, you'll want to open it and edit accordingly.</p>
40
+
41
+ <h2>Documentation</h2>
42
+ <p>Check out the <a href="https://sitepress.cc">Sitepress website</a> for help on getting started and documentation. Since Sitepress is also built on top of Rails, most of the <a href="https://guides.rubyonrails.org/action_view_helpers.html">Rails view helpers</a> work too.</p>
43
+ </article>
@@ -0,0 +1,14 @@
1
+ require "test_helper"
2
+ require "generators/sitepress/controller/controller_generator"
3
+
4
+ class Sitepress::ControllerGeneratorTest < Rails::Generators::TestCase
5
+ tests Sitepress::ControllerGenerator
6
+ destination Rails.root.join("tmp/generators")
7
+ setup :prepare_destination
8
+
9
+ # test "generator runs without errors" do
10
+ # assert_nothing_raised do
11
+ # run_generator ["arguments"]
12
+ # end
13
+ # end
14
+ end
@@ -0,0 +1,14 @@
1
+ require "test_helper"
2
+ require "generators/sitepress/install/install_generator"
3
+
4
+ class Sitepress::InstallGeneratorTest < Rails::Generators::TestCase
5
+ tests Sitepress::InstallGenerator
6
+ destination Rails.root.join("tmp/generators")
7
+ setup :prepare_destination
8
+
9
+ # test "generator runs without errors" do
10
+ # assert_nothing_raised do
11
+ # run_generator ["arguments"]
12
+ # end
13
+ # end
14
+ end
@@ -19,14 +19,15 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
  spec.test_files = Dir["spec/**/*"]
21
21
 
22
+ rails_version = ">= 6.0"
23
+
22
24
  spec.add_development_dependency "rspec-rails"
23
25
  spec.add_development_dependency "pry"
24
- spec.add_development_dependency "rails", ">= 4.0"
26
+ spec.add_development_dependency "rails", rails_version
25
27
 
26
28
  spec.add_runtime_dependency "sitepress-core", spec.version
27
29
 
28
30
  # We don't need every single rals rependency, so grab the subset here.
29
- rails_version = ">= 6.0"
30
31
  spec.add_dependency "railties", rails_version
31
32
  spec.add_dependency "actionpack", rails_version
32
33
  spec.add_dependency "sprockets-rails", ">= 2.0.0"
@@ -0,0 +1,4 @@
1
+ class PageModel < Sitepress::Model
2
+ collection glob: "**/*.html*"
3
+ data :title
4
+ end
@@ -1,3 +1,4 @@
1
1
  Rails.application.routes.draw do
2
2
  get "/baseline/render", to: "baseline#show"
3
+ sitepress_pages
3
4
  end