plum-cms 0.2.1 → 0.2.2
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 +4 -4
- data/CHANGELOG.md +178 -0
- data/README.md +64 -0
- data/app/assets/builds/tailwind.css +1 -1
- data/app/controllers/plum/cp/entries_controller.rb +88 -5
- data/app/controllers/plum/cp/static_cache_controller.rb +12 -0
- data/app/controllers/plum/form_submissions_controller.rb +13 -0
- data/app/controllers/plum/pages_controller.rb +8 -0
- data/app/controllers/plum/theme_assets_controller.rb +2 -0
- data/app/javascript/controllers/plum/write_controller.js +174 -0
- data/app/models/plum/asset.rb +1 -0
- data/app/models/plum/content_type.rb +1 -0
- data/app/models/plum/entry.rb +57 -0
- data/app/models/plum/entry_term.rb +1 -0
- data/app/models/plum/form_definition.rb +1 -0
- data/app/models/plum/global.rb +1 -0
- data/app/models/plum/nav_item.rb +1 -0
- data/app/models/plum/nav_menu.rb +1 -0
- data/app/models/plum/site.rb +2 -0
- data/app/models/plum/site_setting.rb +1 -0
- data/app/models/plum/static_cache_invalidation.rb +30 -0
- data/app/models/plum/taxonomy.rb +1 -0
- data/app/models/plum/term.rb +1 -0
- data/app/services/plum/config_sync.rb +252 -0
- data/app/services/plum/draft_diff.rb +177 -0
- data/app/services/plum/form_renderer.rb +12 -1
- data/app/services/plum/liquid_context.rb +0 -2
- data/app/views/layouts/plum/write.html.erb +140 -0
- data/app/views/plum/cp/dashboard/show.html.erb +10 -3
- data/app/views/plum/cp/entries/_form.html.erb +2 -2
- data/app/views/plum/cp/entries/diff.html.erb +40 -0
- data/app/views/plum/cp/entries/edit.html.erb +24 -0
- data/app/views/plum/cp/entries/index.html.erb +3 -0
- data/app/views/plum/cp/entries/write.html.erb +60 -0
- data/config/plum_routes.rb +5 -0
- data/db/engine_migrate/20260811090000_add_draft_data_to_plum_entries.rb +5 -0
- data/docs/config-as-code.md +103 -0
- data/docs/plum-cli.md +356 -0
- data/docs/static-caching.md +163 -0
- data/lib/generators/plum/install/templates/plum_initializer.rb +8 -0
- data/lib/plum/configuration.rb +12 -1
- data/lib/plum/engine.rb +7 -0
- data/lib/plum/static_cache/middleware.rb +61 -0
- data/lib/plum/static_cache.rb +103 -0
- data/lib/plum/version.rb +1 -1
- data/lib/tasks/plum_config.rake +48 -0
- data/lib/tasks/plum_portability.rake +54 -29
- data/lib/tasks/plum_styles.rake +14 -9
- metadata +21 -3
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require "rack/mime"
|
|
2
|
+
|
|
3
|
+
module Plum
|
|
4
|
+
# Full-page static cache. Rendered public pages are written to disk keyed by
|
|
5
|
+
# host + path; subsequent requests are served from the file without touching
|
|
6
|
+
# the database or Liquid. A web server can serve the same files directly
|
|
7
|
+
# (nginx try_files) for the "full measure" where Rails never sees the hit.
|
|
8
|
+
#
|
|
9
|
+
# Cache entries are invalidated by deletion (see StaticCacheInvalidation) —
|
|
10
|
+
# the next request re-renders and re-stores the page. Over-flushing is cheap,
|
|
11
|
+
# so anything ambiguous flushes the whole site.
|
|
12
|
+
module StaticCache
|
|
13
|
+
MARKER_HEADER = "X-Plum-Static-Cache".freeze
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# Explicit opt-in only — see docs/static-caching.md. There is
|
|
17
|
+
# deliberately no "auto-on in production" mode: this cache is only
|
|
18
|
+
# correct on a single-server deployment, and defaulting it on would
|
|
19
|
+
# silently corrupt pages the moment an app scales to 2+ nodes.
|
|
20
|
+
def enabled?
|
|
21
|
+
!!Plum.configuration.static_cache_enabled
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cache_root
|
|
25
|
+
Pathname(Plum.configuration.static_cache_path || Rails.root.join("storage", "plum_static_cache"))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Pages are stored as {host}/{path}/index.html so a web server can map
|
|
29
|
+
# URLs to files directly. Paths with a file extension (theme assets) are
|
|
30
|
+
# stored verbatim.
|
|
31
|
+
def file_path(host, path)
|
|
32
|
+
key = normalized_path(path)
|
|
33
|
+
return nil unless key
|
|
34
|
+
|
|
35
|
+
base = cache_root.join(sanitized_host(host))
|
|
36
|
+
full = File.extname(key).present? ? base.join(key) : base.join(key, "index.html")
|
|
37
|
+
full = Pathname(File.expand_path(full))
|
|
38
|
+
return nil unless full.to_s.start_with?(cache_root.to_s + File::SEPARATOR)
|
|
39
|
+
|
|
40
|
+
full
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def read(host, path)
|
|
44
|
+
file = file_path(host, path)
|
|
45
|
+
file if file&.file?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def store(host, path, body)
|
|
49
|
+
file = file_path(host, path)
|
|
50
|
+
return unless file
|
|
51
|
+
|
|
52
|
+
file.dirname.mkpath
|
|
53
|
+
tmp = file.sub_ext(".tmp-#{Process.pid}-#{Thread.current.object_id}")
|
|
54
|
+
tmp.binwrite(body)
|
|
55
|
+
File.rename(tmp, file)
|
|
56
|
+
file
|
|
57
|
+
rescue SystemCallError => e
|
|
58
|
+
Rails.logger.error("[Plum] static cache write failed for #{path}: #{e.message}")
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def flush_site!(site)
|
|
63
|
+
return flush_all! if site.nil? || site.domain.blank?
|
|
64
|
+
|
|
65
|
+
[ site.domain, "www.#{site.domain}" ].each do |host|
|
|
66
|
+
dir = cache_root.join(sanitized_host(host))
|
|
67
|
+
FileUtils.rm_rf(dir) if dir.to_s.start_with?(cache_root.to_s) && dir.exist?
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def flush_all!
|
|
72
|
+
return unless cache_root.exist?
|
|
73
|
+
|
|
74
|
+
cache_root.children.each { |child| FileUtils.rm_rf(child) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def content_type_for(file)
|
|
78
|
+
Rack::Mime.mime_type(File.extname(file.to_s), "text/html")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def sanitized_host(host)
|
|
84
|
+
cleaned = host.to_s.downcase.gsub(/[^a-z0-9.\-]/, "")
|
|
85
|
+
cleaned.presence || "_default"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Rejects anything that could escape the cache directory and normalizes
|
|
89
|
+
# "/" and "/about/" style paths to a shared key.
|
|
90
|
+
def normalized_path(path)
|
|
91
|
+
decoded = begin
|
|
92
|
+
URI.decode_www_form_component(path.to_s)
|
|
93
|
+
rescue ArgumentError
|
|
94
|
+
return nil
|
|
95
|
+
end
|
|
96
|
+
return nil if decoded.include?("..") || decoded.include?("\0")
|
|
97
|
+
|
|
98
|
+
trimmed = decoded.delete_prefix("/").chomp("/")
|
|
99
|
+
trimmed.presence || "_root"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/plum/version.rb
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# This repo is both the engine and an app, so Rails loads lib/tasks twice
|
|
2
|
+
# (engine railtie + application). Rake appends actions on re-definition, which
|
|
3
|
+
# would run every task body twice — guard against the second load.
|
|
4
|
+
unless Rake::Task.task_defined?("plum:config:export")
|
|
5
|
+
namespace :plum do
|
|
6
|
+
namespace :config do
|
|
7
|
+
def plum_config_site
|
|
8
|
+
site = ENV["SITE_ID"].present? ? Plum::Site.find(ENV["SITE_ID"]) : Plum::Site.first
|
|
9
|
+
abort "No Plum site exists" unless site
|
|
10
|
+
site
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def plum_config_dir
|
|
14
|
+
ENV["DIR"].presence || Plum.configuration.config_path || Rails.root.join("plum")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "Write the content model (content types, fieldsets) to YAML files (SITE_ID optional; DIR defaults to plum/)"
|
|
18
|
+
task export: :environment do
|
|
19
|
+
files = Plum::ConfigSync.export(site: plum_config_site, dir: plum_config_dir)
|
|
20
|
+
puts "Exported #{files.length} config files to #{plum_config_dir}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "Apply YAML config files to the database (SITE_ID optional; PRUNE=1 deletes types absent from files; FORCE=1 allows deleting types with entries)"
|
|
24
|
+
task sync: :environment do
|
|
25
|
+
result = Plum::ConfigSync.apply(
|
|
26
|
+
site: plum_config_site,
|
|
27
|
+
dir: plum_config_dir,
|
|
28
|
+
prune: ENV["PRUNE"].present?,
|
|
29
|
+
force: ENV["FORCE"].present?
|
|
30
|
+
)
|
|
31
|
+
puts "Config sync: #{result.summary}"
|
|
32
|
+
rescue Plum::ConfigSync::UnsafePruneError => e
|
|
33
|
+
abort e.message
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
desc "Fail if the database content model has drifted from the YAML files (for CI)"
|
|
37
|
+
task check: :environment do
|
|
38
|
+
drift = Plum::ConfigSync.check(site: plum_config_site, dir: plum_config_dir)
|
|
39
|
+
if drift.any?
|
|
40
|
+
drift.each { |line| puts "DRIFT #{line}" }
|
|
41
|
+
abort "Content model has drifted from config files (#{drift.length} differences)"
|
|
42
|
+
else
|
|
43
|
+
puts "Content model matches config files"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -1,37 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
# This repo is both the engine and an app, so Rails loads lib/tasks twice
|
|
2
|
+
# (engine railtie + application). Rake appends actions on re-definition, which
|
|
3
|
+
# would run every task body twice — guard against the second load.
|
|
4
|
+
unless Rake::Task.task_defined?("plum:site:export")
|
|
5
|
+
namespace :plum do
|
|
6
|
+
namespace :site do
|
|
7
|
+
desc "Export a complete Plum site archive (SITE_ID or first site; ARCHIVE required)"
|
|
8
|
+
task export: :environment do
|
|
9
|
+
path = ENV.fetch("ARCHIVE") { abort "ARCHIVE is required" }
|
|
10
|
+
site = ENV["SITE_ID"].present? ? Plum::Site.find(ENV["SITE_ID"]) : Plum::Site.first
|
|
11
|
+
abort "No Plum site exists" unless site
|
|
12
|
+
|
|
13
|
+
archive = Plum::SiteArchive.dump(site: site, path: path)
|
|
14
|
+
puts "Exported #{site.name} to #{archive}"
|
|
15
|
+
end
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
desc "Import a Plum site archive as a new site (ARCHIVE required; NAME and DOMAIN optional)"
|
|
18
|
+
task import: :environment do
|
|
19
|
+
path = ENV.fetch("ARCHIVE") { abort "ARCHIVE is required" }
|
|
20
|
+
site = Plum::SiteArchive.load(path: path, name: ENV["NAME"], domain: ENV["DOMAIN"])
|
|
21
|
+
puts "Imported #{site.name} as site #{site.id}"
|
|
22
|
+
end
|
|
18
23
|
end
|
|
19
|
-
end
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
namespace :site do
|
|
26
|
+
desc "Replace the local site with an archive — the 'pull' refresh (ARCHIVE required; SITE_ID optional; FORCE=1 required in production)"
|
|
27
|
+
task replace: :environment do
|
|
28
|
+
path = ENV.fetch("ARCHIVE") { abort "ARCHIVE is required" }
|
|
29
|
+
if Rails.env.production? && ENV["FORCE"].blank?
|
|
30
|
+
abort "Refusing to replace a site in production (set FORCE=1 to override)"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
old_site = ENV["SITE_ID"].present? ? Plum::Site.find(ENV["SITE_ID"]) : Plum::Site.first
|
|
34
|
+
old_name = old_site&.name
|
|
35
|
+
|
|
36
|
+
site = ActiveRecord::Base.transaction do
|
|
37
|
+
old_site&.destroy!
|
|
38
|
+
Plum::SiteArchive.load(path: path, name: ENV["NAME"], domain: ENV["DOMAIN"])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
puts old_name ? "Replaced #{old_name} with #{site.name} (site #{site.id})" : "Imported #{site.name} as site #{site.id}"
|
|
42
|
+
end
|
|
30
43
|
end
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
namespace :backup do
|
|
46
|
+
desc "Create a timestamped Plum site backup (SITE_ID optional; DIRECTORY defaults to backups/plum)"
|
|
47
|
+
task create: :environment do
|
|
48
|
+
site = ENV["SITE_ID"].present? ? Plum::Site.find(ENV["SITE_ID"]) : Plum::Site.first
|
|
49
|
+
abort "No Plum site exists" unless site
|
|
50
|
+
directory = Rails.root.join(ENV.fetch("DIRECTORY", "backups/plum"))
|
|
51
|
+
filename = "plum-site-#{site.id}-#{Time.current.utc.strftime('%Y%m%d%H%M%S')}.plum.zip"
|
|
52
|
+
archive = Plum::SiteArchive.dump(site: site, path: directory.join(filename))
|
|
53
|
+
puts "Backed up #{site.name} to #{archive}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
desc "Restore a Plum backup as a new site (ARCHIVE required; NAME and DOMAIN optional)"
|
|
57
|
+
task restore: :environment do
|
|
58
|
+
Rake::Task["plum:site:import"].invoke
|
|
59
|
+
end
|
|
35
60
|
end
|
|
36
61
|
end
|
|
37
62
|
end
|
data/lib/tasks/plum_styles.rake
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
# This repo is both the engine and an app, so Rails loads lib/tasks twice
|
|
2
|
+
# (engine railtie + application). Rake appends actions on re-definition, which
|
|
3
|
+
# would run every task body twice — guard against the second load.
|
|
4
|
+
unless Rake::Task.task_defined?("plum:build_styles")
|
|
5
|
+
require "fileutils"
|
|
2
6
|
|
|
3
|
-
namespace :plum do
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
namespace :plum do
|
|
8
|
+
desc "Build Tailwind and copy the result into Plum's packaged stylesheet"
|
|
9
|
+
task build_styles: :environment do
|
|
10
|
+
system(Rails.root.join("bin/rails").to_s, "tailwindcss:build", exception: true)
|
|
11
|
+
source = Rails.root.join("app/assets/builds/tailwind.css")
|
|
12
|
+
destination = Rails.root.join("app/assets/stylesheets/plum/control_panel.css")
|
|
13
|
+
FileUtils.cp(source, destination)
|
|
14
|
+
puts "Packaged #{destination.relative_path_from(Rails.root)}"
|
|
15
|
+
end
|
|
11
16
|
end
|
|
12
17
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: plum-cms
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ben Simmons
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-08-13 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: bcrypt
|
|
@@ -222,6 +223,7 @@ files:
|
|
|
222
223
|
- app/controllers/plum/cp/nav_items_controller.rb
|
|
223
224
|
- app/controllers/plum/cp/nav_menus_controller.rb
|
|
224
225
|
- app/controllers/plum/cp/site_settings_controller.rb
|
|
226
|
+
- app/controllers/plum/cp/static_cache_controller.rb
|
|
225
227
|
- app/controllers/plum/cp/taxonomies_controller.rb
|
|
226
228
|
- app/controllers/plum/cp/terms_controller.rb
|
|
227
229
|
- app/controllers/plum/cp/theme_previews_controller.rb
|
|
@@ -246,6 +248,7 @@ files:
|
|
|
246
248
|
- app/javascript/controllers/plum/image_picker_controller.js
|
|
247
249
|
- app/javascript/controllers/plum/structured_field_controller.js
|
|
248
250
|
- app/javascript/controllers/plum/theme_settings_controller.js
|
|
251
|
+
- app/javascript/controllers/plum/write_controller.js
|
|
249
252
|
- app/models/plum/application_record.rb
|
|
250
253
|
- app/models/plum/asset.rb
|
|
251
254
|
- app/models/plum/content_type.rb
|
|
@@ -261,6 +264,7 @@ files:
|
|
|
261
264
|
- app/models/plum/site.rb
|
|
262
265
|
- app/models/plum/site_scoped.rb
|
|
263
266
|
- app/models/plum/site_setting.rb
|
|
267
|
+
- app/models/plum/static_cache_invalidation.rb
|
|
264
268
|
- app/models/plum/taxonomy.rb
|
|
265
269
|
- app/models/plum/term.rb
|
|
266
270
|
- app/models/plum/user.rb
|
|
@@ -268,6 +272,8 @@ files:
|
|
|
268
272
|
- app/services/plum/block_editor_config.rb
|
|
269
273
|
- app/services/plum/block_library.rb
|
|
270
274
|
- app/services/plum/builder_renderer.rb
|
|
275
|
+
- app/services/plum/config_sync.rb
|
|
276
|
+
- app/services/plum/draft_diff.rb
|
|
271
277
|
- app/services/plum/entry_serializer.rb
|
|
272
278
|
- app/services/plum/field_expander.rb
|
|
273
279
|
- app/services/plum/field_options.rb
|
|
@@ -320,6 +326,7 @@ files:
|
|
|
320
326
|
- app/themes/default/theme.yml
|
|
321
327
|
- app/views/layouts/plum/cp.html.erb
|
|
322
328
|
- app/views/layouts/plum/session.html.erb
|
|
329
|
+
- app/views/layouts/plum/write.html.erb
|
|
323
330
|
- app/views/plum/cp/assets/_form.html.erb
|
|
324
331
|
- app/views/plum/cp/assets/edit.html.erb
|
|
325
332
|
- app/views/plum/cp/assets/index.html.erb
|
|
@@ -332,9 +339,11 @@ files:
|
|
|
332
339
|
- app/views/plum/cp/custom_fields/_input.html.erb
|
|
333
340
|
- app/views/plum/cp/dashboard/show.html.erb
|
|
334
341
|
- app/views/plum/cp/entries/_form.html.erb
|
|
342
|
+
- app/views/plum/cp/entries/diff.html.erb
|
|
335
343
|
- app/views/plum/cp/entries/edit.html.erb
|
|
336
344
|
- app/views/plum/cp/entries/index.html.erb
|
|
337
345
|
- app/views/plum/cp/entries/new.html.erb
|
|
346
|
+
- app/views/plum/cp/entries/write.html.erb
|
|
338
347
|
- app/views/plum/cp/entry_revisions/index.html.erb
|
|
339
348
|
- app/views/plum/cp/fieldsets/index.html.erb
|
|
340
349
|
- app/views/plum/cp/form_definitions/_form.html.erb
|
|
@@ -382,14 +391,18 @@ files:
|
|
|
382
391
|
- db/engine_migrate/20260807140000_create_plum_fieldsets.rb
|
|
383
392
|
- db/engine_migrate/20260807150000_add_focal_point_to_plum_assets.rb
|
|
384
393
|
- db/engine_migrate/20260807160000_add_localization_to_plum_entries.rb
|
|
394
|
+
- db/engine_migrate/20260811090000_add_draft_data_to_plum_entries.rb
|
|
385
395
|
- docs/blueprint-fields.md
|
|
396
|
+
- docs/config-as-code.md
|
|
386
397
|
- docs/extensions.md
|
|
398
|
+
- docs/plum-cli.md
|
|
387
399
|
- docs/portability.md
|
|
388
400
|
- docs/product-principles.md
|
|
389
401
|
- docs/roadmap.md
|
|
390
402
|
- docs/site/homepage.md
|
|
391
403
|
- docs/site/information-architecture.md
|
|
392
404
|
- docs/statamic-parity.md
|
|
405
|
+
- docs/static-caching.md
|
|
393
406
|
- docs/vision.md
|
|
394
407
|
- lib/generators/plum/install/install_generator.rb
|
|
395
408
|
- lib/generators/plum/install/templates/plum_initializer.rb
|
|
@@ -400,7 +413,10 @@ files:
|
|
|
400
413
|
- lib/plum/content_source_registry.rb
|
|
401
414
|
- lib/plum/engine.rb
|
|
402
415
|
- lib/plum/liquid_tags/form_tag.rb
|
|
416
|
+
- lib/plum/static_cache.rb
|
|
417
|
+
- lib/plum/static_cache/middleware.rb
|
|
403
418
|
- lib/plum/version.rb
|
|
419
|
+
- lib/tasks/plum_config.rake
|
|
404
420
|
- lib/tasks/plum_portability.rake
|
|
405
421
|
- lib/tasks/plum_styles.rake
|
|
406
422
|
- vendor/javascript/lexxy.js
|
|
@@ -412,6 +428,7 @@ metadata:
|
|
|
412
428
|
rubygems_mfa_required: 'true'
|
|
413
429
|
source_code_uri: https://github.com/tableneeds/plum
|
|
414
430
|
bug_tracker_uri: https://github.com/tableneeds/plum/issues
|
|
431
|
+
post_install_message:
|
|
415
432
|
rdoc_options: []
|
|
416
433
|
require_paths:
|
|
417
434
|
- lib
|
|
@@ -427,7 +444,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
427
444
|
- !ruby/object:Gem::Version
|
|
428
445
|
version: '0'
|
|
429
446
|
requirements: []
|
|
430
|
-
rubygems_version: 3.
|
|
447
|
+
rubygems_version: 3.5.9
|
|
448
|
+
signing_key:
|
|
431
449
|
specification_version: 4
|
|
432
450
|
summary: A Rails-native CMS engine.
|
|
433
451
|
test_files: []
|