ukiryu 0.1.1 → 0.1.3
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/.github/workflows/release.yml +58 -14
- data/.gitignore +3 -0
- data/.rubocop_todo.yml +170 -79
- data/Gemfile +1 -1
- data/README.adoc +1603 -576
- data/docs/.gitignore +1 -0
- data/docs/Gemfile +10 -0
- data/docs/INDEX.adoc +261 -0
- data/docs/_config.yml +180 -0
- data/docs/advanced/custom-tool-classes.adoc +581 -0
- data/docs/advanced/index.adoc +20 -0
- data/docs/features/configuration.adoc +657 -0
- data/docs/features/index.adoc +31 -0
- data/docs/features/platform-support.adoc +488 -0
- data/docs/getting-started/core-concepts.adoc +666 -0
- data/docs/getting-started/index.adoc +36 -0
- data/docs/getting-started/installation.adoc +216 -0
- data/docs/getting-started/quick-start.adoc +258 -0
- data/docs/guides/env-var-sets.adoc +388 -0
- data/docs/guides/index.adoc +20 -0
- data/docs/interfaces/cli.adoc +609 -0
- data/docs/interfaces/index.adoc +153 -0
- data/docs/interfaces/ruby-api.adoc +538 -0
- data/docs/lychee.toml +49 -0
- data/docs/reference/configuration-options.adoc +720 -0
- data/docs/reference/error-codes.adoc +634 -0
- data/docs/reference/index.adoc +20 -0
- data/docs/reference/ruby-api.adoc +1217 -0
- data/docs/understanding/index.adoc +20 -0
- data/lib/ukiryu/cli.rb +43 -58
- data/lib/ukiryu/cli_commands/base_command.rb +16 -27
- data/lib/ukiryu/cli_commands/cache_command.rb +100 -0
- data/lib/ukiryu/cli_commands/commands_command.rb +8 -8
- data/lib/ukiryu/cli_commands/commands_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/config_command.rb +49 -7
- data/lib/ukiryu/cli_commands/definitions_command.rb +254 -0
- data/lib/ukiryu/cli_commands/describe_command.rb +13 -7
- data/lib/ukiryu/cli_commands/describe_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/docs_command.rb +148 -0
- data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/extract_command.rb +2 -2
- data/lib/ukiryu/cli_commands/info_command.rb +7 -7
- data/lib/ukiryu/cli_commands/lint_command.rb +167 -0
- data/lib/ukiryu/cli_commands/list_command.rb +6 -6
- data/lib/ukiryu/cli_commands/opts_command.rb +2 -2
- data/lib/ukiryu/cli_commands/opts_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/register_command.rb +144 -0
- data/lib/ukiryu/cli_commands/resolve_command.rb +124 -0
- data/lib/ukiryu/cli_commands/run_command.rb +38 -14
- data/lib/ukiryu/cli_commands/run_file_command.rb +2 -2
- data/lib/ukiryu/cli_commands/system_command.rb +50 -32
- data/lib/ukiryu/cli_commands/validate_command.rb +452 -51
- data/lib/ukiryu/cli_commands/which_command.rb +5 -5
- data/lib/ukiryu/command_builder.rb +81 -23
- data/lib/ukiryu/config/env_provider.rb +3 -3
- data/lib/ukiryu/config/env_schema.rb +6 -6
- data/lib/ukiryu/config.rb +11 -11
- data/lib/ukiryu/definition/definition_cache.rb +238 -0
- data/lib/ukiryu/definition/definition_composer.rb +257 -0
- data/lib/ukiryu/definition/definition_linter.rb +460 -0
- data/lib/ukiryu/definition/definition_validator.rb +320 -0
- data/lib/ukiryu/definition/discovery.rb +239 -0
- data/lib/ukiryu/definition/documentation_generator.rb +429 -0
- data/lib/ukiryu/definition/lint_issue.rb +168 -0
- data/lib/ukiryu/definition/loader.rb +139 -0
- data/lib/ukiryu/definition/metadata.rb +159 -0
- data/lib/ukiryu/definition/source.rb +87 -0
- data/lib/ukiryu/definition/sources/file.rb +138 -0
- data/lib/ukiryu/definition/sources/string.rb +88 -0
- data/lib/ukiryu/definition/validation_result.rb +158 -0
- data/lib/ukiryu/definition/version_resolver.rb +194 -0
- data/lib/ukiryu/definition.rb +40 -0
- data/lib/ukiryu/errors.rb +6 -0
- data/lib/ukiryu/execution_context.rb +11 -11
- data/lib/ukiryu/executor.rb +6 -0
- data/lib/ukiryu/extractors/extractor.rb +6 -5
- data/lib/ukiryu/extractors/help_parser.rb +13 -19
- data/lib/ukiryu/logger.rb +3 -1
- data/lib/ukiryu/models/command_definition.rb +3 -3
- data/lib/ukiryu/models/command_info.rb +1 -1
- data/lib/ukiryu/models/components.rb +1 -3
- data/lib/ukiryu/models/env_var_definition.rb +11 -3
- data/lib/ukiryu/models/flag_definition.rb +15 -0
- data/lib/ukiryu/models/option_definition.rb +7 -7
- data/lib/ukiryu/models/platform_profile.rb +6 -3
- data/lib/ukiryu/models/routing.rb +1 -1
- data/lib/ukiryu/models/tool_definition.rb +2 -4
- data/lib/ukiryu/models/tool_metadata.rb +6 -6
- data/lib/ukiryu/models/validation_result.rb +1 -1
- data/lib/ukiryu/models/version_compatibility.rb +6 -3
- data/lib/ukiryu/models/version_detection.rb +10 -1
- data/lib/ukiryu/{registry.rb → register.rb} +54 -38
- data/lib/ukiryu/register_auto_manager.rb +268 -0
- data/lib/ukiryu/schema_validator.rb +31 -10
- data/lib/ukiryu/shell/base.rb +18 -0
- data/lib/ukiryu/shell/bash.rb +19 -1
- data/lib/ukiryu/shell/cmd.rb +11 -1
- data/lib/ukiryu/shell/powershell.rb +11 -1
- data/lib/ukiryu/shell.rb +1 -1
- data/lib/ukiryu/tool.rb +107 -95
- data/lib/ukiryu/tool_index.rb +22 -22
- data/lib/ukiryu/tools/base.rb +12 -25
- data/lib/ukiryu/tools/generator.rb +7 -7
- data/lib/ukiryu/tools.rb +3 -3
- data/lib/ukiryu/type.rb +20 -5
- data/lib/ukiryu/version.rb +1 -1
- data/lib/ukiryu/version_detector.rb +21 -2
- data/lib/ukiryu.rb +6 -3
- data/ukiryu-proposal.md +41 -41
- data/ukiryu.gemspec +1 -0
- metadata +64 -8
- data/.gitmodules +0 -3
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Custom Tool Classes
|
|
4
|
+
parent: Advanced
|
|
5
|
+
nav_order: 3
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
== Custom Tool Classes
|
|
9
|
+
|
|
10
|
+
You can extend Ukiryu tool classes to create custom behavior specific to your application's needs.
|
|
11
|
+
|
|
12
|
+
// Purpose
|
|
13
|
+
== Purpose
|
|
14
|
+
|
|
15
|
+
This page documents how to create custom tool classes by extending Ukiryu's generated tool classes using standard Ruby inheritance.
|
|
16
|
+
|
|
17
|
+
// References
|
|
18
|
+
== References
|
|
19
|
+
|
|
20
|
+
* link:/interfaces/ruby-api[Ruby API Documentation]
|
|
21
|
+
* link:/advanced/writing-profiles[Writing Tool Profiles]
|
|
22
|
+
|
|
23
|
+
// Concepts
|
|
24
|
+
== Concepts
|
|
25
|
+
|
|
26
|
+
* **Tool class inheritance**: Standard Ruby subclassing of dynamically generated tool classes
|
|
27
|
+
* **Domain-specific wrappers**: Custom methods that encapsulate common workflows
|
|
28
|
+
* **Direct inheritance**: Subclassing the tool class for full access to all methods
|
|
29
|
+
* **Wrapper pattern**: Composing the tool class for isolation and flexibility
|
|
30
|
+
|
|
31
|
+
// Getting Tool Classes
|
|
32
|
+
== Getting Tool Classes
|
|
33
|
+
|
|
34
|
+
Ukiryu provides two ways to access tool classes:
|
|
35
|
+
|
|
36
|
+
=== Traditional API (Instance-based)
|
|
37
|
+
|
|
38
|
+
Returns an instance of the tool:
|
|
39
|
+
|
|
40
|
+
[source,ruby]
|
|
41
|
+
----
|
|
42
|
+
tool = Ukiryu::Tool.get(:inkscape)
|
|
43
|
+
result = tool.execute(:export, { inputs: ['input.svg'], output: 'output.png' })
|
|
44
|
+
----
|
|
45
|
+
|
|
46
|
+
Use this when: You need quick, one-off tool execution without customization.
|
|
47
|
+
|
|
48
|
+
=== OOP API (Class-based)
|
|
49
|
+
|
|
50
|
+
Returns the tool class for subclassing:
|
|
51
|
+
|
|
52
|
+
[source,ruby]
|
|
53
|
+
----
|
|
54
|
+
inkscape_class = Ukiryu::Tools.get_class(:inkscape)
|
|
55
|
+
# Or use it directly for inheritance
|
|
56
|
+
class MyTool < Ukiryu::Tools.get_class(:inkscape)
|
|
57
|
+
end
|
|
58
|
+
----
|
|
59
|
+
|
|
60
|
+
Use this when: You want to create custom tool classes with specialized behavior.
|
|
61
|
+
|
|
62
|
+
// Direct Inheritance Pattern
|
|
63
|
+
== Direct Inheritance Pattern
|
|
64
|
+
|
|
65
|
+
Subclass the tool class directly to add custom methods while retaining full access to all tool functionality.
|
|
66
|
+
|
|
67
|
+
=== Basic Example
|
|
68
|
+
|
|
69
|
+
Create a specialized thumbnail generator:
|
|
70
|
+
|
|
71
|
+
[source,ruby]
|
|
72
|
+
----
|
|
73
|
+
class ThumbnailInkscape < Ukiryu::Tools.get_class(:inkscape)
|
|
74
|
+
def generate_thumbnail(input, output, size: 150)
|
|
75
|
+
execute(:export, {
|
|
76
|
+
inputs: [input],
|
|
77
|
+
output: output,
|
|
78
|
+
width: size,
|
|
79
|
+
height: size,
|
|
80
|
+
dpi: 72
|
|
81
|
+
})
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Use your custom tool
|
|
86
|
+
tool = ThumbnailInkscape.new
|
|
87
|
+
tool.generate_thumbnail('drawing.svg', 'thumbnail.png', size: 200)
|
|
88
|
+
----
|
|
89
|
+
|
|
90
|
+
=== Multiple Specialized Methods
|
|
91
|
+
|
|
92
|
+
Create a tool with multiple domain-specific operations:
|
|
93
|
+
|
|
94
|
+
[source,ruby]
|
|
95
|
+
----
|
|
96
|
+
class AssetGenerator < Ukiryu::Tools.get_class(:inkscape)
|
|
97
|
+
# Generate icons for web use
|
|
98
|
+
def icon_set(input, basename)
|
|
99
|
+
execute(:export, inputs: [input], output: "#{basename}_icon.png", width: 32, height: 32)
|
|
100
|
+
execute(:export, inputs: [input], output: "#{basename}_icon@2x.png", width: 64, height: 64)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Generate high-resolution export
|
|
104
|
+
def print_export(input, output)
|
|
105
|
+
execute(:export, {
|
|
106
|
+
inputs: [input],
|
|
107
|
+
output: output,
|
|
108
|
+
dpi: 300,
|
|
109
|
+
export_text_to_path: true
|
|
110
|
+
})
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Generate optimized web image
|
|
114
|
+
def web_image(input, output)
|
|
115
|
+
execute(:export, {
|
|
116
|
+
inputs: [input],
|
|
117
|
+
output: output,
|
|
118
|
+
dpi: 96
|
|
119
|
+
})
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
tool = AssetGenerator.new
|
|
124
|
+
tool.icon_set('logo.svg', 'app')
|
|
125
|
+
tool.print_export('document.svg', 'document-print.png')
|
|
126
|
+
tool.web_image('banner.svg', 'banner.png')
|
|
127
|
+
----
|
|
128
|
+
|
|
129
|
+
=== Parameter Templates
|
|
130
|
+
|
|
131
|
+
Create methods that pre-configure common parameter sets:
|
|
132
|
+
|
|
133
|
+
[source,ruby]
|
|
134
|
+
----
|
|
135
|
+
class PdfConverter < Ukiryu::Tools.get_class(:inkscape)
|
|
136
|
+
def convert_to_pdf(input, output, options = {})
|
|
137
|
+
params = {
|
|
138
|
+
inputs: [input],
|
|
139
|
+
output: output,
|
|
140
|
+
export_text_to_path: true,
|
|
141
|
+
export_ignore_filters: false
|
|
142
|
+
}.merge(options)
|
|
143
|
+
|
|
144
|
+
execute(:export, params)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Use default settings
|
|
149
|
+
tool = PdfConverter.new
|
|
150
|
+
tool.convert_to_pdf('drawing.svg', 'drawing.pdf')
|
|
151
|
+
|
|
152
|
+
# Override specific settings
|
|
153
|
+
tool.convert_to_pdf('drawing.svg', 'drawing.pdf', export_text_to_path: false)
|
|
154
|
+
----
|
|
155
|
+
|
|
156
|
+
// Wrapper Pattern
|
|
157
|
+
== Wrapper Pattern
|
|
158
|
+
|
|
159
|
+
Compose the tool class instead of inheriting, providing complete isolation from the underlying tool implementation.
|
|
160
|
+
|
|
161
|
+
=== When to Use Wrapper Pattern
|
|
162
|
+
|
|
163
|
+
Use the wrapper pattern when you need:
|
|
164
|
+
|
|
165
|
+
* Complete isolation from tool API changes
|
|
166
|
+
* Selective exposure of tool methods
|
|
167
|
+
* Custom error handling
|
|
168
|
+
* Integration with non-Ukiryu code
|
|
169
|
+
|
|
170
|
+
=== Basic Wrapper
|
|
171
|
+
|
|
172
|
+
Create a clean interface that exposes only what you need:
|
|
173
|
+
|
|
174
|
+
[source,ruby]
|
|
175
|
+
----
|
|
176
|
+
class ThumbnailService
|
|
177
|
+
def initialize
|
|
178
|
+
@tool = Ukiryu::Tools.get_class(:inkscape).new
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def create(input, output, size: 150)
|
|
182
|
+
result = @tool.execute(:export, {
|
|
183
|
+
inputs: [input],
|
|
184
|
+
output: output,
|
|
185
|
+
width: size,
|
|
186
|
+
height: size
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
raise "Thumbnail generation failed: #{result.stderr}" unless result.success?
|
|
190
|
+
|
|
191
|
+
result
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def batch_create(inputs, output_dir, size: 150)
|
|
195
|
+
inputs.map do |input|
|
|
196
|
+
basename = File.basename(input, '.svg')
|
|
197
|
+
output = File.join(output_dir, "#{basename}_thumb.png")
|
|
198
|
+
create(input, output, size: size)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
service = ThumbnailService.new
|
|
204
|
+
service.create('drawing.svg', 'drawing_thumb.png')
|
|
205
|
+
service.batch_create(Dir['*.svg'], 'thumbnails', size: 200)
|
|
206
|
+
----
|
|
207
|
+
|
|
208
|
+
=== Method Forwarding
|
|
209
|
+
|
|
210
|
+
Use `method_missing` to forward undefined methods to the wrapped tool:
|
|
211
|
+
|
|
212
|
+
[source,ruby]
|
|
213
|
+
----
|
|
214
|
+
class InkscapeWrapper
|
|
215
|
+
def initialize(tool_class: Ukiryu::Tools.get_class(:inkscape))
|
|
216
|
+
@tool = tool_class.new
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Custom methods
|
|
220
|
+
def quick_export(input, output)
|
|
221
|
+
@tool.execute(:export, inputs: [input], output: output, dpi: 96)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Forward all other methods to the tool
|
|
225
|
+
def method_missing(name, *args, &block)
|
|
226
|
+
@tool.send(name, *args, &block)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def respond_to_missing?(name, include_private = false)
|
|
230
|
+
@tool.respond_to?(name, include_private) || super
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
wrapper = InkscapeWrapper.new
|
|
235
|
+
wrapper.quick_export('file.svg', 'file.png') # Custom method
|
|
236
|
+
wrapper.execute(:query, inputs: ['file.svg']) # Forwarded method
|
|
237
|
+
wrapper.version # Forwarded method
|
|
238
|
+
----
|
|
239
|
+
|
|
240
|
+
=== Multi-Tool Wrapper
|
|
241
|
+
|
|
242
|
+
Wrap multiple tools in a single cohesive interface:
|
|
243
|
+
|
|
244
|
+
[source,ruby]
|
|
245
|
+
----
|
|
246
|
+
class GraphicsPipeline
|
|
247
|
+
def initialize
|
|
248
|
+
@inkscape = Ukiryu::Tools.get_class(:inkscape).new
|
|
249
|
+
@imagemagick = Ukiryu::Tools.get_class(:imagemagick).new
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def svg_to_optimized_png(input, output)
|
|
253
|
+
# Export SVG to PNG with Inkscape
|
|
254
|
+
@inkscape.execute(:export, inputs: [input], output: output, dpi: 150)
|
|
255
|
+
|
|
256
|
+
# Optimize with ImageMagick
|
|
257
|
+
@imagemagick.execute(:mogrify, inputs: [output], strip: true, quality: 85)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def batch_convert(input_dir, output_dir)
|
|
261
|
+
Dir[File.join(input_dir, '*.svg')].each do |input|
|
|
262
|
+
basename = File.basename(input, '.svg')
|
|
263
|
+
output = File.join(output_dir, "#{basename}.png")
|
|
264
|
+
svg_to_optimized_png(input, output)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
pipeline = GraphicsPipeline.new
|
|
270
|
+
pipeline.svg_to_optimized_png('drawing.svg', 'drawing.png')
|
|
271
|
+
pipeline.batch_convert('assets/svg', 'assets/png')
|
|
272
|
+
----
|
|
273
|
+
|
|
274
|
+
// Pattern Comparison
|
|
275
|
+
== Pattern Comparison
|
|
276
|
+
|
|
277
|
+
=== By Use Case
|
|
278
|
+
|
|
279
|
+
[cols="1,1,4"]
|
|
280
|
+
|===
|
|
281
|
+
|Use Case |Recommended Pattern |Rationale
|
|
282
|
+
|
|
283
|
+
|Application-specific tools
|
|
284
|
+
|Direct Inheritance
|
|
285
|
+
|Full access to tool methods, minimal boilerplate
|
|
286
|
+
|
|
287
|
+
|Library/API design
|
|
288
|
+
|Wrapper Pattern
|
|
289
|
+
|Isolation from API changes, controlled interface
|
|
290
|
+
|
|
291
|
+
|Internal scripts
|
|
292
|
+
|Direct Inheritance
|
|
293
|
+
|Quick implementation, direct tool access
|
|
294
|
+
|
|
295
|
+
|Complex workflows
|
|
296
|
+
|Wrapper Pattern
|
|
297
|
+
|Custom orchestration, multi-tool composition
|
|
298
|
+
|
|
299
|
+
|Testing/mocking
|
|
300
|
+
|Wrapper Pattern
|
|
301
|
+
|Easy to inject mock tool implementations
|
|
302
|
+
|===
|
|
303
|
+
|
|
304
|
+
=== By Characteristic
|
|
305
|
+
|
|
306
|
+
[cols="1,1,1"]
|
|
307
|
+
|===
|
|
308
|
+
|Characteristic |Direct Inheritance |Wrapper Pattern
|
|
309
|
+
|
|
310
|
+
|Tool method access
|
|
311
|
+
|All methods available
|
|
312
|
+
|Selective exposure
|
|
313
|
+
|
|
314
|
+
|Isolation from changes
|
|
315
|
+
|Low (inherits API changes)
|
|
316
|
+
|High (controlled interface)
|
|
317
|
+
|
|
318
|
+
|Boilerplate code
|
|
319
|
+
|Minimal
|
|
320
|
+
|More (forwarding needed)
|
|
321
|
+
|
|
322
|
+
|Customization flexibility
|
|
323
|
+
|High (can override methods)
|
|
324
|
+
|High (any interface design)
|
|
325
|
+
|
|
326
|
+
|Testing ease
|
|
327
|
+
|Moderate
|
|
328
|
+
|High (easy to mock)
|
|
329
|
+
|
|
330
|
+
|Learning curve
|
|
331
|
+
|Low (standard inheritance)
|
|
332
|
+
|Moderate (composition patterns)
|
|
333
|
+
|===
|
|
334
|
+
|
|
335
|
+
// Best Practices
|
|
336
|
+
== Best Practices
|
|
337
|
+
|
|
338
|
+
=== Naming Conventions
|
|
339
|
+
|
|
340
|
+
Use descriptive names that indicate the tool's purpose:
|
|
341
|
+
|
|
342
|
+
[source,ruby]
|
|
343
|
+
----
|
|
344
|
+
# Good - descriptive and specific
|
|
345
|
+
class ThumbnailInkscape < Ukiryu::Tools.get_class(:inkscape)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
class AssetGenerator < Ukiryu::Tools.get_class(:inkscape)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
class PdfConverter < Ukiryu::Tools.get_class(:inkscape)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Avoid - too generic
|
|
355
|
+
class MyTool < Ukiryu::Tools.get_class(:inkscape)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
class CustomInkscape < Ukiryu::Tools.get_class(:inkscape)
|
|
359
|
+
end
|
|
360
|
+
----
|
|
361
|
+
|
|
362
|
+
=== Error Handling
|
|
363
|
+
|
|
364
|
+
Add appropriate error handling for custom methods:
|
|
365
|
+
|
|
366
|
+
[source,ruby]
|
|
367
|
+
----
|
|
368
|
+
class RobustExporter < Ukiryu::Tools.get_class(:inkscape)
|
|
369
|
+
def safe_export(input, output, options = {})
|
|
370
|
+
raise ArgumentError, "Input file not found: #{input}" unless File.exist?(input)
|
|
371
|
+
|
|
372
|
+
result = execute(:export, {
|
|
373
|
+
inputs: [input],
|
|
374
|
+
output: output
|
|
375
|
+
}.merge(options))
|
|
376
|
+
|
|
377
|
+
unless result.success?
|
|
378
|
+
raise "Export failed: #{result.stderr}"
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
result
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
----
|
|
385
|
+
|
|
386
|
+
=== Documentation
|
|
387
|
+
|
|
388
|
+
Document your custom methods clearly:
|
|
389
|
+
|
|
390
|
+
[source,ruby]
|
|
391
|
+
----
|
|
392
|
+
class DocumentConverter < Ukiryu::Tools.get_class(:inkscape)
|
|
393
|
+
# Convert SVG to PDF with print-optimized settings
|
|
394
|
+
#
|
|
395
|
+
# @param input [String] Path to input SVG file
|
|
396
|
+
# @param output [String] Path to output PDF file
|
|
397
|
+
# @return [Result] Execution result
|
|
398
|
+
# @raise [RuntimeError] If conversion fails
|
|
399
|
+
#
|
|
400
|
+
# Uses 300 DPI and exports text to paths for print compatibility.
|
|
401
|
+
def to_pdf(input, output)
|
|
402
|
+
execute(:export, {
|
|
403
|
+
inputs: [input],
|
|
404
|
+
output: output,
|
|
405
|
+
dpi: 300,
|
|
406
|
+
export_text_to_path: true
|
|
407
|
+
}).tap do |result|
|
|
408
|
+
raise "PDF conversion failed" unless result.success?
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
----
|
|
413
|
+
|
|
414
|
+
=== Configuration
|
|
415
|
+
|
|
416
|
+
Use class-level configuration for defaults:
|
|
417
|
+
|
|
418
|
+
[source,ruby]
|
|
419
|
+
----
|
|
420
|
+
class ConfigurableConverter < Ukiryu::Tools.get_class(:inkscape)
|
|
421
|
+
class << self
|
|
422
|
+
attr_accessor :default_dpi, :default_format
|
|
423
|
+
|
|
424
|
+
def configure
|
|
425
|
+
yield self
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
self.default_dpi = 96
|
|
430
|
+
self.default_format = :png
|
|
431
|
+
|
|
432
|
+
def convert(input, output, options = {})
|
|
433
|
+
params = {
|
|
434
|
+
inputs: [input],
|
|
435
|
+
output: output,
|
|
436
|
+
dpi: self.class.default_dpi
|
|
437
|
+
}.merge(options)
|
|
438
|
+
|
|
439
|
+
execute(:export, params)
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# Configure globally
|
|
444
|
+
ConfigurableConverter.configure do |config|
|
|
445
|
+
config.default_dpi = 300
|
|
446
|
+
config.default_format = :svg
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
tool = ConfigurableConverter.new
|
|
450
|
+
tool.convert('file.svg', 'file.png') # Uses 300 DPI default
|
|
451
|
+
----
|
|
452
|
+
|
|
453
|
+
// Reload Behavior
|
|
454
|
+
== Reload Behavior
|
|
455
|
+
|
|
456
|
+
Tool classes are generated once when the application starts and remain cached for the lifetime of the process.
|
|
457
|
+
|
|
458
|
+
=== Profile Updates
|
|
459
|
+
|
|
460
|
+
When tool profiles are updated:
|
|
461
|
+
|
|
462
|
+
* New methods/parameters are available after application restart
|
|
463
|
+
* Existing custom classes continue to work with the updated profiles
|
|
464
|
+
* Removed methods will cause `NoMethodError` until the custom class is updated
|
|
465
|
+
|
|
466
|
+
=== Development Workflow
|
|
467
|
+
|
|
468
|
+
For development, restart your application after profile changes:
|
|
469
|
+
|
|
470
|
+
[source,bash]
|
|
471
|
+
----
|
|
472
|
+
# In development
|
|
473
|
+
bundle exec rackup -s thin -O
|
|
474
|
+
|
|
475
|
+
# After profile changes, restart (Ctrl+C then run again)
|
|
476
|
+
----
|
|
477
|
+
|
|
478
|
+
For production, deploy updated code and profiles together:
|
|
479
|
+
|
|
480
|
+
[source,bash]
|
|
481
|
+
----
|
|
482
|
+
# Deploy updated profiles and code
|
|
483
|
+
git pull
|
|
484
|
+
bundle exec pumactl -P tmp/pids/puma.pid phased-restart
|
|
485
|
+
----
|
|
486
|
+
|
|
487
|
+
// Complete Example
|
|
488
|
+
== Complete Example
|
|
489
|
+
|
|
490
|
+
A complete asset pipeline using custom tool classes:
|
|
491
|
+
|
|
492
|
+
[source,ruby]
|
|
493
|
+
----
|
|
494
|
+
# lib/asset_pipeline.rb
|
|
495
|
+
|
|
496
|
+
require 'ukiryu'
|
|
497
|
+
|
|
498
|
+
# SVG to PNG converter with standard web settings
|
|
499
|
+
class WebAssetConverter < Ukiryu::Tools.get_class(:inkscape)
|
|
500
|
+
# Standard web-optimized PNG
|
|
501
|
+
def web_png(input, output)
|
|
502
|
+
execute(:export, {
|
|
503
|
+
inputs: [input],
|
|
504
|
+
output: output,
|
|
505
|
+
dpi: 96,
|
|
506
|
+
export_text_to_path: false
|
|
507
|
+
})
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# High-DPI (Retina) version
|
|
511
|
+
def high_dpi_png(input, output, scale: 2)
|
|
512
|
+
execute(:export, {
|
|
513
|
+
inputs: [input],
|
|
514
|
+
output: output,
|
|
515
|
+
dpi: 96 * scale
|
|
516
|
+
})
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# Icon set generation
|
|
520
|
+
def icon_set(input, basename, sizes: [16, 32, 64, 128, 256])
|
|
521
|
+
sizes.each do |size|
|
|
522
|
+
execute(:export, {
|
|
523
|
+
inputs: [input],
|
|
524
|
+
output: "#{basename}_#{size}x#{size}.png",
|
|
525
|
+
width: size,
|
|
526
|
+
height: size,
|
|
527
|
+
dpi: 96
|
|
528
|
+
})
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
# Asset pipeline that orchestrates conversion
|
|
534
|
+
class AssetPipeline
|
|
535
|
+
def initialize(source_dir, output_dir)
|
|
536
|
+
@source_dir = source_dir
|
|
537
|
+
@output_dir = output_dir
|
|
538
|
+
@converter = WebAssetConverter.new
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
def process_all
|
|
542
|
+
ensure_output_dir
|
|
543
|
+
|
|
544
|
+
svg_files.each do |svg_file|
|
|
545
|
+
basename = File.basename(svg_file, '.svg')
|
|
546
|
+
process_asset(svg_file, basename)
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
private
|
|
551
|
+
|
|
552
|
+
def ensure_output_dir
|
|
553
|
+
FileUtils.mkdir_p(@output_dir)
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def svg_files
|
|
557
|
+
Dir[File.join(@source_dir, '*.svg')]
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def process_asset(input_file, basename)
|
|
561
|
+
output_file = File.join(@output_dir, "#{basename}.png")
|
|
562
|
+
|
|
563
|
+
puts "Processing #{basename}..."
|
|
564
|
+
@converter.web_png(input_file, output_file)
|
|
565
|
+
puts " ✓ Created #{basename}.png"
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
# Usage
|
|
570
|
+
if __FILE__ == $PROGRAM_NAME
|
|
571
|
+
pipeline = AssetPipeline.new('assets/svg', 'assets/png')
|
|
572
|
+
pipeline.process_all
|
|
573
|
+
end
|
|
574
|
+
----
|
|
575
|
+
|
|
576
|
+
// See Also
|
|
577
|
+
== See Also
|
|
578
|
+
|
|
579
|
+
* link:/interfaces/ruby-api[Ruby API] - Tool class reference
|
|
580
|
+
* link:/advanced/writing-profiles[Writing Tool Profiles] - Creating custom tool definitions
|
|
581
|
+
* link:/features/configuration[Configuration Methods] - Setting up tool behavior
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Advanced
|
|
4
|
+
nav_order: 7
|
|
5
|
+
has_children: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
= Advanced
|
|
9
|
+
|
|
10
|
+
Advanced topics for extending and customizing Ukiryu.
|
|
11
|
+
|
|
12
|
+
// Overview
|
|
13
|
+
== Overview
|
|
14
|
+
|
|
15
|
+
This section covers advanced usage and extension:
|
|
16
|
+
|
|
17
|
+
* link:/advanced/writing-profiles[Writing Tool Profiles] - Create custom tool profiles
|
|
18
|
+
* link:/advanced/tool-versions[Tool Version Management] - Multiple versions and compatibility
|
|
19
|
+
* link:/advanced/platform-handling[Platform-Specific Handling] - Advanced platform adaptation
|
|
20
|
+
* link:/advanced/performance[Performance Optimization] - Caching and optimization
|