ro 4.4.0 → 5.1.0
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/Gemfile.lock +42 -16
- data/MIGRATION.md +320 -0
- data/README.md +31 -19
- data/a.yml +60 -0
- data/bin/ro +10 -0
- data/lib/ro/_lib.rb +1 -1
- data/lib/ro/asset.rb +48 -6
- data/lib/ro/collection.rb +51 -13
- data/lib/ro/migrator.rb +285 -0
- data/lib/ro/node.rb +53 -13
- data/lib/ro/root.rb +75 -1
- data/lib/ro/script/migrate.rb +204 -0
- data/lib/ro/script/server.rb +1 -1
- data/lib/ro.rb +1 -0
- data/public/api/ro/index-1.json +82 -148
- data/public/api/ro/index.json +82 -148
- data/public/api/ro/nerd/fastest-possible-embeddings/index.json +7 -8
- data/public/api/ro/nerd/ima/index.json +3 -4
- data/public/api/ro/nerd/index/index.json +5 -6
- data/public/api/ro/nerd/index-1.json +15 -18
- data/public/api/ro/nerd/index.json +15 -18
- data/public/api/ro/pages/contact/index.json +4 -5
- data/public/api/ro/pages/cv/index.json +3 -4
- data/public/api/ro/pages/disco/index.json +9 -10
- data/public/api/ro/pages/index/index.json +2 -3
- data/public/api/ro/pages/index-1.json +25 -82
- data/public/api/ro/pages/index.json +25 -82
- data/public/api/ro/pages/jess/index.json +4 -5
- data/public/api/ro/pages/now/index.json +3 -4
- data/public/api/ro/posts/almost-died-in-an-ice-cave/index.json +21 -22
- data/public/api/ro/posts/facebook-and-global-extremism/index.json +8 -9
- data/public/api/ro/posts/index-1.json +42 -48
- data/public/api/ro/posts/index.json +42 -48
- data/public/api/ro/posts/lemmings-considered-harmful/index.json +3 -4
- data/public/api/ro/posts/lost-in-the-desert/index.json +3 -4
- data/public/api/ro/posts/mission/index.json +3 -4
- data/public/api/ro/posts/return-your-laptop/index.json +4 -5
- data/ro.gemspec +247 -18
- data/specs/001-simplify-asset-structure/IMPLEMENTATION_SUMMARY.md +212 -0
- data/specs/001-simplify-asset-structure/checklists/requirements.md +36 -0
- data/specs/001-simplify-asset-structure/contracts/collection_api.md +407 -0
- data/specs/001-simplify-asset-structure/contracts/migrator_api.md +461 -0
- data/specs/001-simplify-asset-structure/contracts/node_api.md +294 -0
- data/specs/001-simplify-asset-structure/data-model.md +381 -0
- data/specs/001-simplify-asset-structure/plan.md +90 -0
- data/specs/001-simplify-asset-structure/quickstart.md +575 -0
- data/specs/001-simplify-asset-structure/research.md +333 -0
- data/specs/001-simplify-asset-structure/spec.md +127 -0
- data/specs/001-simplify-asset-structure/tasks.md +349 -0
- data/test/fixtures/new_structure/mixed/test-json.json +5 -0
- data/test/fixtures/new_structure/mixed/test-yaml.yml +3 -0
- data/test/fixtures/new_structure/posts/metadata-only.yml +7 -0
- data/test/fixtures/new_structure/posts/nested-test/assets/subdirectory/image.png +2 -0
- data/test/fixtures/new_structure/posts/nested-test.yml +7 -0
- data/test/fixtures/new_structure/posts/sample-post/assets/body.md +5 -0
- data/test/fixtures/new_structure/posts/sample-post/assets/image.jpg +2 -0
- data/test/fixtures/new_structure/posts/sample-post.yml +7 -0
- data/test/fixtures/old_structure/posts/assets-only/assets/test.txt +1 -0
- data/test/fixtures/old_structure/posts/sample-post/assets/body.md +5 -0
- data/test/fixtures/old_structure/posts/sample-post/assets/image.jpg +2 -0
- data/test/fixtures/old_structure/posts/sample-post/attributes.yml +2 -0
- data/test/integration/ro_integration_test.rb +165 -0
- data/test/test_helper.rb +149 -0
- data/test/tmp/migration_test_1760746513.backup.20251018001513/migration_test_1760746513/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760746513.backup.20251018001513/migration_test_1760746513/posts/sample-post/attributes.yml +7 -0
- data/test/tmp/migration_test_1760746513.backup.20251018001513/migration_test_1760746513/posts/sample-post/body.md +5 -0
- data/test/tmp/migration_test_1760746513.backup.20251018001513/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760746513.backup.20251018001513/posts/sample-post/attributes.yml +7 -0
- data/test/tmp/migration_test_1760746513.backup.20251018001513/posts/sample-post/body.md +5 -0
- data/test/tmp/migration_test_1760746556.backup.20251018001556/migration_test_1760746556/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760746556.backup.20251018001556/migration_test_1760746556/posts/sample-post/attributes.yml +7 -0
- data/test/tmp/migration_test_1760746556.backup.20251018001556/migration_test_1760746556/posts/sample-post/body.md +5 -0
- data/test/tmp/migration_test_1760746556.backup.20251018001556/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760746556.backup.20251018001556/posts/sample-post/attributes.yml +7 -0
- data/test/tmp/migration_test_1760746556.backup.20251018001556/posts/sample-post/body.md +5 -0
- data/test/tmp/migration_test_1760755248.backup.20251018024048/migration_test_1760755248/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760755248.backup.20251018024048/migration_test_1760755248/posts/sample-post/attributes.yml +7 -0
- data/test/tmp/migration_test_1760755248.backup.20251018024048/migration_test_1760755248/posts/sample-post/body.md +5 -0
- data/test/tmp/migration_test_1760755248.backup.20251018024048/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760755248.backup.20251018024048/posts/sample-post/attributes.yml +7 -0
- data/test/tmp/migration_test_1760755248.backup.20251018024048/posts/sample-post/body.md +5 -0
- data/test/tmp/migration_test_1760758803.backup.20251018034003/migration_test_1760758803/posts/sample-post/body.md +5 -0
- data/test/tmp/migration_test_1760758803.backup.20251018034003/migration_test_1760758803/posts/sample-post/image.jpg +2 -0
- data/test/tmp/migration_test_1760758803.backup.20251018034003/migration_test_1760758803/posts/sample-post.yml +7 -0
- data/test/tmp/migration_test_1760758803.backup.20251018034003/posts/sample-post/body.md +5 -0
- data/test/tmp/migration_test_1760758803.backup.20251018034003/posts/sample-post/image.jpg +2 -0
- data/test/tmp/migration_test_1760758803.backup.20251018034003/posts/sample-post.yml +7 -0
- data/test/tmp/migration_test_1760758869.backup.20251018034109/migration_test_1760758869/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760758869.backup.20251018034109/migration_test_1760758869/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760758869.backup.20251018034109/migration_test_1760758869/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760758869.backup.20251018034109/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760758869.backup.20251018034109/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760758869.backup.20251018034109/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760758920.backup.20251018034200/migration_test_1760758920/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760758920.backup.20251018034200/migration_test_1760758920/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760758920.backup.20251018034200/migration_test_1760758920/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760758920.backup.20251018034200/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760758920.backup.20251018034200/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760758920.backup.20251018034200/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760824728.backup.20251018215848/migration_test_1760824728/posts/assets-only/assets/test.txt +1 -0
- data/test/tmp/migration_test_1760824728.backup.20251018215848/migration_test_1760824728/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760824728.backup.20251018215848/migration_test_1760824728/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760824728.backup.20251018215848/migration_test_1760824728/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760824728.backup.20251018215848/posts/assets-only/assets/test.txt +1 -0
- data/test/tmp/migration_test_1760824728.backup.20251018215848/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760824728.backup.20251018215848/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760824728.backup.20251018215848/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760844153.backup.20251019032233/migration_test_1760844153/posts/assets-only/assets/test.txt +1 -0
- data/test/tmp/migration_test_1760844153.backup.20251019032233/migration_test_1760844153/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760844153.backup.20251019032233/migration_test_1760844153/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760844153.backup.20251019032233/migration_test_1760844153/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760844153.backup.20251019032233/posts/assets-only/assets/test.txt +1 -0
- data/test/tmp/migration_test_1760844153.backup.20251019032233/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760844153.backup.20251019032233/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760844153.backup.20251019032233/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760940939.backup.20251020061539/migration_test_1760940939/posts/assets-only/assets/test.txt +1 -0
- data/test/tmp/migration_test_1760940939.backup.20251020061539/migration_test_1760940939/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760940939.backup.20251020061539/migration_test_1760940939/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760940939.backup.20251020061539/migration_test_1760940939/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760940939.backup.20251020061539/posts/assets-only/assets/test.txt +1 -0
- data/test/tmp/migration_test_1760940939.backup.20251020061539/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760940939.backup.20251020061539/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760940939.backup.20251020061539/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760941048.backup.20251020061728/migration_test_1760941048/posts/assets-only/assets/test.txt +1 -0
- data/test/tmp/migration_test_1760941048.backup.20251020061728/migration_test_1760941048/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760941048.backup.20251020061728/migration_test_1760941048/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760941048.backup.20251020061728/migration_test_1760941048/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/migration_test_1760941048.backup.20251020061728/posts/assets-only/assets/test.txt +1 -0
- data/test/tmp/migration_test_1760941048.backup.20251020061728/posts/sample-post/assets/body.md +5 -0
- data/test/tmp/migration_test_1760941048.backup.20251020061728/posts/sample-post/assets/image.jpg +2 -0
- data/test/tmp/migration_test_1760941048.backup.20251020061728/posts/sample-post/attributes.yml +2 -0
- data/test/tmp/new_structure_test_1760746452/mixed/test-json.json +5 -0
- data/test/tmp/new_structure_test_1760746452/mixed/test-yaml.yml +3 -0
- data/test/tmp/new_structure_test_1760746452/posts/metadata-only.yml +7 -0
- data/test/tmp/new_structure_test_1760746452/posts/nested-test/subdirectory/image.png +2 -0
- data/test/tmp/new_structure_test_1760746452/posts/nested-test.yml +7 -0
- data/test/tmp/new_structure_test_1760746452/posts/sample-post/body.md +5 -0
- data/test/tmp/new_structure_test_1760746452/posts/sample-post/image.jpg +2 -0
- data/test/tmp/new_structure_test_1760746452/posts/sample-post.yml +7 -0
- data/test/unit/asset_test.rb +90 -0
- data/test/unit/collection_test.rb +127 -0
- data/test/unit/migrator_test.rb +209 -0
- data/test/unit/node_test.rb +138 -0
- metadata +127 -19
- data/public/api/ro/pages/about/index.json +0 -60
- /data/public/ro/nerd/{fastest-possible-embeddings/attributes.yml → fastest-possible-embeddings.yml} +0 -0
- /data/public/ro/nerd/{ima/attributes.yml → ima.yml} +0 -0
- /data/public/ro/nerd/{index/attributes.yml → index.yml} +0 -0
- /data/public/ro/pages/{contact/attributes.yml → contact.yml} +0 -0
- /data/public/ro/pages/{cv/attributes.yml → cv.yml} +0 -0
- /data/public/ro/pages/{disco/attributes.yml → disco.yml} +0 -0
- /data/public/ro/pages/{index/attributes.yml → index.yml} +0 -0
- /data/public/ro/pages/{jess/attributes.yml → jess.yml} +0 -0
- /data/public/ro/pages/{now/attributes.yml → now.yml} +0 -0
- /data/public/ro/posts/{almost-died-in-an-ice-cave/attributes.yml → almost-died-in-an-ice-cave.yml} +0 -0
- /data/public/ro/posts/{facebook-and-global-extremism/attributes.yml → facebook-and-global-extremism.yml} +0 -0
- /data/public/ro/posts/{lemmings-considered-harmful/attributes.yml → lemmings-considered-harmful.yml} +0 -0
- /data/public/ro/posts/{lost-in-the-desert/attributes.yml → lost-in-the-desert.yml} +0 -0
- /data/public/ro/posts/{mission/attributes.yml → mission.yml} +0 -0
- /data/public/ro/posts/{return-your-laptop/attributes.yml → return-your-laptop.yml} +0 -0
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Test Helper for ro gem v5.0 tests
|
|
2
|
+
#
|
|
3
|
+
# Common setup, assertions, and utilities for unit and integration tests
|
|
4
|
+
|
|
5
|
+
require 'pathname'
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
require 'yaml'
|
|
8
|
+
require 'json'
|
|
9
|
+
|
|
10
|
+
# Load the ro gem
|
|
11
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
12
|
+
require 'ro'
|
|
13
|
+
|
|
14
|
+
module TestHelper
|
|
15
|
+
# Get path to test fixtures directory
|
|
16
|
+
def fixtures_path
|
|
17
|
+
Pathname.new(File.expand_path('../fixtures', __FILE__))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Get path to old structure fixtures
|
|
21
|
+
def old_structure_path
|
|
22
|
+
fixtures_path / 'old_structure'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Get path to new structure fixtures
|
|
26
|
+
def new_structure_path
|
|
27
|
+
fixtures_path / 'new_structure'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Create a temporary test directory
|
|
31
|
+
def create_temp_dir(name = 'test')
|
|
32
|
+
dir = Pathname.new(File.expand_path("../tmp/#{name}_#{Time.now.to_i}", __FILE__))
|
|
33
|
+
FileUtils.mkdir_p(dir)
|
|
34
|
+
dir
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Clean up temporary directory
|
|
38
|
+
def cleanup_temp_dir(dir)
|
|
39
|
+
FileUtils.rm_rf(dir) if dir && dir.exist?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Assert that a path exists
|
|
43
|
+
def assert_path_exists(path, message = nil)
|
|
44
|
+
assert Pathname.new(path).exist?, message || "Expected path to exist: #{path}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Assert that a file contains specific content
|
|
48
|
+
def assert_file_contains(path, content, message = nil)
|
|
49
|
+
actual = File.read(path)
|
|
50
|
+
assert actual.include?(content), message || "Expected file #{path} to contain: #{content}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Assert that a YAML file has a specific key/value
|
|
54
|
+
def assert_yaml_has(path, key, value = nil, message = nil)
|
|
55
|
+
data = YAML.load_file(path)
|
|
56
|
+
assert data.key?(key.to_s) || data.key?(key.to_sym), message || "Expected YAML to have key: #{key}"
|
|
57
|
+
if value
|
|
58
|
+
actual_value = data[key.to_s] || data[key.to_sym]
|
|
59
|
+
assert_equal value, actual_value, message || "Expected #{key} to equal #{value}, got #{actual_value}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Create a test node in old structure format
|
|
64
|
+
def create_old_structure_node(collection_path, node_id, attributes = {}, assets = [])
|
|
65
|
+
node_dir = collection_path / node_id
|
|
66
|
+
FileUtils.mkdir_p(node_dir)
|
|
67
|
+
FileUtils.mkdir_p(node_dir / 'assets')
|
|
68
|
+
|
|
69
|
+
# Write attributes.yml
|
|
70
|
+
File.write(node_dir / 'attributes.yml', attributes.to_yaml)
|
|
71
|
+
|
|
72
|
+
# Create asset files
|
|
73
|
+
assets.each do |asset_file|
|
|
74
|
+
asset_path = node_dir / 'assets' / asset_file
|
|
75
|
+
FileUtils.mkdir_p(asset_path.dirname)
|
|
76
|
+
File.write(asset_path, "test content for #{asset_file}")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
node_dir
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Create a test node in new structure format
|
|
83
|
+
def create_new_structure_node(collection_path, node_id, attributes = {}, assets = [])
|
|
84
|
+
FileUtils.mkdir_p(collection_path)
|
|
85
|
+
|
|
86
|
+
# Write metadata file (identifier.yml)
|
|
87
|
+
File.write(collection_path / "#{node_id}.yml", attributes.to_yaml)
|
|
88
|
+
|
|
89
|
+
# Create asset directory and files
|
|
90
|
+
if assets.any?
|
|
91
|
+
node_dir = collection_path / node_id
|
|
92
|
+
FileUtils.mkdir_p(node_dir)
|
|
93
|
+
|
|
94
|
+
assets.each do |asset_file|
|
|
95
|
+
asset_path = node_dir / asset_file
|
|
96
|
+
FileUtils.mkdir_p(asset_path.dirname)
|
|
97
|
+
File.write(asset_path, "test content for #{asset_file}")
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
collection_path / "#{node_id}.yml"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Base test class
|
|
106
|
+
class RoTestCase
|
|
107
|
+
include TestHelper
|
|
108
|
+
|
|
109
|
+
def setup
|
|
110
|
+
# Override in subclasses
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def teardown
|
|
114
|
+
# Override in subclasses
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Simple assertion methods (compatible with custom test runner)
|
|
118
|
+
def assert(condition, message = "Assertion failed")
|
|
119
|
+
raise AssertionError, message unless condition
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def assert_equal(expected, actual, message = nil)
|
|
123
|
+
msg = message || "Expected #{expected.inspect}, got #{actual.inspect}"
|
|
124
|
+
assert expected == actual, msg
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def assert_nil(actual, message = nil)
|
|
128
|
+
msg = message || "Expected nil, got #{actual.inspect}"
|
|
129
|
+
assert actual.nil?, msg
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def assert_not_nil(actual, message = nil)
|
|
133
|
+
msg = message || "Expected non-nil value"
|
|
134
|
+
assert !actual.nil?, msg
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def assert_raises(exception_class, message = nil)
|
|
138
|
+
begin
|
|
139
|
+
yield
|
|
140
|
+
assert false, message || "Expected #{exception_class} to be raised"
|
|
141
|
+
rescue exception_class
|
|
142
|
+
# Expected
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
class AssertionError < StandardError; end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
puts "Test helper loaded successfully"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test asset
|
data/test/tmp/migration_test_1760824728.backup.20251018215848/posts/assets-only/assets/test.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test asset
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test asset
|
data/test/tmp/migration_test_1760844153.backup.20251019032233/posts/assets-only/assets/test.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test asset
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test asset
|
data/test/tmp/migration_test_1760940939.backup.20251020061539/posts/assets-only/assets/test.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test asset
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test asset
|
data/test/tmp/migration_test_1760941048.backup.20251020061728/posts/assets-only/assets/test.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test asset
|