Class: Ro::Node
Instance Attribute Summary collapse
-
#metadata_file ⇒ Object
readonly
Returns the value of attribute metadata_file.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#root ⇒ Object
readonly
Returns the value of attribute root.
Instance Method Summary collapse
- #<=>(other) ⇒ Object
- #[](*args) ⇒ Object
-
#_ignored_files ⇒ Object
T028: Updated ignore patterns for new structure.
- #_load_asset_attributes ⇒ Object
-
#_load_base_attributes ⇒ Object
T026: Modified to load from external metadata_file (new structure).
- #_load_file_attributes ⇒ Object
- #_load_meta_attributes ⇒ Object
- #_render(file) ⇒ Object
- #_render_context ⇒ Object
- #as_json ⇒ Object
-
#asset_dir ⇒ Object
T027: Updated to return assets/ subdirectory in both old and new structure.
- #asset_for(*args) ⇒ Object
- #asset_for?(*args, &block) ⇒ Boolean
- #asset_paths ⇒ Object
- #asset_urls ⇒ Object
- #assets ⇒ Object
- #attributes ⇒ Object
- #collection ⇒ Object
- #created_at ⇒ Object
- #default_sort_key ⇒ Object
- #fetch(*args) ⇒ Object
- #files ⇒ Object
- #get(*args) ⇒ Object
- #id ⇒ Object
- #identifier ⇒ Object
-
#initialize(collection_or_path, metadata_file = nil) ⇒ Node
constructor
T023: Updated to accept (collection, metadata_file) for new structure.
- #inspect ⇒ Object
- #load_attributes ⇒ Object
- #load_attributes! ⇒ Object
- #method_missing(method, *args, &block) ⇒ Object
- #name ⇒ Object
- #path_for ⇒ Object
- #relative_path ⇒ Object
- #sort_key ⇒ Object
- #src_for(*args) ⇒ Object
- #to_hash ⇒ Object
- #to_json ⇒ Object
- #to_s ⇒ Object
- #to_str ⇒ Object
- #to_yaml ⇒ Object
- #type ⇒ Object
- #update_attributes!(attrs = {}, **context) ⇒ Object
- #updated_at ⇒ Object
- #url_for(relative_path, options = {}) ⇒ Object
- #urls ⇒ Object
Methods included from Klass
Constructor Details
#initialize(collection_or_path, metadata_file = nil) ⇒ Node
T023: Updated to accept (collection, metadata_file) for new structure
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/ro/node.rb', line 8 def initialize(collection_or_path, = nil) if # New structure: collection + metadata_file @collection = collection_or_path = Path.for() # Raise error if metadata file doesn't exist unless .exist? raise Errno::ENOENT, "No such file or directory - #{@metadata_file}" end @root = @collection.root # Derive node ID from metadata filename (without extension) # T025: ID derived from metadata filename node_id = .basename.to_s.sub(/\.(yml|yaml|json|toml)$/, '') # Path is the node directory (sibling to metadata file) @path = @collection.path.join(node_id) else # Old structure compatibility: just a path @path = Path.for(collection_or_path) @root = Root.for(@path.parent.parent) = nil end @attributes = :lazyload end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
312 313 314 315 316 317 318 319 320 |
# File 'lib/ro/node.rb', line 312 def method_missing(method, *args, &block) key = method.to_s if attributes.has_key?(key) attributes[key] else super end end |
Instance Attribute Details
#metadata_file ⇒ Object (readonly)
Returns the value of attribute metadata_file.
5 6 7 |
# File 'lib/ro/node.rb', line 5 def end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
5 6 7 |
# File 'lib/ro/node.rb', line 5 def path @path end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
5 6 7 |
# File 'lib/ro/node.rb', line 5 def root @root end |
Instance Method Details
#<=>(other) ⇒ Object
354 355 356 |
# File 'lib/ro/node.rb', line 354 def <=>(other) sort_key <=> other.sort_key end |
#[](*args) ⇒ Object
238 239 240 |
# File 'lib/ro/node.rb', line 238 def [](*args) attributes.get(*args) end |
#_ignored_files ⇒ Object
T028: Updated ignore patterns for new structure
191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/ro/node.rb', line 191 def _ignored_files # Both old and new structure: ignore attributes files and assets/ subdirectory ignored_files = %w[ attributes.yml attributes.yaml attributes.json ./assets/**/** ].map do |glob| @path.glob(glob).select(&:file?) end.flatten end |
#_load_asset_attributes ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/ro/node.rb', line 103 def _load_asset_attributes {}.tap do |hash| assets.each do |asset| key = asset.name url = asset.url path = asset.path.relative_to(@root) src = asset.src img = asset.img size = asset.size value = { url:, path:, size:, img:, src: } hash[key] = value end @attributes.set(assets: hash) end end |
#_load_base_attributes ⇒ Object
T026: Modified to load from external metadata_file (new structure)
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/ro/node.rb', line 87 def _load_base_attributes if && .exist? # New structure: load from explicit metadata file attrs = _render() update_attributes!(attrs, file: ) else # Old structure: search for attributes.yml in node directory glob = "attributes.{yml,yaml,json}" @path.glob(glob) do |file| attrs = _render(file) update_attributes!(attrs, file:) end end end |
#_load_file_attributes ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/ro/node.rb', line 137 def _load_file_attributes ignored = _ignored_files @path.files.each do |file| next if ignored.include?(file) rel = file.relative_to(@path) key = rel.parts basename = key.pop base = basename.split('.', 2).first key.push(base) value = _render(file) if value.is_a?(HTML) attrs = value.front_matter update_attributes!(attrs, file:) end if @attributes.has?(key) raise Error.new("path=#{ @path.inspect } masks #{ key.inspect } in #{ @attributes.inspect }!") end @attributes.set(key => value) end end |
#_load_meta_attributes ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/ro/node.rb', line 122 def {}.tap do |hash| hash.update( identifier:, type:, id:, urls:, created_at:, updated_at:, ) @attributes.set(_meta: hash) end end |
#_render(file) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/ro/node.rb', line 204 def _render(file) node = self value = Ro.render(file, _render_context) if value.is_a?(HTML) front_matter = value.front_matter html = Ro.(value, node) value = HTML.new(html, front_matter:) end if value.is_a?(Hash) attributes = value value = Ro.(attributes, node) end value end |
#_render_context ⇒ Object
223 224 225 226 227 228 |
# File 'lib/ro/node.rb', line 223 def _render_context to_hash.tap do |context| context[:ro] ||= root context[:collection] ||= collection end end |
#as_json ⇒ Object
338 339 340 |
# File 'lib/ro/node.rb', line 338 def as_json(...) to_hash.as_json(...) end |
#asset_dir ⇒ Object
T027: Updated to return assets/ subdirectory in both old and new structure
247 248 249 250 251 |
# File 'lib/ro/node.rb', line 247 def asset_dir # Both old and new structure use assets/ subdirectory # This prevents files from being rendered as templates path.join('assets') end |
#asset_for(*args) ⇒ Object
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/ro/node.rb', line 265 def asset_for(*args) = Map.(args) path_info = Path.relative(args) path = @path.join('assets', path_info) glob = path_info.gsub(/[_-]/, '[_-]') globs = [ @path.call('assets', "#{glob}"), @path.call('assets', "#{glob}*"), @path.call('assets', "**/#{glob}*") ] candidates = globs.map { |glob| Dir.glob(glob, ::File::FNM_CASEFOLD) }.flatten.compact.uniq.sort case candidates.size when 0 raise ArgumentError, "no asset matching #{globs.inspect}" else path = candidates.last end Asset.for(path, node: self) end |
#asset_for?(*args, &block) ⇒ Boolean
293 294 295 296 297 |
# File 'lib/ro/node.rb', line 293 def asset_for?(*args, &block) asset_for(*args, &block) rescue StandardError nil end |
#asset_paths ⇒ Object
253 254 255 |
# File 'lib/ro/node.rb', line 253 def asset_paths asset_dir.select { |entry| entry.file? }.sort end |
#asset_urls ⇒ Object
261 262 263 |
# File 'lib/ro/node.rb', line 261 def asset_urls assets.map(&:url) end |
#assets ⇒ Object
257 258 259 |
# File 'lib/ro/node.rb', line 257 def assets asset_paths.map { |path| Asset.for(path, node: self) } end |
#attributes ⇒ Object
66 67 68 69 |
# File 'lib/ro/node.rb', line 66 def attributes load_attributes @attributes end |
#collection ⇒ Object
62 63 64 |
# File 'lib/ro/node.rb', line 62 def collection @collection || @root.collection_for(type) end |
#created_at ⇒ Object
370 371 372 |
# File 'lib/ro/node.rb', line 370 def created_at files.map{|file| File.stat(file).ctime}.min end |
#default_sort_key ⇒ Object
362 363 364 365 366 367 368 |
# File 'lib/ro/node.rb', line 362 def default_sort_key position = (attributes[:position] ? Float(attributes[:position]) : 0.0) published_at = (attributes[:published_at] ? Time.parse(attributes[:published_at].to_s) : Time.at(0)).utc.iso8601 created_at = (attributes[:created_at] ? Time.parse(attributes[:created_at].to_s) : Time.at(0)).utc.iso8601 [position, published_at, created_at, name] end |
#fetch(*args) ⇒ Object
230 231 232 |
# File 'lib/ro/node.rb', line 230 def fetch(*args) attributes.fetch(*args) end |
#files ⇒ Object
346 347 348 |
# File 'lib/ro/node.rb', line 346 def files path.glob('**/**').select { |entry| entry.file? }.sort end |
#get(*args) ⇒ Object
234 235 236 |
# File 'lib/ro/node.rb', line 234 def get(*args) attributes.get(*args) end |
#id ⇒ Object
46 47 48 |
# File 'lib/ro/node.rb', line 46 def id name end |
#identifier ⇒ Object
54 55 56 |
# File 'lib/ro/node.rb', line 54 def identifier File.join(type, id) end |
#inspect ⇒ Object
58 59 60 |
# File 'lib/ro/node.rb', line 58 def inspect identifier end |
#load_attributes ⇒ Object
71 72 73 |
# File 'lib/ro/node.rb', line 71 def load_attributes load_attributes! if @attributes == :lazyload end |
#load_attributes! ⇒ Object
75 76 77 78 79 80 81 82 83 84 |
# File 'lib/ro/node.rb', line 75 def load_attributes! @attributes = Map.new _load_base_attributes _load_file_attributes _load_asset_attributes @attributes end |
#name ⇒ Object
37 38 39 40 41 42 43 44 |
# File 'lib/ro/node.rb', line 37 def name if # T025: For new structure, name comes from metadata filename .basename.to_s.sub(/\.(yml|yaml|json|toml)$/, '') else @path.name end end |
#path_for ⇒ Object
303 304 305 |
# File 'lib/ro/node.rb', line 303 def path_for(...) @path.join(...) end |
#relative_path ⇒ Object
242 243 244 |
# File 'lib/ro/node.rb', line 242 def relative_path path.relative_to(root) end |
#sort_key ⇒ Object
358 359 360 |
# File 'lib/ro/node.rb', line 358 def sort_key default_sort_key end |
#src_for(*args) ⇒ Object
307 308 309 310 |
# File 'lib/ro/node.rb', line 307 def src_for(*args) key = Path.relative(:assets, :src, args).split('/') get(key) end |
#to_hash ⇒ Object
322 323 324 |
# File 'lib/ro/node.rb', line 322 def to_hash attributes.to_hash end |
#to_json ⇒ Object
334 335 336 |
# File 'lib/ro/node.rb', line 334 def to_json(...) JSON.pretty_generate(to_hash, ...) end |
#to_s ⇒ Object
326 327 328 |
# File 'lib/ro/node.rb', line 326 def to_s(...) to_json(...) end |
#to_str ⇒ Object
330 331 332 |
# File 'lib/ro/node.rb', line 330 def to_str(...) to_json(...) end |
#to_yaml ⇒ Object
342 343 344 |
# File 'lib/ro/node.rb', line 342 def to_yaml(...) to_hash.to_yaml(...) end |
#type ⇒ Object
50 51 52 |
# File 'lib/ro/node.rb', line 50 def type @path.parent.name end |
#update_attributes!(attrs = {}, **context) ⇒ Object
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/ro/node.rb', line 165 def update_attributes!(attrs = {}, **context) attrs = Map.for(attrs) blacklist = %w[ assets _meta ] blacklist.each do |key| if attrs.has_key?(key) Ro.error!("#{ key } is blacklisted!", **context) end end keys = @attributes.depth_first_keys attrs.depth_first_keys.each do |key| if keys.include?(key) Ro.error!("#{ attrs.inspect } clobbers #{ @attributes.inspect }!", **context) end end @attributes.update(attrs) end |
#updated_at ⇒ Object
374 375 376 |
# File 'lib/ro/node.rb', line 374 def updated_at files.map{|file| File.stat(file).mtime}.max end |
#url_for(relative_path, options = {}) ⇒ Object
299 300 301 |
# File 'lib/ro/node.rb', line 299 def url_for(relative_path, = {}) Ro.url_for(self.relative_path, relative_path, ) end |
#urls ⇒ Object
350 351 352 |
# File 'lib/ro/node.rb', line 350 def urls files.map { |file| url_for(file.relative_to(@path)) }.sort end |