iron-cms 0.5.2 → 0.7.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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/builds/iron.css +682 -388
  3. data/app/assets/tailwind/iron/application.css +1 -0
  4. data/app/assets/tailwind/iron/components/button.css +0 -7
  5. data/app/assets/tailwind/iron/components/checkbox.css +21 -0
  6. data/app/assets/tailwind/iron/components/form.css +1 -1
  7. data/app/assets/tailwind/iron/lexxy.css +165 -51
  8. data/app/controllers/iron/account/exports_controller.rb +26 -0
  9. data/app/controllers/iron/account/imports_controller.rb +27 -0
  10. data/app/helpers/iron/form_builder.rb +7 -0
  11. data/app/javascript/iron/controllers/local_preference_controller.js +62 -0
  12. data/app/jobs/iron/export_job.rb +9 -0
  13. data/app/jobs/iron/import_job.rb +9 -0
  14. data/app/models/concerns/iron/broadcastable.rb +9 -0
  15. data/app/models/concerns/iron/processable.rb +34 -0
  16. data/app/models/iron/account/export.rb +86 -0
  17. data/app/models/iron/account/import.rb +208 -0
  18. data/app/models/iron/block_definition/exportable.rb +14 -0
  19. data/app/models/iron/block_definition/importable.rb +27 -0
  20. data/app/models/iron/block_definition.rb +1 -1
  21. data/app/models/iron/content_type/exportable.rb +20 -0
  22. data/app/models/iron/content_type/importable.rb +32 -0
  23. data/app/models/iron/content_type.rb +1 -1
  24. data/app/models/iron/current.rb +6 -3
  25. data/app/models/iron/entry/exportable.rb +49 -0
  26. data/app/models/iron/entry/importable.rb +181 -0
  27. data/app/models/iron/entry.rb +1 -1
  28. data/app/models/iron/field.rb +9 -1
  29. data/app/models/iron/field_definition/exportable.rb +23 -0
  30. data/app/models/iron/field_definition/importable.rb +39 -0
  31. data/app/models/iron/field_definition.rb +1 -1
  32. data/app/models/iron/fields/block.rb +34 -0
  33. data/app/models/iron/fields/block_list.rb +8 -0
  34. data/app/models/iron/fields/boolean.rb +4 -0
  35. data/app/models/iron/fields/date.rb +4 -0
  36. data/app/models/iron/fields/file.rb +16 -0
  37. data/app/models/iron/fields/number.rb +4 -0
  38. data/app/models/iron/fields/reference.rb +4 -0
  39. data/app/models/iron/fields/reference_list.rb +4 -0
  40. data/app/models/iron/fields/rich_text_area.rb +32 -0
  41. data/app/models/iron/fields/text_area.rb +4 -0
  42. data/app/models/iron/fields/text_field.rb +4 -0
  43. data/app/models/iron/user.rb +2 -0
  44. data/app/views/iron/account/exports/index.html.erb +43 -0
  45. data/app/views/iron/account/exports/new.html.erb +39 -0
  46. data/app/views/iron/account/exports/show.html.erb +40 -0
  47. data/app/views/iron/account/imports/index.html.erb +43 -0
  48. data/app/views/iron/account/imports/new.html.erb +52 -0
  49. data/app/views/iron/account/imports/show.html.erb +37 -0
  50. data/app/views/iron/content_types/index.html.erb +1 -8
  51. data/app/views/iron/entries/fields/_block.html.erb +23 -10
  52. data/app/views/iron/entries/fields/_file.html.erb +3 -3
  53. data/app/views/iron/settings/show.html.erb +4 -11
  54. data/app/views/layouts/iron/application.html.erb +14 -0
  55. data/config/routes.rb +3 -9
  56. data/db/migrate/20251209103109_create_iron_account_exports.rb +13 -0
  57. data/db/migrate/20251209103110_create_iron_account_imports.rb +13 -0
  58. data/lib/iron/version.rb +1 -1
  59. data/lib/iron.rb +1 -1
  60. metadata +41 -28
  61. data/app/controllers/iron/contents_controller.rb +0 -33
  62. data/app/controllers/iron/schemas_controller.rb +0 -32
  63. data/app/models/concerns/iron/csv_serializable.rb +0 -28
  64. data/app/models/iron/archive.rb +0 -69
  65. data/app/models/iron/block_definition/portable.rb +0 -20
  66. data/app/models/iron/content_export.rb +0 -73
  67. data/app/models/iron/content_import/entry_builder.rb +0 -80
  68. data/app/models/iron/content_import/entry_snapshot.rb +0 -23
  69. data/app/models/iron/content_import/field_reconstructor.rb +0 -276
  70. data/app/models/iron/content_import/field_snapshot.rb +0 -33
  71. data/app/models/iron/content_import/registry.rb +0 -32
  72. data/app/models/iron/content_import/session.rb +0 -89
  73. data/app/models/iron/content_import.rb +0 -15
  74. data/app/models/iron/content_type/portable.rb +0 -30
  75. data/app/models/iron/entry/portable.rb +0 -35
  76. data/app/models/iron/field/portable.rb +0 -33
  77. data/app/models/iron/field_definition/portable.rb +0 -42
  78. data/app/models/iron/schema_archive.rb +0 -71
  79. data/app/models/iron/schema_exporter.rb +0 -15
  80. data/app/models/iron/schema_importer/import_strategy.rb +0 -59
  81. data/app/models/iron/schema_importer/merge_strategy.rb +0 -52
  82. data/app/models/iron/schema_importer/replace_strategy.rb +0 -51
  83. data/app/models/iron/schema_importer/safe_strategy.rb +0 -55
  84. data/app/models/iron/schema_importer.rb +0 -108
  85. data/app/views/iron/contents/new.html.erb +0 -34
  86. data/app/views/iron/schemas/new.html.erb +0 -57
  87. data/lib/iron/test_fixtures.rb +0 -50
