sitepress-rails 2.0.0.beta3 → 2.0.0.beta8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 308a11e4d4472d626fb4f81581085c06e6ae98b2334f0540c314fd000be6a6c4
4
- data.tar.gz: 5fcea186abadb31c9ca5d33b392dc6b5b197c7514421f5a07857d253ef34d051
3
+ metadata.gz: 8847542b4eda39498873172a50e932d9f227bb5449ad36d164a47f563eb0d058
4
+ data.tar.gz: 027d9477d21a64e432fa4a5875570cd64b52eb5fb4124479fd360e5a8d5d31fd
5
5
  SHA512:
6
- metadata.gz: f1fa2ccd305669f776a95e9723fa4aede892ecfce34801481761278da67eeccaf12e056e07b256a81dbf2d253d09a6810f1bb906b09c54a79723a3e24de56967
7
- data.tar.gz: 1744e9291b6a268a196baaf57a2f208ddbb59b5ce62839b41bda20ee87debcacd7fa30d224a434a6c55878c7fa68ad74e1845cd21f3fbeb05bbc71937847e454
6
+ metadata.gz: 2f25562abb42ffa015f2f5736c2fe63e54f773f93f3d25fdcc8704eb23e0636fd9bd1a7e7f6834bf179092c1979620e1f96e7f1e761a1be4f3a8c8d2a2ee7591
7
+ data.tar.gz: 5fc33c2a07f472d74f1bcde41041ee833fd9bab24985282ad0e512004d5d0adc27b84c5da4cf567257699fe3f830613e040b8ebb7478760cbc137e1ef7e6d67f
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ .bundle/
2
+ log/*.log
3
+ pkg/
4
+ test/dummy/db/*.sqlite3
5
+ test/dummy/db/*.sqlite3-journal
6
+ test/dummy/log/*.log
7
+ test/dummy/tmp/
data/bin/rails ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
6
+ ENGINE_PATH = File.expand_path('../../lib/sitepress-rails/engine', __FILE__)
7
+
8
+ # Set up gems listed in the Gemfile.
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
10
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
11
+
12
+ require 'rails/all'
13
+ require 'rails/engine/commands'
@@ -4,7 +4,7 @@ module Sitepress
4
4
  # hosts like S3. To achieve this effect, we have to compile `pages/blah.html.haml`
5
5
  # to a folder with the filename `index.html`, so the final path would be `/blah/index.html`
6
6
  class DirectoryIndexPath < IndexPath
7
- def path_with_default_format
7
+ def filename_with_default_format
8
8
  File.join(node.name, "#{node.default_name}.#{format}")
9
9
  end
10
10
  end
@@ -4,16 +4,16 @@ module Sitepress
4
4
  # pages too, mainly grabbing the root, which doesn't have a name in the node, to the default_name
5
5
  # of the node, which is usually `index`.
6
6
  class IndexPath < RootPath
7
- def path_without_format
7
+ def filename_without_format
8
8
  node.name
9
9
  end
10
10
 
11
- def path_with_format
11
+ def filename_with_format
12
12
  "#{node.name}.#{format}"
13
13
  end
14
14
 
15
- def path_with_default_format
16
- path_with_format
15
+ def filename_with_default_format
16
+ filename_with_format
17
17
  end
18
18
  end
19
19
  end
@@ -13,27 +13,31 @@ module Sitepress
13
13
  @resource = resource
14
14
  end
15
15
 
16
- def path
16
+ def filename
17
17
  if format.nil?
18
- path_without_format
18
+ filename_without_format
19
19
  elsif format == node.default_format
20
- path_with_default_format
20
+ filename_with_default_format
21
21
  elsif format
22
- path_with_format
22
+ filename_with_format
23
23
  end
24
24
  end
25
25
 
26
+ def path
27
+ File.join(*resource.lineage, filename)
28
+ end
29
+
26
30
  protected
27
- def path_without_format
31
+ def filename_without_format
28
32
  node.default_name
29
33
  end
30
34
 
31
- def path_with_format
35
+ def filename_with_format
32
36
  "#{node.default_name}.#{format}"
33
37
  end
34
38
 
35
- def path_with_default_format
36
- path_with_format
39
+ def filename_with_default_format
40
+ filename_with_format
37
41
  end
38
42
  end
39
43
  end
@@ -6,63 +6,56 @@ module Sitepress
6
6
  class Compiler
7
7
  include FileUtils
8
8
 
9
- class ResourceCompiler
10
- attr_reader :resource
9
+ attr_reader :site, :root_path
11
10
 
12
- def initialize(resource)
13
- @resource = resource
11
+ def initialize(site:, root_path:, stdout: $stdout)
12
+ @site = site
13
+ @stdout = stdout
14
+ @root_path = Pathname.new(root_path)
15
+ end
16
+
17
+ # Iterates through all pages and writes them to disk
18
+ def compile
19
+ status "Building #{site.root_path.expand_path} to #{root_path.expand_path}"
20
+ resources.each do |resource, path|
21
+ if resource.renderable?
22
+ status " Rendering #{path}"
23
+ File.open(path.expand_path, "w"){ |f| f.write render resource }
24
+ else
25
+ status " Copying #{path}"
26
+ cp resource.asset.path, path.expand_path
27
+ end
28
+ rescue
29
+ status "Error building #{resource.inspect}"
30
+ raise
14
31
  end
32
+ status "Successful build to #{root_path.expand_path}"
33
+ end
34
+
35
+ private
36
+ def resources
37
+ Enumerator.new do |y|
38
+ mkdir_p root_path
15
39
 
16
- def compilation_path
17
- File.join(*resource.lineage, compilation_filename)
40
+ site.resources.each do |resource|
41
+ path = build_path resource
42
+ mkdir_p path.dirname
43
+ y << [resource, path]
44
+ end
45
+ end
18
46
  end
19
47
 
20
- # Compiled assets have a slightly different filename for assets, especially the root node.
21
- def compilation_filename(path_builder: BuildPaths::DirectoryIndexPath, root_path_builder: BuildPaths::RootPath)
22
- path_builder = resource.node.root? ? root_path_builder : path_builder
23
- path_builder.new(resource).path
48
+ def build_path(resource)
49
+ path_builder = resource.node.root? ? BuildPaths::RootPath : BuildPaths::DirectoryIndexPath
50
+ root_path.join path_builder.new(resource).path
24
51
  end
25
52
 
26
- def render(page)
53
+ def render(resource)
27
54
  Renderers::Server.new(resource).render
28
55
  end
29
- end
30
56
 
31
- attr_reader :site
32
-
33
- def initialize(site:, stdout: $stdout)
34
- @site = site
35
- @stdout = stdout
36
- end
37
-
38
- # Iterates through all pages and writes them to disk
39
- def compile(target_path:)
40
- target_path = Pathname.new(target_path)
41
- mkdir_p target_path
42
- cache_resources = @site.cache_resources
43
- @stdout.puts "Compiling #{@site.root_path.expand_path}"
44
-
45
- begin
46
- @site.cache_resources = true
47
- @site.resources.each do |resource|
48
- compiler = ResourceCompiler.new(resource)
49
- path = target_path.join(compiler.compilation_path)
50
- mkdir_p path.dirname
51
- if resource.renderable?
52
- @stdout.puts " Rendering #{path}"
53
- File.open(path.expand_path, "w"){ |f| f.write compiler.render(resource) }
54
- else
55
- @stdout.puts " Copying #{path}"
56
- FileUtils.cp resource.asset.path, path.expand_path
57
- end
58
- rescue => e
59
- @stdout.puts "Error compiling #{resource.inspect}"
60
- raise
61
- end
62
- @stdout.puts "Successful compilation to #{target_path.expand_path}"
63
- ensure
64
- @site.cache_resources = cache_resources
57
+ def status(message)
58
+ @stdout.puts message
65
59
  end
66
- end
67
60
  end
68
61
  end
@@ -20,26 +20,20 @@ module Sitepress
20
20
  # Load paths from `Sitepress#site` into rails so it can render views, helpers, etc. properly.
21
21
  initializer :set_sitepress_paths, before: :set_autoload_paths do |app|
22
22
  app.paths["app/helpers"].push site.helpers_path.expand_path
23
+ app.paths["app/assets"].push site.assets_path.expand_path
23
24
  app.paths["app/views"].push site.root_path.expand_path
24
25
  app.paths["app/views"].push site.pages_path.expand_path
25
26
  end
26
27
 
27
28
  # Configure sprockets paths for the site.
28
- initializer :set_asset_paths, before: :append_assets_path do |app|
29
+ initializer :set_manifest_file_path, before: :append_assets_path do |app|
29
30
  manifest_file = sitepress_configuration.manifest_file_path.expand_path
30
-
31
- if manifest_file.exist?
32
- app.paths["app/assets"].push site.assets_path.expand_path
33
- app.config.assets.precompile << manifest_file.to_s
34
- else
35
- Rails.logger.warn "WARNING: Sitepress could not enable Sprockets because it could not find a manifest file at #{manifest_file.to_s.inspect}."
36
- end
31
+ app.config.assets.precompile << manifest_file.to_s if manifest_file.exist?
37
32
  end
38
33
 
39
34
  # Configure Sitepress with Rails settings.
40
35
  initializer :configure_sitepress do |app|
41
36
  sitepress_configuration.parent_engine = app
42
- sitepress_configuration.cache_resources = app.config.cache_classes
43
37
  end
44
38
 
45
39
  private
@@ -3,11 +3,11 @@ require "sitepress-core"
3
3
  module Sitepress
4
4
  autoload :Compiler, "sitepress/compiler"
5
5
  autoload :RailsConfiguration, "sitepress/rails_configuration"
6
- autoload :RouteConstraint, "sitepress/route_constraint"
7
6
  module Renderers
8
7
  autoload :Controller, "sitepress/renderers/controller"
9
8
  autoload :Server, "sitepress/renderers/server"
10
9
  end
10
+ autoload :RouteConstraint, "sitepress/route_constraint"
11
11
  module BuildPaths
12
12
  autoload :RootPath, "sitepress/build_paths/root_path"
13
13
  autoload :IndexPath, "sitepress/build_paths/index_path"
@@ -17,6 +17,9 @@ module Sitepress
17
17
  # Rescued by ActionController to display page not found error.
18
18
  PageNotFoundError = Class.new(StandardError)
19
19
 
20
+ # Raised when any of the Render subclasses can't render a page.
21
+ RenderingError = Class.new(RuntimeError)
22
+
20
23
  # Make site available via Sitepress.site from Rails app.
21
24
  def self.site
22
25
  configuration.site
@@ -1,18 +1,20 @@
1
1
  module Sitepress
2
2
  module Renderers
3
+ # This would be the ideal way to render Sitepress resources, but there's a lot
4
+ # of hackery involved in getting it to work properly.
3
5
  class Controller
4
- attr_reader :controller, :page
6
+ attr_reader :controller, :resource
5
7
 
6
- def initialize(page, controller = SiteController)
8
+ def initialize(resource, controller = SiteController)
7
9
  @controller = controller
8
- @page = page
10
+ @resource = resource
9
11
  end
10
12
 
11
13
  def render
12
- renderer.render inline: page.body,
13
- type: page.asset.template_extensions.last,
14
+ renderer.render inline: resource.body,
15
+ type: resource.asset.template_extensions.last,
14
16
  layout: resolve_layout,
15
- content_type: page.mime_type.to_s
17
+ content_type: resource.mime_type.to_s
16
18
  end
17
19
 
18
20
  private
@@ -29,15 +31,15 @@ module Sitepress
29
31
  end
30
32
 
31
33
  def renderer
32
- controller.renderer.new("PATH_INFO" => page.request_path)
34
+ controller.renderer.new("PATH_INFO" => resource.request_path)
33
35
  end
34
36
 
35
37
  def resolve_layout
36
- return page.data.fetch("layout") if page.data.key? "layout"
38
+ return resource.data.fetch("layout") if resource.data.key? "layout"
37
39
  return layout unless has_layout_conditions?
38
40
 
39
41
  clause, formats = layout_conditions.first
40
- format = page.format.to_s
42
+ format = resource.format.to_s
41
43
 
42
44
  case clause
43
45
  when :only
@@ -1,21 +1,29 @@
1
1
  module Sitepress
2
2
  module Renderers
3
+ # Renders resources by invoking a rack call to the Rails application. From my
4
+ # experiments rendering as of 2021, this is the most reliable way to render
5
+ # resources. Rendering via `Renderers::Controller` has lots of various subtle issues
6
+ # that are surprising. People don't like surprises, so I opted to render through a
7
+ # slightly heavier stack.
3
8
  class Server
4
- attr_reader :rails_app, :page
9
+ attr_reader :rails_app, :resource
5
10
 
6
- def initialize(page, rails_app = Rails.application)
11
+ def initialize(resource, rails_app = Rails.application)
7
12
  @rails_app = rails_app
8
- @page = page
13
+ @resource = resource
9
14
  end
10
15
 
11
16
  def render
12
17
  code, headers, response = rails_app.routes.call env
13
18
  response.body
19
+ rescue => e
20
+ raise RenderingError.new "Error rendering #{resource.request_path.inspect} at #{resource.asset.path.expand_path.to_s.inspect}:\n#{e.message}"
14
21
  end
15
22
 
23
+ private
16
24
  def env
17
25
  {
18
- "PATH_INFO"=> page.request_path,
26
+ "PATH_INFO"=> resource.request_path,
19
27
  "REQUEST_METHOD"=>"GET",
20
28
  "rack.input" => "GET"
21
29
  }
@@ -0,0 +1,132 @@
1
+ module Sitepress
2
+ # Serves up Sitepress site pages in a rails application. This is mixed into the
3
+ # Sitepress::SiteController, but may be included into other controllers for static
4
+ # page behavior.
5
+ module SitePages
6
+ # Rails 5 requires a format to be given to the private layout method
7
+ # to return the path to the layout.
8
+ DEFAULT_PAGE_RAILS_FORMATS = [:html].freeze
9
+
10
+ # Default root path of resources.
11
+ ROOT_RESOURCE_PATH = "".freeze
12
+
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ rescue_from Sitepress::PageNotFoundError, with: :page_not_found
17
+ helper Sitepress::Engine.helpers
18
+ helper_method :current_page, :site
19
+ before_action :append_relative_partial_path, only: :show
20
+ after_action :reload_site, only: :show
21
+ end
22
+
23
+ def show
24
+ render_page current_page
25
+ end
26
+
27
+ protected
28
+ def render_page(page)
29
+ if page.renderable?
30
+ render_text_resource page
31
+ else
32
+ send_binary_resource page
33
+ end
34
+ end
35
+
36
+ def current_page
37
+ @current_page ||= find_resource
38
+ end
39
+
40
+ def site
41
+ Sitepress.site
42
+ end
43
+
44
+ def page_not_found(e)
45
+ raise ActionController::RoutingError, e.message
46
+ end
47
+
48
+ private
49
+ def append_relative_partial_path
50
+ append_view_path current_page.asset.path.dirname
51
+ end
52
+
53
+ def render_text_resource(resource)
54
+ render inline: resource.body,
55
+ type: resource.asset.template_extensions.last,
56
+ layout: resource.data.fetch("layout", controller_layout),
57
+ content_type: resource.mime_type.to_s
58
+ end
59
+
60
+ def send_binary_resource(resource)
61
+ send_file resource.asset.path,
62
+ disposition: :inline,
63
+ type: resource.mime_type.to_s
64
+ end
65
+
66
+ # Sitepress::PageNotFoundError is handled in the default Sitepress::SiteController
67
+ # with an execption that Rails can use to display a 404 error.
68
+ def get(path)
69
+ resource = site.resources.get(path)
70
+ if resource.nil?
71
+ # TODO: Display error in context of Reources class root.
72
+ raise Sitepress::PageNotFoundError, "No such page: #{path}"
73
+ else
74
+ resource
75
+ end
76
+ end
77
+
78
+ # Default finder of the resource for the current controller context. If the :resource_path
79
+ # isn't present, then its probably the root path so grab that.
80
+ def find_resource
81
+ get params.fetch(:resource_path, ROOT_RESOURCE_PATH)
82
+ end
83
+
84
+ # Returns the current layout for the inline Sitepress renderer. This is
85
+ # exposed via some really convoluted private methods inside of the various
86
+ # versions of Rails, so I try my best to hack out the path to the layout below.
87
+ def controller_layout
88
+ private_layout_method = self.method(:_layout)
89
+ layout =
90
+ if Rails.version >= "6"
91
+ private_layout_method.call lookup_context, current_page_rails_formats
92
+ elsif Rails.version >= "5"
93
+ private_layout_method.call current_page_rails_formats
94
+ else
95
+ private_layout_method.call
96
+ end
97
+
98
+ if layout.instance_of? String # Rails 4 and 5 return a string from above.
99
+ layout
100
+ elsif layout # Rails 3 and older return an object that gives us a file name
101
+ File.basename(layout.identifier).split('.').first
102
+ else
103
+ # If none of the conditions are met, then no layout was
104
+ # specified, so nil is returned.
105
+ nil
106
+ end
107
+ end
108
+
109
+ # Rails 5 requires an extension, like `:html`, to resolve a template. This
110
+ # method returns the intersection of the formats Rails supports from Mime::Types
111
+ # and the current page's node formats. If nothing intersects, HTML is returned
112
+ # as a default.
113
+ def current_page_rails_formats
114
+ extensions = current_page.node.formats.extensions
115
+ supported_extensions = extensions & Mime::EXTENSION_LOOKUP.keys
116
+
117
+ if supported_extensions.empty?
118
+ DEFAULT_PAGE_RAILS_FORMATS
119
+ else
120
+ supported_extensions.map?(&:to_sym)
121
+ end
122
+ end
123
+
124
+ def reload_site
125
+ site.reload! if reload_site?
126
+ end
127
+
128
+ def reload_site?
129
+ !Rails.configuration.cache_classes
130
+ end
131
+ end
132
+ end