sitepress-core 5.0.0.beta1 → 5.0.0.beta3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ca9aab55ae45bf39216231a491ffa4f432682c2b5549e0905d9561436e57b5a
4
- data.tar.gz: 42fb6f8c6b22a8e18895689a3a5d792e1c8daabd899be7530ad86d4a84c926dd
3
+ metadata.gz: f8f487224acfb9abed8c58d92458335235c1a3643dd7ff379181ff314c150cc6
4
+ data.tar.gz: 8105632137100f27f86f542a9215b634a72eae4ae824da2472015af14d3cbee2
5
5
  SHA512:
6
- metadata.gz: 846b3c0ae6ef4b30405cd1aece7eafcd5650ce7df355bbeb65675e25ddfa55d85baf7b7b4462a64eb3f13c9fbc4e8ef2cf661d1e52294d8f05a4aa637f2c9448
7
- data.tar.gz: 26ff50a30859fa7160793d36e6353d207fe980b6ae8c898a5657527508ebc5bc9774b8bd8a012b7252cbe4082eeaa347afb21c0830abb4d2447f294d305780e0
6
+ metadata.gz: 6f7385c3f09cb8af5af3fcffeca64c73eebe82d513a23e015d05161b1b06b28d00f2687a06f2c94c7295b560e3f75bcf7cebcf0de25ae42ba45af0a4af881c7a
7
+ data.tar.gz: 641f2e5b2da44d0f3c86ea390dd4a089c4a27739a7a16cfd34d93932e1c934b9b5fdbe06d66652a6d3681e217c665b8799b38f712760e201154f67e5533c5258
@@ -46,7 +46,7 @@ module Sitepress
46
46
  def_delegators :@hash, :keys, :values, :key?
47
47
 
48
48
  def initialize(hash)
49
- @hash = hash
49
+ @hash = hash || {}
50
50
  end
51
51
 
52
52
  def fetch(key, *args, &block)
@@ -30,8 +30,10 @@ module Sitepress
30
30
  end
31
31
 
32
32
  def process_asset(path, node)
33
- asset = source_for(path)
34
- node.child(asset.node_name).resources.add_asset(asset, format: asset.format)
33
+ source = source_for(path)
34
+ # Parse the path to get node_name and format for tree building
35
+ parsed_path = Path.new(path.to_s)
36
+ node.child(parsed_path.node_name).resources.add_asset(source, format: parsed_path.format)
35
37
  end
36
38
 
37
39
  def source_for(path)
@@ -1,4 +1,3 @@
1
- require "mime/types"
2
1
  require "fastimage"
3
2
 
4
3
  module Sitepress
@@ -10,35 +9,17 @@ module Sitepress
10
9
  # image.height # => 1080
11
10
  # image.data["width"] # => 1920
12
11
  #
13
- class Image
12
+ class Image < Static
14
13
  MIME_TYPES = %w[image/png image/jpeg image/gif image/webp].freeze
15
14
 
16
15
  def self.mime_types
17
16
  MIME_TYPES
18
17
  end
19
18
 
20
- attr_reader :path
21
-
22
- def initialize(path:)
23
- @path = Pathname.new(path)
24
- end
25
-
26
19
  def filename
27
20
  path.basename.to_s
28
21
  end
29
22
 
30
- def node_name
31
- path.basename(".*").to_s.split(".").first
32
- end
33
-
34
- def format
35
- path.extname.delete(".").to_sym
36
- end
37
-
38
- def mime_type
39
- MIME::Types.type_for(path.to_s).first
40
- end
41
-
42
23
  def size
43
24
  exists? ? File.size(path) : nil
44
25
  end
@@ -58,18 +39,6 @@ module Sitepress
58
39
  }.compact)
59
40
  end
60
41
 
61
- def body
62
- File.binread(path)
63
- end
64
-
65
- def exists?
66
- path.exist?
67
- end
68
-
69
- def inspect
70
- "#<#{self.class}:0x#{object_id.to_s(16)} path=#{path.to_s.inspect}>"
71
- end
72
-
73
42
  private
74
43
 
75
44
  def dimensions
@@ -1,17 +1,11 @@
1
- require "mime/types"
2
- require "forwardable"
3
1
  require "fileutils"
2
+ require "forwardable"
4
3
 
5
4
  module Sitepress
6
- # Represents a page on a website - a file that may be parsed to extract
7
- # metadata or be renderable via a template. Multiple resources
8
- # may point to the same page. Properties of a page should be mutable.
9
- # The Resource object is immutable and may be modified by the Resources proxy.
10
- class Page
11
- # If we can't resolve a mime type for the resource, we'll fall
12
- # back to this binary octet-stream type so the client can download
13
- # the resource and figure out what to do with it.
14
- DEFAULT_MIME_TYPE = MIME::Types["application/octet-stream"].first
5
+ # A source for text-based files that may have frontmatter.
6
+ # Handles parsing of YAML frontmatter and provides access to data and body.
7
+ class Page < Static
8
+ extend Forwardable
15
9
 