@@ -1,276 +0,0 @@
1
- require "csv"
2
-
3
- module Iron
4
- class ContentImport::FieldReconstructor
5
- def initialize(content_type, registry, archive)
6
- @content_type = content_type
7
- @registry = registry
8
- @archive = archive
9
- end
10
-
11
- def reconstruct_all(csv_data)
12
- entry_snapshots = build_entry_snapshots(csv_data)
13
-
14
- entry_snapshots.each do |entry_gid, snapshot|
15
- reconstruct_entry(entry_gid, snapshot)
16
- end
17
- end
18
-
19
- private
20
-
21
- def build_entry_snapshots(csv_data)
22
- snapshots = {}
23
- return snapshots unless csv_data.present?
24
-
25
- CSV.parse(csv_data, headers: true) do |row|
26
- entry_key = row["entry_key"]
27
- next if entry_key.blank?
28
-
29
- snapshot = (snapshots[entry_key] ||= ContentImport::EntrySnapshot.new(entry_key))
30
- populate_snapshot(snapshot, row)
31
- end
32
-
33
- snapshots
34
- end
35
-
36
- def populate_snapshot(snapshot, row)
37
- path = row["path"]
38
- return if path.blank?
39
-
40
- if path == "/route"
41
- snapshot.add_route(row["value"])
42
- elsif path.start_with?("/fields/")
43
- add_field_snapshot(snapshot, row)
44
- end
45
- end
46
-
47
- def add_field_snapshot(snapshot, row)
48
- locale_code = row["locale"]
49
- return unless locale_code.present?
50
-
51
- field_snapshot = ContentImport::FieldSnapshot.new(
52
- locale_code: locale_code,
53
- path: row["path"],
54
- value: row["value"]
55
- )
56
- snapshot.add_field_snapshot(field_snapshot)
57
- end
58
-
59
- def reconstruct_entry(entry_gid, snapshot)
60
- entry = @registry.find_entry(entry_gid)
61
- return unless entry
62
-
63
- update_entry_route(entry, snapshot.route) unless snapshot.route.nil?
64
- reconstruct_entry_fields(entry, snapshot)
65
- end
66
-
67
- def update_entry_route(entry, route)
68
- entry.update_columns(route: route)
69
- end
70
-
71
- def reconstruct_entry_fields(entry, snapshot)
72
- snapshot.group_field_snapshots_by_locale.each do |locale_code, field_snapshots|
73
- locale = Iron::Locale.find_by(code: locale_code)
74
- next unless locale
75
-
76
- reconstruct_fields_for_locale(entry, field_snapshots, locale)
77
- end
78
- end
79
-
80
- def reconstruct_fields_for_locale(entry, field_snapshots, locale)
81
- field_groups = field_snapshots
82
- .select(&:field_path?)
83
- .group_by(&:field_handle)
84
-
85
- field_groups.each do |handle, snapshots|
86
- definition = @content_type.field_definitions.find_by(handle: handle)
87
- next unless definition
88
-
89
- field = entry.find_or_build_field(definition, locale)
90
- reconstruct_field(field, snapshots)
91
- end
92
- end
93
-
94
- def reconstruct_field(field, snapshots)
95
- case field
96
- when Iron::Fields::TextField
97
- field.value_string = snapshots.first.value
98
- when Iron::Fields::TextArea
99
- field.value_text = snapshots.first.value
100
- when Iron::Fields::Number
101
- field.value_decimal = snapshots.first.value.to_f if snapshots.first.value.present?
102
- when Iron::Fields::Boolean
103
- field.value_boolean = ActiveModel::Type::Boolean.new.cast(snapshots.first.value)
104
- when Iron::Fields::Date
105
- field.value_datetime = Time.zone.parse(snapshots.first.value) if snapshots.first.value.present?
106
- when Iron::Fields::File
107
- attach_file(field, snapshots.first.value)
108
- when Iron::Fields::Reference
109
- field.referenced_entry = @registry.find_entry(snapshots.first.value)
110
- when Iron::Fields::RichTextArea
111
- reconstruct_rich_text(field, snapshots)
112
- when Iron::Fields::ReferenceList
113
- reconstruct_reference_list(field, snapshots)
114
- when Iron::Fields::Block
115
- reconstruct_block(field, snapshots)
116
- when Iron::Fields::BlockList
117
- reconstruct_block_list(field, snapshots)
118
- end
119
-
120
- field.save!(validate: false)
121
- rescue ArgumentError
122
- # Handle date parsing errors
123
- field.save!(validate: false)
124
- end
125
-
126
- def attach_file(field, blob_id)
127
- return unless blob_id.present?
128
-
129
- data = @archive["uploads/#{blob_id}"]
130
- return unless data
131
-
132
- field.file.attach(
133
- io: StringIO.new(data),
134
- filename: "imported-#{blob_id}",
135
- content_type: "application/octet-stream"
136
- )
137
- end
138
-
139
- def reconstruct_rich_text(field, snapshots)
140
- html_snapshot = snapshots.find { |s| s.path.end_with?("/html") }
141
-
142
- if html_snapshot&.value.present?
143
- field.update!(rich_text: html_snapshot.value)
144
- else
145
- field.rich_text&.destroy
146
- end
147
- end
148
-
149
- def reconstruct_reference_list(field, snapshots)
150
- # Group snapshots by rank (the first segment after the field handle)
151
- field.save!(validate: false) if field.new_record?
152
-
153
- items = snapshots.filter_map do |snapshot|
154
- segments = snapshot.field_segments
155
- next if segments.length < 2
156
-
157
- rank = segments.last
158
- entry = @registry.find_entry(snapshot.value)
159
- next unless entry
160
-
161
- { rank:, entry: }
162
- end
163
-
164
- existing = field.references.index_by(&:rank)
165
- kept_ids = []
166
-
167
- items.each do |item|
168
- reference = existing[item[:rank]] || field.references.build(rank: item[:rank])
169
- reference.entry = item[:entry]
170
- reference.save!
171
- kept_ids << reference.id
172
- end
173
-
174
- field.references.where.not(id: kept_ids).destroy_all
175
- end
176
-
177
- def reconstruct_block(field, snapshots)
178
- # Fields::Block IS the block - it doesn't have a separate block relation
179
- # The field already has the block definition through field.definition
180
-
181
- # Group snapshots by field handle
182
- field_snapshots = snapshots.reject { |s| s.path.include?("/_block") }
183
- reconstruct_block_fields(field, field_snapshots, field.locale)
184
- end
185
-
186
- def reconstruct_block_list(field, snapshots)
187
- # Group snapshots by block rank
188
- blocks_by_rank = {}
189
-
190
- snapshots.each do |snapshot|
191
- segments = snapshot.field_segments
192
- next if segments.size < 2
193
-
194
- rank = segments[1]
195
- blocks_by_rank[rank] ||= []
196
- blocks_by_rank[rank] << snapshot
197
- end
198
-
199
- # Clear all existing blocks first to avoid rank conflicts
200
- field.blocks.destroy_all
201
-
202
- blocks_by_rank.each do |rank, block_snapshots|
203
- # Find block handle
204
- block_snapshot = block_snapshots.find { |s| s.path.include?("/_block") }
205
- block_handle = block_snapshot&.value
206
- next unless block_handle
207
-
208
- block_definition = Iron::BlockDefinition.find_by(handle: block_handle)
209
- next unless block_definition
210
-
211
- block = field.blocks.build(
212
- rank: rank,
213
- entry: field.entry,
214
- locale: field.locale,
215
- definition: block_definition
216
- )
217
-
218
- # Save the block first to establish it in the database
219
- block.save!(validate: false)
220
-
221
- # Then reconstruct its fields
222
- field_snapshots = block_snapshots.reject { |s| s.path.include?("/_block") }
223
- reconstruct_block_fields(block, field_snapshots, field.locale)
224
- end
225
- end
226
-
227
- def reconstruct_block_fields(block, snapshots, locale)
228
- # Group snapshots by field handle
229
- field_groups = {}
230
-
231
- snapshots.each do |snapshot|
232
- field_handle = block_child_handle(block, snapshot)
233
- next unless field_handle
234
-
235
- field_groups[field_handle] ||= []
236
- field_groups[field_handle] << snapshot
237
- end
238
-
239
- # Get the actual block definition
240
- block_def = if block.respond_to?(:block_definition)
241
- block.block_definition
242
- else
243
- block.definition
244
- end
245
-
246
- # Clear existing fields to avoid rank conflicts
247
- block.fields.where(locale: locale).destroy_all
248
-
249
- field_groups.each do |handle, field_snapshots|
250
- definition = block_def.field_definitions.find_by(handle: handle)
251
- next unless definition
252
-
253
- field = block.fields.build(
254
- type: definition.field_type,
255
- entry: block.entry,
256
- definition: definition,
257
- locale: locale
258
- )
259
-
260
- reconstruct_field(field, field_snapshots)
261
- end
262
- end
263
-
264
- def block_child_handle(block, snapshot)
265
- relative_segments = snapshot.field_segments
266
- block_path = block.send(:field_path)
267
-
268
- if block_path.present?
269
- block_segments = block_path.sub(%r{^/fields/}, "").split("/")
270
- relative_segments = relative_segments.drop(block_segments.length)
271
- end
272
-
273
- relative_segments.first
274
- end
275
- end
276
- end
@@ -1,33 +0,0 @@
1
- module Iron
2
- class ContentImport::FieldSnapshot
3
- attr_reader :locale_code, :path, :value
4
-
5
- def initialize(locale_code:, path:, value:)
6
- @locale_code = locale_code
7
- @path = path
8
- @value = value
9
- end
10
-
11
- def field_handle
12
- return nil unless field_path?
13
-
14
- segments[0]
15
- end
16
-
17
- def field_segments
18
- return [] unless field_path?
19
-
20
- segments
21
- end
22
-
23
- def field_path?
24
- @path&.start_with?("/fields/")
25
- end
26
-
27
- private
28
-
29
- def segments
30
- @segments ||= @path.sub(%r{^/fields/}, "").split("/")
31
- end
32
- end
33
- end
@@ -1,32 +0,0 @@
1
- module Iron
2
- class ContentImport::Registry
3
- def initialize
4
- @entries_by_original_gid = {}
5
- end
6
-
7
- def register_entry(original_gid, entry)
8
- @entries_by_original_gid[original_gid] = entry
9
- @entries_by_original_gid[entry.to_gid.to_s] = entry
10
- end
11
-
12
- def find_entry(gid_string)
13
- return nil if gid_string.blank?
14
-
15
- cached_entry = @entries_by_original_gid[gid_string]
16
- return cached_entry if cached_entry
17
-
18
- locate_existing_entry(gid_string)
19
- end
20
-
21
- private
22
-
23
- def locate_existing_entry(gid_string)
24
- entry = ::GlobalID::Locator.locate(gid_string)
25
- return nil unless entry.is_a?(Iron::Entry)
26
-
27
- @entries_by_original_gid[gid_string] = entry
28
- rescue ActiveRecord::RecordNotFound
29
- nil
30
- end
31
- end
32
- end
@@ -1,89 +0,0 @@
1
- require "csv"
2
-
3
- module Iron
4
- class ContentImport::Session
5
- attr_reader :errors
6
-
7
- def initialize(archive)
8
- raise ArgumentError, "Missing import archive" unless archive.present?
9
-
10
- @archive = archive
11
- @errors = []
12
- @registry = ContentImport::Registry.new
13
- end
14
-
15
- def run
16
- ActiveRecord::Base.transaction do
17
- prepare_entries
18
- reconstruct_entries
19
-
20
- Result.new(success: true)
21
- end
22
- rescue StandardError => e
23
- warn e.full_message
24
- @errors << "Import failed: #{e.message}"
25
- Result.new(success: false, errors: @errors)
26
- end
27
-
28
- private
29
-
30
- def prepare_entries
31
- content_manifests.each do |content_type, csv_data|
32
- entry_builder = ContentImport::EntryBuilder.new(content_type, @registry)
33
- entry_builder.ensure_entries_exist(csv_data)
34
- end
35
- end
36
-
37
- def reconstruct_entries
38
- content_manifests.each do |content_type, csv_data|
39
- reconstructor = ContentImport::FieldReconstructor.new(content_type, @registry, @archive)
40
- reconstructor.reconstruct_all(csv_data)
41
- end
42
- end
43
-
44
- def content_manifests
45
- @content_manifests ||= build_content_manifests
46
- end
47
-
48
- def build_content_manifests
49
- @archive.file_paths
50
- .select { |path| content_manifest?(path) }
51
- .sort
52
- .filter_map do |path|
53
- content_type = find_content_type(path)
54
-
55
- unless content_type
56
- @errors << "Unknown content type '#{extract_handle(path)}' in archive"
57
- next
58
- end
59
-
60
- [ content_type, @archive[path] ]
61
- end
62
- end
63
-
64
- def content_manifest?(path)
65
- path.start_with?("content/") && path.end_with?(".csv")
66
- end
67
-
68
- def extract_handle(path)
69
- ::File.basename(path, ".csv")
70
- end
71
-
72
- def find_content_type(path)
73
- Iron::ContentType.find_by(handle: extract_handle(path))
74
- end
75
-
76
- class Result
77
- attr_reader :errors
78
-
79
- def initialize(success:, errors: [])
80
- @success = success
81
- @errors = errors
82
- end
83
-
84
- def success?
85
- @success
86
- end
87
- end
88
- end
89
- end
@@ -1,15 +0,0 @@
1
- module Iron
2
- class ContentImport
3
- def self.import(archive)
4
- new(archive).import
5
- end
6
-
7
- def initialize(archive)
8
- @archive = archive
9
- end
10
-
11
- def import
12
- Session.new(@archive).run
13
- end
14
- end
15
- end
@@ -1,30 +0,0 @@
1
- module Iron
2
- module ContentType::Portable
3
- extend ActiveSupport::Concern
4
- include ::Iron::CsvSerializable
5
-
6
- class_methods do
7
- def csv_headers
8
- %w[handle name type description icon title_field_handle web_publishing_enabled web_page_title_field_handle base_path]
9
- end
10
-
11
- def csv_scope
12
- includes(:title_field_definition, :web_page_title_field_definition)
13
- end
14
- end
15
-
16
- def to_csv_row
17
- [
18
- handle,
19
- name,
20
- type.demodulize.underscore,
21
- description,
22
- icon,
23
- title_field_definition&.handle,
24
- web_publishing_enabled,
25
- web_page_title_field_definition&.handle,
26
- base_path
27
- ]
28
- end
29
- end
30
- end
@@ -1,35 +0,0 @@
1
- module Iron
2
- class Entry
3
- module Portable
4
- extend ActiveSupport::Concern
5
-
6
- def to_csv_rows
7
- rows = []
8
-
9
- rows.concat(entry_attribute_rows)
10
-
11
- fields.where(parent_type: nil).includes(:locale, :definition).each do |field|
12
- rows.concat(field.to_csv_rows)
13
- end
14
-
15
- rows
16
- end
17
-
18
- private
19
-
20
- def entry_attribute_rows
21
- rows = []
22
-
23
- rows << csv_row("/created_at", "date", created_at.iso8601)
24
- rows << csv_row("/updated_at", "date", updated_at.iso8601)
25
- rows << csv_row("/route", "text", route) unless route.nil?
26
-
27
- rows
28
- end
29
-
30
- def csv_row(path, type, value)
31
- [ to_gid, nil, path, type, value ]
32
- end
33
- end
34
- end
35
- end
@@ -1,33 +0,0 @@
1
- module Iron
2
- class Field
3
- module Portable
4
- extend ActiveSupport::Concern
5
-
6
- def to_csv_rows
7
- [ csv_row(field_path, csv_type, csv_value) ]
8
- end
9
-
10
- def field_path
11
- if parent.is_a?(Fields::Block)
12
- "#{parent.field_path}/#{definition.handle}"
13
- elsif parent.is_a?(Fields::BlockList)
14
- "#{parent.field_path}/#{rank}"
15
- else
16
- "/fields/#{definition.handle}"
17
- end
18
- end
19
-
20
- def csv_row(path, type, value = nil)
21
- [ entry.to_gid, locale&.code, path, type, value ]
22
- end
23
-
24
- def csv_type
25
- raise NotImplementedError, "#{self.class} must implement csv_type"
26
- end
27
-
28
- def csv_value
29
- raise NotImplementedError, "#{self.class} must implement csv_value"
30
- end
31
- end
32
- end
33
- end
@@ -1,42 +0,0 @@
1
- module Iron
2
- module FieldDefinition::Portable
3
- extend ActiveSupport::Concern
4
- include ::Iron::CsvSerializable
5
-
6
- class_methods do
7
- def csv_headers
8
- %w[parent_type parent_handle handle name type rank metadata supported_block_definitions supported_content_types]
9
- end
10
-
11
- def csv_scope
12
- includes(:schemable)
13
- end
14
- end
15
-
16
- def to_csv_row
17
- [
18
- schemable_type == "Iron::ContentType" ? "content_type" : "block",
19
- schemable.handle,
20
- handle,
21
- name,
22
- type.demodulize.underscore,
23
- rank,
24
- metadata.to_json,
25
- export_supported_block_definitions,
26
- export_supported_content_types
27
- ]
28
- end
29
-
30
- private
31
-
32
- def export_supported_block_definitions
33
- return nil unless respond_to?(:supported_block_definitions)
34
- supported_block_definitions.pluck(:handle).join("|")
35
- end
36
-
37
- def export_supported_content_types
38
- return nil unless respond_to?(:supported_content_types)
39
- supported_content_types.pluck(:handle).join("|")
40
- end
41
- end
42
- end
@@ -1,71 +0,0 @@
1
- require "csv"
2
- require "rubygems/package"
3
- require "stringio"
4
-
5
- module Iron
6
- class SchemaArchive
7
- REQUIRED_FILES = %w[content_types.csv block_definitions.csv field_definitions.csv].freeze
8
-
9
- attr_reader :errors
10
-
11
- def self.from_file(file)
12
- new(file.read)
13
- end
14
-
15
- def initialize(tar_content = nil)
16
- @tar_content = tar_content
17
- @csv_files = {}
18
- @errors = []
19
- extract_files if tar_content
20
- end
21
-
22
- def valid?
23
- return false if @tar_content.nil?
24
-
25
- missing_files = REQUIRED_FILES - @csv_files.keys
26
- if missing_files.any?
27
- @errors << "Missing required files: #{missing_files.join(', ')}"
28
- end
29
-
30
- @errors.empty?
31
- end
32
-
33
- def [](filename)
34
- @csv_files[filename]
35
- end
36
-
37
- def add_file(filename, csv_content)
38
- @csv_files[filename] = csv_content
39
- end
40
-
41
- def to_tar
42
- tarfile = StringIO.new
43
-
44
- Gem::Package::TarWriter.new(tarfile) do |tar|
45
- @csv_files.each do |filename, content|
46
- tar.add_file_simple(filename, 0644, content.bytesize) do |io|
47
- io.write(content)
48
- end
49
- end
50
- end
51
-
52
- tarfile.string
53
- end
54
-
55
- private
56
-
57
- def extract_files
58
- tar_io = StringIO.new(@tar_content)
59
-
60
- Gem::Package::TarReader.new(tar_io) do |tar|
61
- tar.each do |entry|
62
- if entry.file? && entry.full_name.end_with?(".csv")
63
- @csv_files[entry.full_name] = entry.read.force_encoding("UTF-8")
64
- end
65
- end
66
- end
67
- rescue StandardError => e
68
- @errors << "Failed to extract tar archive: #{e.message}"
69
- end
70
- end
71
- end
@@ -1,15 +0,0 @@
1
- module Iron
2
- class SchemaExporter
3
- def self.export
4
- new.export
5
- end
6
-
7
- def export
8
- archive = SchemaArchive.new
9
- archive.add_file("content_types.csv", ContentType.to_csv)
10
- archive.add_file("block_definitions.csv", BlockDefinition.to_csv)
11
- archive.add_file("field_definitions.csv", FieldDefinition.to_csv)
12
- archive.to_tar
13
- end
14
- end
15
- end