16
10
  # MIME types that Page can handle - text-based content that may have frontmatter
17
11
  MIME_TYPES = %w[
@@ -36,18 +30,12 @@ module Sitepress
36
30
  # documents, JSON, exif data on images, etc.
37
31
  DEFAULT_PARSER = Parsers::Frontmatter
38
32
 
39
- attr_reader :path
40
33
  attr_writer :body
41
34
 
42
- extend Forwardable
43
- def_delegators :renderer, :render
44
- def_delegators :path, :handler, :node_name, :format, :exists?
45
-
46
- def initialize(path:, mime_type: nil, parser: DEFAULT_PARSER)
47
- # The MIME::Types gem returns an array when types are looked up.
48
- # This grabs the first one, which is likely the intent on these lookups.
49
- @mime_type = Array(mime_type).first
50
- @path = Path.new path
35
+ def_delegator :renderer, :render, :serialize
36
+
37
+ def initialize(path:, parser: DEFAULT_PARSER)
38
+ super(path: path)
51
39
  @parser_klass = parser
52
40
  end
53
41
 
@@ -69,26 +57,11 @@ module Sitepress
69
57
  exists? ? parser.body_line_offset : 1
70
58
  end
71
59
 
72
- # Treat resources with the same request path as equal.
60
+ # Treat sources with the same path as equal.
73
61
  def ==(other)
74
62
  path == other.path
75
63
  end
76
64
 
77
- def inspect
78
- "#<#{self.class}:0x#{object_id.to_s(16)} path=#{path.to_s.inspect}>"
79
- end
80
-
81
- def mime_type
82
- @mime_type ||= inferred_mime_type || DEFAULT_MIME_TYPE
83
- end
84
-
85
- # Certain files, like binary file types, aren't something that we should try to
86
- # parse. When this returns true in some cases, a reference to the file will be
87
- # passed and skip all the overhead of trying to parse and render.
88
- def renderable?
89
- !!handler
90
- end
91
-
92
65
  # When changing the parser, clear all cached parsed data.
93
66
  def parser=(parser_klass)
94
67
  @parser = nil
@@ -110,42 +83,23 @@ module Sitepress
110
83
  end
111
84
 
112
85
  def save
113
- File.write path, render
86
+ File.write path, serialize
114
87
  end
115
88
 
116
89
  def renderer
117
90
  @parser_klass::Renderer.new(data: data, body: body)
118
91
  end
119
92
 
120
- # Renders the page in a view context. This is part of the Renderable protocol
121
- # that allows any object to be used as a resource source.
122
- def render_in(view_context)
123
- template = ActionView::Template.new(
124
- body,
125
- path.to_s,
126
- ActionView::Template.handler_for_extension(handler),
127
- locals: []
128
- )
129
- template.render(view_context, {})
130
- end
131
-
132
93
  private
133
94
  def parse_error(&parse)
134
95
  parse.call
135
96
  rescue StandardError => e
136
- raise ParseError, "Error parsing #{path.expand_path}: #{e.class} - #{e.message}"
97
+ raise ParseError, "Error parsing #{File.expand_path(path)}: #{e.class} - #{e.message}"
137
98
  end
138
99
 
139
100
  def parser
140
101
  @parser ||= @parser_klass.new File.read path
141
102
  end
142
-
143
- # Returns the mime type of the file extension. If a type can't
144
- # be resolved then we'll just grab the first type.
145
- def inferred_mime_type
146
- format_extension = path.format&.to_s
147
- MIME::Types.type_for(format_extension).first if format_extension
148
- end
149
103
  end
150
104
 
151
105
  # Backwards compatibility
@@ -1,18 +1,23 @@
1
1
  require "forwardable"
2
+ require "mime/types"
2
3
 
3
4
  module Sitepress
4
- # Represents the request path of an asset. There may be multiple
5
- # resources that point to the same asset. Resources are immutable
6
- # and may be altered by the resource proxy.
5
+ # Represents the web-facing view of a source file. A Resource wraps a source
6
+ # and adds web-serving concerns: request path, handler, format, and rendering.
7
7
  #
8
- # The source can be any object that implements the Renderable protocol:
9
- # - #data - returns a Hash of metadata
10
- # - #render_in(view_context) - renders the content
8
+ # Source = file on disk (Pathname)
9
+ # Resource = web representation (Path parsing, rendering)
11
10
  class Resource
12
11
  extend Forwardable
13
12
  def_delegators :source, :body
14
13
 
15
- attr_reader :node, :source
14
+ # If we can't resolve a mime type for the resource, we'll fall
15
+ # back to this binary octet-stream type so the client can download
16
+ # the resource and figure out what to do with it.
17
+ DEFAULT_MIME_TYPE = MIME::Types["application/octet-stream"].first
18
+
19
+ attr_reader :node, :source, :source_path
20
+
16
21
  alias :asset :source # Backwards compatibility
17
22
 
18
23
  # Check if the source implements the data protocol.
@@ -20,14 +25,14 @@ module Sitepress
20
25
  source.respond_to?(:data)
21
26
  end
22
27
 
23
- # Check if the source implements the render_in protocol.
24
- def renderable?
25
- source.respond_to?(:render_in)
28
+ # Delegate to source.
29
+ def data
30
+ has_data? ? source.data : Data.manage({})
26
31
  end
27
32
 
28
- # Returns metadata from the source, or an empty hash if not available.
29
- def data
30
- has_data? ? source.data : {}
33
+ # Delegate to source.
34
+ def fetch_data(key, *args, &block)
35
+ source.fetch_data(key, *args, &block)
31
36
  end
32
37
 
33
38
  attr_accessor :format, :mime_type, :handler
@@ -39,9 +44,27 @@ module Sitepress
39
44
  @source = source || asset
40
45
  raise ArgumentError, "Either asset: or source: must be provided" unless @source
41
46
  @node = node
42
- @format = format || @source.format
43
- @mime_type = mime_type || @source.mime_type
44
- @handler = handler || source_handler
47
+ # Parse the source path to extract handler, format, node_name (if source has path)
48
+ if @source.respond_to?(:path)
49
+ @source_path = Path.new(@source.path.to_s)
50
+ @format = format || @source_path.format
51
+ @handler = handler || @source_path.handler
52
+ else
53
+ @source_path = nil
54
+ @format = format
55
+ @handler = handler
56
+ end
57
+ @mime_type = mime_type || inferred_mime_type || DEFAULT_MIME_TYPE
58
+ end
59
+
60
+ # The node name is derived from the source path
61
+ def node_name
62
+ @source_path&.node_name
63
+ end
64
+
65
+ # Whether this resource can be rendered (has a template handler)
66
+ def renderable?
67
+ !!handler
45
68
  end
46
69
 
47
70
  def request_path
@@ -129,8 +152,16 @@ module Sitepress
129
152
  node.parents.reject(&:root?).reverse.map(&:name)
130
153
  end
131
154
 
155
+ # Renders the resource in a view context using the appropriate template handler.
132
156
  def render_in(view_context)
133
- renderable? ? source.render_in(view_context) : nil
157
+ return nil unless renderable?
158
+ template = ActionView::Template.new(
159
+ body,
160
+ source.path.to_s,
161
+ ActionView::Template.handler_for_extension(handler),
162
+ locals: []
163
+ )
164
+ template.render(view_context, {})
134
165
  end
135
166
 
136
167
  private
@@ -156,8 +187,10 @@ module Sitepress
156
187
  end
157
188
  end
158
189
 
159
- def source_handler
160
- @source.handler if @source.respond_to?(:handler)
190
+ # Returns the mime type inferred from the format extension
191
+ def inferred_mime_type
192
+ format_extension = format&.to_s
193
+ MIME::Types.type_for(format_extension).first if format_extension
161
194
  end
162
195
 
163
196
  # Deals with situations, particularly in the root node and other "index" nodes, for the `request_path`
@@ -1,13 +1,13 @@
1
1
  require "mime/types"
2
2
 
3
3
  module Sitepress
4
- # A source for static files that are served as-is without processing.
5
- # Used as a fallback for files that don't match Image or Page MIME types.
4
+ # Base class for source files. A source represents a file on disk
5
+ # without any web-serving concerns (handlers, formats, etc.).
6
6
  #
7
7
  # Example:
8
- # static = Static.new(path: "fonts/roboto.woff2")
9
- # static.mime_type # => #<MIME::Type font/woff2>
10
- # static.body # => binary content
8
+ # source = Static.new(path: "fonts/roboto.woff2")
9
+ # source.mime_type # => #<MIME::Type font/woff2>
10
+ # source.body # => binary content
11
11
  #
12
12
  class Static
13
13
  attr_reader :path
@@ -16,12 +16,8 @@ module Sitepress
16
16
  @path = Pathname.new(path)
17
17
  end
18
18
 
19
- def node_name
20
- path.basename(".*").to_s.split(".").first
21
- end
22
-
23
- def format
24
- path.extname.delete(".").to_sym
19
+ def exists?
20
+ path.exist?
25
21
  end
26
22
 
27
23
  def mime_type
@@ -36,8 +32,10 @@ module Sitepress
36
32
  @data ||= Data.manage({})
37
33
  end
38
34
 
39
- def exists?
40
- path.exist?
35
+ def fetch_data(key, *args, &block)
36
+ data.fetch(key, *args, &block)
37
+ rescue KeyError
38
+ raise KeyError, "key not found: #{key.inspect} in #{path}"
41
39
  end
42
40
 
43
41
  def inspect
@@ -1,3 +1,3 @@
1
1
  module Sitepress
2
- VERSION = "5.0.0.beta1".freeze
2
+ VERSION = "5.0.0.beta3".freeze
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sitepress-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0.beta1
4
+ version: 5.0.0.beta3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brad Gessler
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-02-20 00:00:00.000000000 Z
10
+ date: 2026-04-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bundler