omml 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/CLAUDE.md +19 -9
- data/TODO.uniwordfix/00-overview.md +29 -0
- data/TODO.uniwordfix/01-revert-contributor-changes.md +25 -0
- data/TODO.uniwordfix/02-models-base.md +55 -0
- data/TODO.uniwordfix/03-migrate-models.md +38 -0
- data/TODO.uniwordfix/04-autoload-conversion.md +52 -0
- data/TODO.uniwordfix/05-encapsulation-cleanup.md +67 -0
- data/TODO.uniwordfix/06-register-in-api.md +44 -0
- data/TODO.uniwordfix/07-spec-coverage.md +43 -0
- data/TODO.uniwordfix/08-spec-cross-context.md +63 -0
- data/TODO.uniwordfix/09-spec-cleanup.md +37 -0
- data/TODO.uniwordfix/10-claude-md-stale.md +34 -0
- data/lib/omml/configuration/context_registry.rb +36 -2
- data/lib/omml/configuration/model_registry.rb +6 -2
- data/lib/omml/configuration/type_resolver.rb +0 -2
- data/lib/omml/configuration.rb +5 -5
- data/lib/omml/models/base.rb +16 -0
- data/lib/omml/models/ct_acc.rb +1 -3
- data/lib/omml/models/ct_acc_pr.rb +1 -3
- data/lib/omml/models/ct_anchor.rb +1 -3
- data/lib/omml/models/ct_attr.rb +1 -3
- data/lib/omml/models/ct_bar.rb +1 -3
- data/lib/omml/models/ct_bar_pr.rb +1 -3
- data/lib/omml/models/ct_bookmark.rb +0 -2
- data/lib/omml/models/ct_bookmark_range.rb +0 -2
- data/lib/omml/models/ct_border.rb +1 -3
- data/lib/omml/models/ct_border_box.rb +1 -3
- data/lib/omml/models/ct_border_box_pr.rb +1 -3
- data/lib/omml/models/ct_box.rb +1 -3
- data/lib/omml/models/ct_box_pr.rb +1 -3
- data/lib/omml/models/ct_br.rb +1 -3
- data/lib/omml/models/ct_break_bin.rb +1 -3
- data/lib/omml/models/ct_break_bin_sub.rb +1 -3
- data/lib/omml/models/ct_calendar_type.rb +1 -3
- data/lib/omml/models/ct_char.rb +1 -3
- data/lib/omml/models/ct_color.rb +1 -3
- data/lib/omml/models/ct_control.rb +1 -3
- data/lib/omml/models/ct_ctrl_pr.rb +1 -3
- data/lib/omml/models/ct_custom_xml_pr.rb +1 -3
- data/lib/omml/models/ct_custom_xml_run.rb +1 -3
- data/lib/omml/models/ct_d.rb +1 -3
- data/lib/omml/models/ct_d_pr.rb +1 -3
- data/lib/omml/models/ct_data_binding.rb +1 -3
- data/lib/omml/models/ct_decimal_number.rb +1 -3
- data/lib/omml/models/ct_drawing.rb +1 -3
- data/lib/omml/models/ct_east_asian_layout.rb +1 -3
- data/lib/omml/models/ct_effect_extent.rb +1 -3
- data/lib/omml/models/ct_em.rb +1 -3
- data/lib/omml/models/ct_empty.rb +1 -3
- data/lib/omml/models/ct_eq_arr.rb +1 -3
- data/lib/omml/models/ct_eq_arr_pr.rb +1 -3
- data/lib/omml/models/ct_f.rb +1 -3
- data/lib/omml/models/ct_f_pr.rb +1 -3
- data/lib/omml/models/ct_f_type.rb +1 -3
- data/lib/omml/models/ct_ff_check_box.rb +1 -3
- data/lib/omml/models/ct_ff_data.rb +1 -3
- data/lib/omml/models/ct_ff_help_text.rb +1 -3
- data/lib/omml/models/ct_ff_name.rb +1 -3
- data/lib/omml/models/ct_ff_status_text.rb +1 -3
- data/lib/omml/models/ct_ff_text_input.rb +1 -3
- data/lib/omml/models/ct_ff_text_type.rb +1 -3
- data/lib/omml/models/ct_ffdd_list.rb +1 -3
- data/lib/omml/models/ct_fit_text.rb +1 -3
- data/lib/omml/models/ct_fld_char.rb +1 -3
- data/lib/omml/models/ct_fonts.rb +1 -3
- data/lib/omml/models/ct_ftn_edn_ref.rb +1 -3
- data/lib/omml/models/ct_func.rb +1 -3
- data/lib/omml/models/ct_func_pr.rb +1 -3
- data/lib/omml/models/ct_graphical_object.rb +1 -3
- data/lib/omml/models/ct_graphical_object_data.rb +1 -3
- data/lib/omml/models/ct_graphical_object_frame_locking.rb +1 -3
- data/lib/omml/models/ct_group_chr.rb +1 -3
- data/lib/omml/models/ct_group_chr_pr.rb +1 -3
- data/lib/omml/models/ct_highlight.rb +1 -3
- data/lib/omml/models/ct_hps_measure.rb +1 -3
- data/lib/omml/models/ct_hyperlink.rb +1 -3
- data/lib/omml/models/ct_inline.rb +1 -3
- data/lib/omml/models/ct_integer2.rb +1 -3
- data/lib/omml/models/ct_integer255.rb +1 -3
- data/lib/omml/models/ct_lang.rb +1 -3
- data/lib/omml/models/ct_language.rb +1 -3
- data/lib/omml/models/ct_lim_loc.rb +1 -3
- data/lib/omml/models/ct_lim_low.rb +1 -3
- data/lib/omml/models/ct_lim_low_pr.rb +1 -3
- data/lib/omml/models/ct_lim_upp.rb +1 -3
- data/lib/omml/models/ct_lim_upp_pr.rb +1 -3
- data/lib/omml/models/ct_lock.rb +1 -3
- data/lib/omml/models/ct_m.rb +1 -3
- data/lib/omml/models/ct_m_pr.rb +1 -3
- data/lib/omml/models/ct_macro_name.rb +1 -3
- data/lib/omml/models/ct_manual_break.rb +1 -3
- data/lib/omml/models/ct_markup.rb +1 -3
- data/lib/omml/models/ct_markup_range.rb +0 -2
- data/lib/omml/models/ct_math_pr.rb +1 -3
- data/lib/omml/models/ct_mc.rb +1 -3
- data/lib/omml/models/ct_mc_pr.rb +1 -3
- data/lib/omml/models/ct_mcs.rb +1 -3
- data/lib/omml/models/ct_move_bookmark.rb +0 -2
- data/lib/omml/models/ct_mr.rb +1 -3
- data/lib/omml/models/ct_nary.rb +1 -3
- data/lib/omml/models/ct_nary_pr.rb +1 -3
- data/lib/omml/models/ct_non_visual_drawing_props.rb +1 -3
- data/lib/omml/models/ct_non_visual_graphic_frame_properties.rb +1 -3
- data/lib/omml/models/ct_o_math.rb +1 -3
- data/lib/omml/models/ct_o_math_arg.rb +1 -3
- data/lib/omml/models/ct_o_math_arg_pr.rb +1 -3
- data/lib/omml/models/ct_o_math_jc.rb +1 -3
- data/lib/omml/models/ct_o_math_para.rb +1 -3
- data/lib/omml/models/ct_o_math_para_pr.rb +1 -3
- data/lib/omml/models/ct_object.rb +0 -2
- data/lib/omml/models/ct_office_art_extension.rb +1 -3
- data/lib/omml/models/ct_office_art_extension_list.rb +1 -3
- data/lib/omml/models/ct_on_off.rb +1 -3
- data/lib/omml/models/ct_p_tab.rb +1 -3
- data/lib/omml/models/ct_perm.rb +1 -3
- data/lib/omml/models/ct_perm_start.rb +0 -2
- data/lib/omml/models/ct_phant.rb +1 -3
- data/lib/omml/models/ct_phant_pr.rb +1 -3
- data/lib/omml/models/ct_picture.rb +0 -2
- data/lib/omml/models/ct_picture_base.rb +1 -3
- data/lib/omml/models/ct_placeholder.rb +1 -3
- data/lib/omml/models/ct_point2_d.rb +1 -3
- data/lib/omml/models/ct_pos_h.rb +1 -3
- data/lib/omml/models/ct_pos_v.rb +1 -3
- data/lib/omml/models/ct_positive_size2_d.rb +1 -3
- data/lib/omml/models/ct_proof_err.rb +1 -3
- data/lib/omml/models/ct_r.rb +1 -3
- data/lib/omml/models/ct_r_pr.rb +1 -3
- data/lib/omml/models/ct_r_pr_change.rb +0 -2
- data/lib/omml/models/ct_r_pr_original.rb +1 -3
- data/lib/omml/models/ct_rad.rb +1 -3
- data/lib/omml/models/ct_rad_pr.rb +1 -3
- data/lib/omml/models/ct_rel.rb +1 -3
- data/lib/omml/models/ct_rpr.rb +1 -3
- data/lib/omml/models/ct_ruby.rb +1 -3
- data/lib/omml/models/ct_ruby_align.rb +1 -3
- data/lib/omml/models/ct_ruby_content.rb +1 -3
- data/lib/omml/models/ct_ruby_pr.rb +1 -3
- data/lib/omml/models/ct_run_track_change.rb +0 -2
- data/lib/omml/models/ct_s_pre.rb +1 -3
- data/lib/omml/models/ct_s_pre_pr.rb +1 -3
- data/lib/omml/models/ct_s_sub.rb +1 -3
- data/lib/omml/models/ct_s_sub_pr.rb +1 -3
- data/lib/omml/models/ct_s_sub_sup.rb +1 -3
- data/lib/omml/models/ct_s_sub_sup_pr.rb +1 -3
- data/lib/omml/models/ct_s_sup.rb +1 -3
- data/lib/omml/models/ct_s_sup_pr.rb +1 -3
- data/lib/omml/models/ct_script.rb +1 -3
- data/lib/omml/models/ct_sdt_combo_box.rb +1 -3
- data/lib/omml/models/ct_sdt_content_run.rb +1 -3
- data/lib/omml/models/ct_sdt_date.rb +1 -3
- data/lib/omml/models/ct_sdt_date_mapping_type.rb +1 -3
- data/lib/omml/models/ct_sdt_doc_part.rb +1 -3
- data/lib/omml/models/ct_sdt_drop_down_list.rb +1 -3
- data/lib/omml/models/ct_sdt_end_pr.rb +1 -3
- data/lib/omml/models/ct_sdt_list_item.rb +1 -3
- data/lib/omml/models/ct_sdt_pr.rb +1 -3
- data/lib/omml/models/ct_sdt_run.rb +1 -3
- data/lib/omml/models/ct_sdt_text.rb +1 -3
- data/lib/omml/models/ct_shd.rb +1 -3
- data/lib/omml/models/ct_shp.rb +1 -3
- data/lib/omml/models/ct_signed_hps_measure.rb +1 -3
- data/lib/omml/models/ct_signed_twips_measure.rb +1 -3
- data/lib/omml/models/ct_simple_field.rb +1 -3
- data/lib/omml/models/ct_smart_tag_pr.rb +1 -3
- data/lib/omml/models/ct_smart_tag_run.rb +1 -3
- data/lib/omml/models/ct_spacing_rule.rb +1 -3
- data/lib/omml/models/ct_string.rb +1 -3
- data/lib/omml/models/ct_style.rb +1 -3
- data/lib/omml/models/ct_sym.rb +1 -3
- data/lib/omml/models/ct_text.rb +1 -3
- data/lib/omml/models/ct_text_effect.rb +1 -3
- data/lib/omml/models/ct_text_scale.rb +1 -3
- data/lib/omml/models/ct_top_bot.rb +1 -3
- data/lib/omml/models/ct_track_change.rb +0 -2
- data/lib/omml/models/ct_track_change_numbering.rb +0 -2
- data/lib/omml/models/ct_twips_measure.rb +1 -3
- data/lib/omml/models/ct_un_signed_integer.rb +1 -3
- data/lib/omml/models/ct_underline.rb +1 -3
- data/lib/omml/models/ct_vertical_align_run.rb +1 -3
- data/lib/omml/models/ct_wordprocessing_on_off.rb +1 -3
- data/lib/omml/models/ct_wrap_none.rb +1 -3
- data/lib/omml/models/ct_wrap_path.rb +1 -3
- data/lib/omml/models/ct_wrap_square.rb +1 -3
- data/lib/omml/models/ct_wrap_through.rb +1 -3
- data/lib/omml/models/ct_wrap_tight.rb +1 -3
- data/lib/omml/models/ct_wrap_top_bottom.rb +1 -3
- data/lib/omml/models/ct_x_align.rb +1 -3
- data/lib/omml/models/ct_y_align.rb +1 -3
- data/lib/omml/models/groups/eg_content_run_content.rb +1 -3
- data/lib/omml/models/groups/eg_math_content.rb +1 -3
- data/lib/omml/models/groups/eg_o_math_elements.rb +1 -3
- data/lib/omml/models/groups/eg_o_math_math_elements.rb +1 -3
- data/lib/omml/models/groups/eg_office_art_extension_list.rb +1 -3
- data/lib/omml/models/groups/eg_p_content.rb +1 -3
- data/lib/omml/models/groups/eg_r_pr.rb +1 -3
- data/lib/omml/models/groups/eg_r_pr_base.rb +1 -3
- data/lib/omml/models/groups/eg_r_pr_content.rb +1 -3
- data/lib/omml/models/groups/eg_r_pr_math.rb +1 -3
- data/lib/omml/models/groups/eg_range_markup_elements.rb +1 -3
- data/lib/omml/models/groups/eg_ruby_content.rb +1 -3
- data/lib/omml/models/groups/eg_run_inner_content.rb +1 -3
- data/lib/omml/models/groups/eg_run_level_elts.rb +1 -3
- data/lib/omml/models/groups/eg_script_style.rb +1 -3
- data/lib/omml/models/groups/eg_word_r_pr.rb +1 -3
- data/lib/omml/models/groups/eg_wrap_type.rb +1 -3
- data/lib/omml/models/groups.rb +0 -18
- data/lib/omml/models/o_math.rb +1 -3
- data/lib/omml/models/o_math_para.rb +1 -3
- data/lib/omml/models/registerable.rb +22 -0
- data/lib/omml/models/simple_types/enum_string.rb +5 -0
- data/lib/omml/models/simple_types/hex_binary.rb +0 -1
- data/lib/omml/models/simple_types/st_adj_coordinate.rb +2 -1
- data/lib/omml/models/simple_types/st_align_h.rb +0 -2
- data/lib/omml/models/simple_types/st_align_v.rb +0 -2
- data/lib/omml/models/simple_types/st_border.rb +0 -2
- data/lib/omml/models/simple_types/st_br_clear.rb +0 -2
- data/lib/omml/models/simple_types/st_br_type.rb +0 -2
- data/lib/omml/models/simple_types/st_break_bin.rb +0 -2
- data/lib/omml/models/simple_types/st_break_bin_sub.rb +0 -2
- data/lib/omml/models/simple_types/st_calendar_type.rb +0 -2
- data/lib/omml/models/simple_types/st_combine_brackets.rb +0 -2
- data/lib/omml/models/simple_types/st_coordinate.rb +0 -1
- data/lib/omml/models/simple_types/st_displaced_by_custom_xml.rb +0 -2
- data/lib/omml/models/simple_types/st_ed_grp.rb +0 -2
- data/lib/omml/models/simple_types/st_em.rb +0 -2
- data/lib/omml/models/simple_types/st_f_type.rb +0 -2
- data/lib/omml/models/simple_types/st_ff_text_type.rb +0 -2
- data/lib/omml/models/simple_types/st_fld_char_type.rb +0 -2
- data/lib/omml/models/simple_types/st_geom_guide_name.rb +0 -2
- data/lib/omml/models/simple_types/st_hex_color.rb +2 -1
- data/lib/omml/models/simple_types/st_highlight_color.rb +0 -2
- data/lib/omml/models/simple_types/st_hint.rb +0 -2
- data/lib/omml/models/simple_types/st_info_text_type.rb +0 -2
- data/lib/omml/models/simple_types/st_integer2.rb +0 -1
- data/lib/omml/models/simple_types/st_integer255.rb +0 -1
- data/lib/omml/models/simple_types/st_jc.rb +0 -2
- data/lib/omml/models/simple_types/st_lang.rb +2 -1
- data/lib/omml/models/simple_types/st_lim_loc.rb +0 -2
- data/lib/omml/models/simple_types/st_lock.rb +0 -2
- data/lib/omml/models/simple_types/st_on_off.rb +0 -2
- data/lib/omml/models/simple_types/st_p_tab_alignment.rb +0 -2
- data/lib/omml/models/simple_types/st_p_tab_leader.rb +0 -2
- data/lib/omml/models/simple_types/st_p_tab_relative_to.rb +0 -2
- data/lib/omml/models/simple_types/st_positive_coordinate.rb +0 -1
- data/lib/omml/models/simple_types/st_proof_err.rb +0 -2
- data/lib/omml/models/simple_types/st_rel_from_h.rb +0 -2
- data/lib/omml/models/simple_types/st_rel_from_v.rb +0 -2
- data/lib/omml/models/simple_types/st_ruby_align.rb +0 -2
- data/lib/omml/models/simple_types/st_script.rb +0 -2
- data/lib/omml/models/simple_types/st_sdt_date_mapping_type.rb +0 -2
- data/lib/omml/models/simple_types/st_shd.rb +0 -2
- data/lib/omml/models/simple_types/st_shp.rb +0 -2
- data/lib/omml/models/simple_types/st_spacing_rule.rb +0 -1
- data/lib/omml/models/simple_types/st_style.rb +0 -2
- data/lib/omml/models/simple_types/st_text_effect.rb +0 -2
- data/lib/omml/models/simple_types/st_text_scale.rb +0 -1
- data/lib/omml/models/simple_types/st_theme.rb +0 -2
- data/lib/omml/models/simple_types/st_theme_color.rb +0 -2
- data/lib/omml/models/simple_types/st_top_bot.rb +0 -2
- data/lib/omml/models/simple_types/st_underline.rb +0 -2
- data/lib/omml/models/simple_types/st_vertical_align_run.rb +0 -2
- data/lib/omml/models/simple_types/st_wrap_text.rb +0 -2
- data/lib/omml/models/simple_types/st_x_align.rb +0 -2
- data/lib/omml/models/simple_types/st_y_align.rb +0 -2
- data/lib/omml/models/simple_types/token.rb +5 -0
- data/lib/omml/models/simple_types/unsigned_int.rb +0 -1
- data/lib/omml/models/simple_types/unsigned_long.rb +0 -1
- data/lib/omml/models/simple_types.rb +0 -59
- data/lib/omml/models.rb +269 -177
- data/lib/omml/version.rb +1 -1
- metadata +15 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bda4c28c48972ee48e21e79fbec32b5dcdfb63ffb2f3aa503fef7de30e3c4ca9
|
|
4
|
+
data.tar.gz: 8acc0ec6b5465135f64e65e2f9d5ed430efbc5e3fa10f24fd10c4f056f3a3c15
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 18b8ce8eab3d4114104bcf90d87faf2edb8be5d42542ee4d347faca30dd8cc78ff258cd911af62cd05b5a1d0c5e177471a9dc040ae8ff165e8fe7d4a11543c79
|
|
7
|
+
data.tar.gz: 5d5fd0178c24ce73e0b18985604e214e66b84d9b1de01725e50349942065a0a3ff64c7f53a97e36dc6e74659eaeefcf9bd4f9137fed56bc9abdd0863f369f83b
|
data/CLAUDE.md
CHANGED
|
@@ -27,24 +27,28 @@ The top-level API is in `lib/omml.rb`. `Parser` in `lib/omml/parser.rb` handles
|
|
|
27
27
|
|
|
28
28
|
### Type context system (`Omml::Configuration`)
|
|
29
29
|
|
|
30
|
-
`Configuration` manages a Lutaml `GlobalContext` (context ID `:omml`) that holds a registry of all model classes.
|
|
30
|
+
`Configuration` manages a Lutaml `GlobalContext` (context ID `:omml`) that holds a registry of all model classes. The context is populated lazily on first parse via `populate_context!`, which eager-loads every model so its `inherited` hook fires and registers it.
|
|
31
31
|
|
|
32
32
|
Model IDs are derived from class names: prefix `CT`/`EG`/`ST` is stripped and snake-cased with the prefix (e.g., `CTOMath` → `:ct_o_math`).
|
|
33
33
|
|
|
34
|
+
Consumers (e.g. Uniword) register their own context via `Omml::Configuration.register_in(:consumer_id)`. This populates the consumer's type registry directly with all Omml types so OMML subtrees embedded in foreign-context models resolve without falling back through `:omml`. See `spec/omml/cross_context_spec.rb` for the contract.
|
|
35
|
+
|
|
34
36
|
### Model layer (`lib/omml/models/`)
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
All model loading is `autoload`-based. `lib/omml/models.rb` declares every class as an `autoload` on the `Omml::Models` module, plus an `eager_load!` helper that `populate_context!` invokes to trigger class definition (and thus registration).
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
- **Simple types** (`simple_types/*.rb`) — enum/value types like `STJc`, `STOnOff`. These wrap string values with validation.
|
|
40
|
-
- **Group models** (`groups/*.rb`) — shared compositional groups (e.g., `EGOMathElements`). These use `import_model_attributes` / `import_model_mappings` to compose attributes from other groups, implementing the OMML schema's choice/sequence patterns.
|
|
40
|
+
Three categories of models:
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
- **Complex types** (`ct_*.rb`) and root element wrappers (`o_math.rb`, `o_math_para.rb`) — extend `Omml::Models::Base`, which provides:
|
|
43
|
+
- `lutaml_default_register` returning `Omml::Configuration.context_id` (single source of truth), so consumers referencing Omml classes route serialization and parsing back to `:omml`.
|
|
44
|
+
- An `inherited` hook that auto-registers each subclass with `Omml::Configuration`. No per-file `register_model` call is needed.
|
|
45
|
+
- **Simple types** (`simple_types/*.rb`) — enum/value types like `STJc`, `STOnOff`. Most extend `EnumString` or `Token`, both of which carry an `inherited` hook for auto-registration. A handful extend Lutaml types directly (`Lutaml::Model::Type::Integer`, `::Decimal`, `::Value`) and are registered explicitly. Three classes (`STAdjCoordinate`, `STHexColor`, `STLang`) include `Omml::Models::Registerable` because they resolve dependent types via a class-level register accessor.
|
|
46
|
+
- **Group models** (`groups/*.rb`) — shared compositional groups (e.g., `EGOMathElements`). These use `import_model_attributes` / `import_model_mappings` to compose attributes from other groups, implementing the OMML schema's choice/sequence patterns. Like complex types, they extend `Base`.
|
|
43
47
|
|
|
44
48
|
### Namespaces
|
|
45
49
|
|
|
46
|
-
- `Omml::Namespace` — the OMML namespace (`http://schemas.openxmlformats.org/officeDocument/2006/math`, prefix `m`)
|
|
47
|
-
- `Omml::WordprocessingNamespace` — the Word ML namespace (prefix `w`), used for elements like `w:rPr` that appear inside OMML
|
|
50
|
+
- `Omml::Namespace` — the OMML namespace (`http://schemas.openxmlformats.org/officeDocument/2006/math`, prefix `m`). This is the canonical OMML namespace class; downstream consumers (Uniword, Plurimath) should reference it directly rather than redefine their own.
|
|
51
|
+
- `Omml::WordprocessingNamespace` — the Word ML namespace (prefix `w`), used for elements like `w:rPr` that appear inside OMML.
|
|
48
52
|
|
|
49
53
|
### Type substitutions (`TypeSubstitutions`)
|
|
50
54
|
|
|
@@ -53,12 +57,18 @@ Many OMML simple types are aliases for built-in Lutaml types (e.g., `STString`
|
|
|
53
57
|
## Testing
|
|
54
58
|
|
|
55
59
|
- `spec/omml_spec.rb` — unit tests for parsing, context management, and error handling
|
|
56
|
-
- `spec/
|
|
60
|
+
- `spec/omml_fixture_round_trip_spec.rb` — parses 279 OMML fixture files from `spec/fixtures/omml/`, round-trips them through parse → serialize, and compares the XML trees structurally (not string-equality). This is the primary correctness check.
|
|
57
61
|
- The round-trip spec expects exact XML structural fidelity: same elements, attributes, namespaces, text content, and child ordering.
|
|
62
|
+
- `spec/omml/lutaml_default_register_spec.rb` — asserts every `Serializable` subclass under `Omml::Models` carries `:omml` as its default register (mirrors the Mml gem's coverage spec).
|
|
63
|
+
- `spec/omml/cross_context_spec.rb` — proves embedded OMML parses correctly when a foreign-context parent references Omml types (the Uniword scenario).
|
|
64
|
+
- `spec/omml/opal_boot_spec.rb` — verifies the Opal boot file stays in sync with autoload declarations.
|
|
58
65
|
|
|
59
66
|
## Key conventions
|
|
60
67
|
|
|
61
68
|
- All models are in the `Omml::Models` module namespace
|
|
69
|
+
- New complex types or groups inherit `Omml::Models::Base`; new enum-like simple types inherit `EnumString` or `Token`. No `register_model` call is needed — `inherited` handles it.
|
|
70
|
+
- All internal library loading uses Ruby `autoload`, never `require_relative`
|
|
71
|
+
- No `instance_variable_set` / `instance_variable_get` / `send(:private_method)` / `respond_to?` — use public APIs and structural type checks (`is_a?`, `<`, `include?`)
|
|
62
72
|
- The XML adapter is resolved by lutaml-model's AdapterResolver (lazy auto-detection)
|
|
63
73
|
- Models use `ordered` and `sequence` blocks for XML mapping to preserve element order
|
|
64
74
|
- `render_empty: true` is used on elements that must be emitted even when empty
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Uniwordfix — Plan & Overview
|
|
2
|
+
|
|
3
|
+
Goal: make `omml` cleanly consumable by Uniword (and any other OOXML
|
|
4
|
+
consumer). No redefinitions, no imperative mutation, no encapsulation
|
|
5
|
+
violations. Fully model-driven, declarative, autoload-based.
|
|
6
|
+
|
|
7
|
+
## Sequence
|
|
8
|
+
|
|
9
|
+
| # | File | Subject |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| 01 | revert-contributor-changes.md | Drop the two unstaged diffs that broke specs / used runtime hooks |
|
|
12
|
+
| 02 | models-base.md | Introduce `Omml::Models::Base` with declarative `lutaml_default_register` and `inherited` auto-registration |
|
|
13
|
+
| 03 | migrate-models.md | Change every model class to inherit `Base`; remove side-effect `register_model` calls |
|
|
14
|
+
| 04 | autoload-conversion.md | Replace `require_relative` chains with `autoload` declarations; add `eager_load!` for context populate |
|
|
15
|
+
| 05 | encapsulation-cleanup.md | Eliminate `instance_variable_set`, `respond_to?`, `send` from library and specs |
|
|
16
|
+
| 06 | register-in-api.md | Public `Omml::Configuration.register_in(consumer_id)` API for consumers |
|
|
17
|
+
| 07 | spec-coverage.md | `lutaml_default_register` coverage spec (mirrors Mml pattern) |
|
|
18
|
+
| 08 | spec-cross-context.md | Cross-context integration spec (embedded Omml in foreign parent) |
|
|
19
|
+
| 09 | spec-cleanup.md | Replace private-API reaching in existing specs with public API |
|
|
20
|
+
| 10 | claude-md-stale.md | Remove `CommonCode::RootModel` reference; document the new architecture |
|
|
21
|
+
|
|
22
|
+
## Principles enforced
|
|
23
|
+
|
|
24
|
+
- Single source of truth for register context (`Omml::Configuration.context_id`)
|
|
25
|
+
- Declarative class-level tagging (no `define_singleton_method`)
|
|
26
|
+
- Auto-registration via `inherited` hook (no per-file `register_model` calls)
|
|
27
|
+
- Autoload everywhere; no `require_relative` for internal library code
|
|
28
|
+
- Public API for cross-cutting concerns; no reaching into internals
|
|
29
|
+
- Real model instances in specs; no doubles; no `send`/`instance_variable_get`
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# 01 — Revert contributor's two unstaged changes
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
Two unstaged edits on `feat/opal-boot-and-workflow` need to go:
|
|
6
|
+
|
|
7
|
+
1. `lib/omml/namespace.rb` — added `element_form_default :qualified`
|
|
8
|
+
2. `lib/omml/configuration/model_registry.rb` — added `define_singleton_method(:lutaml_default_register)` runtime hook
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
- The namespace change breaks 86 of 306 round-trip specs. Real-world OMML
|
|
13
|
+
documents mix prefixed and unqualified locals (`<br/>` inside `<m:r>`).
|
|
14
|
+
Forcing `:qualified` emits `<m:br/>` and breaks fidelity.
|
|
15
|
+
- The runtime hook is imperative mutation that bypasses lutaml-model's
|
|
16
|
+
intended inheritance pattern. It hardcodes `:omml` instead of using
|
|
17
|
+
`context_id`. Replaced declaratively in step 02.
|
|
18
|
+
|
|
19
|
+
## How
|
|
20
|
+
|
|
21
|
+
`git checkout -- lib/omml/namespace.rb lib/omml/configuration/model_registry.rb`
|
|
22
|
+
|
|
23
|
+
## Verification
|
|
24
|
+
|
|
25
|
+
`bundle exec rspec` returns to 306 examples, 0 failures.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# 02 — Introduce `Omml::Models::Base`
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
New file `lib/omml/models/base.rb`:
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
module Omml
|
|
9
|
+
module Models
|
|
10
|
+
class Base < Lutaml::Model::Serializable
|
|
11
|
+
def self.inherited(subclass)
|
|
12
|
+
super
|
|
13
|
+
Omml::Configuration.register_model(subclass)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.lutaml_default_register
|
|
17
|
+
Omml::Configuration.context_id
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`EnumString` also gains an `inherited` hook so `ST*` types auto-register:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
class EnumString < Lutaml::Model::Type::String
|
|
28
|
+
def self.inherited(subclass)
|
|
29
|
+
super
|
|
30
|
+
Omml::Configuration.register_model(subclass)
|
|
31
|
+
end
|
|
32
|
+
# ...
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Why
|
|
37
|
+
|
|
38
|
+
- Declarative: each model class self-documents via inheritance
|
|
39
|
+
- Single source of truth: `context_id` referenced, not hardcoded `:omml`
|
|
40
|
+
- OCP: adding a new model = adding a file, no other code changes
|
|
41
|
+
- DRY: no per-file `register_model` boilerplate
|
|
42
|
+
- Matches Mml's proven pattern (with one improvement: auto-registration
|
|
43
|
+
via `inherited` removes Mml's centralized registration block)
|
|
44
|
+
|
|
45
|
+
## Note on `lutaml_default_register` for `EnumString`
|
|
46
|
+
|
|
47
|
+
Upstream's `Register.resolve_for_child` only consults `lutaml_default_register`
|
|
48
|
+
for `Serializable` subclasses (register.rb:98-100). `EnumString` extends
|
|
49
|
+
`Type::String`, so the method is never called on it. No need to declare it.
|
|
50
|
+
|
|
51
|
+
## Verification
|
|
52
|
+
|
|
53
|
+
- `Omml::Models::CTAcc.lutaml_default_register == :omml`
|
|
54
|
+
- `Omml::Models::STJc` auto-registers without explicit `register_model` call
|
|
55
|
+
- `Omml::Models::CTBookmark < CTBookmarkRange` — auto-registers through chain
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 03 — Migrate every model to inherit `Base`
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
For every model file under `lib/omml/models/`:
|
|
6
|
+
|
|
7
|
+
1. Change `< Lutaml::Model::Serializable` to `< Base` (or `< Omml::Models::Base`)
|
|
8
|
+
2. Delete the trailing `Omml::Configuration.register_model(...)` call
|
|
9
|
+
3. Delete the `id:` parameter where present (derivation handles it)
|
|
10
|
+
|
|
11
|
+
For `EnumString` descendants (`ST*`):
|
|
12
|
+
1. Inheritance unchanged (`< EnumString`)
|
|
13
|
+
2. Delete the trailing `Omml::Configuration.register_model(...)` call
|
|
14
|
+
|
|
15
|
+
For root wrappers `OMath` and `OMathPara`:
|
|
16
|
+
1. Change to `< Base`
|
|
17
|
+
2. Delete `Omml::Configuration.register_model(OMath, id: :o_math)` — derivation produces `:o_math` from "OMath" via snake_case, so explicit ID is redundant. Same for `OMathPara`.
|
|
18
|
+
|
|
19
|
+
## Why
|
|
20
|
+
|
|
21
|
+
- Removes ~250 lines of boilerplate registration calls
|
|
22
|
+
- Single registration path via `Base.inherited` / `EnumString.inherited`
|
|
23
|
+
- Adding a new model = adding a file with `class CTNew < Base` — done
|
|
24
|
+
|
|
25
|
+
## How
|
|
26
|
+
|
|
27
|
+
Script-based mechanical edit across 249 files. Manual inspection of:
|
|
28
|
+
- Files with inheritance from another model (`CTBookmarkRange < CTMarkupRange`)
|
|
29
|
+
— these propagate `inherited` through the chain automatically
|
|
30
|
+
- Files with `EnumString` parent (ST* types)
|
|
31
|
+
- Root wrappers `OMath`, `OMathPara`
|
|
32
|
+
|
|
33
|
+
## Verification
|
|
34
|
+
|
|
35
|
+
After migration:
|
|
36
|
+
- `Omml::Configuration.registered_models.keys.size` == 249 (same as before)
|
|
37
|
+
- Full spec suite passes (306 examples)
|
|
38
|
+
- Round-trip corpus unaffected
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# 04 — Convert `require_relative` chains to `autoload`
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
Three files use `require_relative` for internal library code:
|
|
6
|
+
|
|
7
|
+
- `lib/omml/models.rb` (176 declarations)
|
|
8
|
+
- `lib/omml/models/simple_types.rb` (57 declarations)
|
|
9
|
+
- `lib/omml/models/groups.rb` (16 declarations)
|
|
10
|
+
|
|
11
|
+
Convert each to `autoload` declarations in the parent namespace's file.
|
|
12
|
+
|
|
13
|
+
Pattern:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
# lib/omml/models.rb
|
|
17
|
+
module Omml
|
|
18
|
+
module Models
|
|
19
|
+
autoload :Base, "omml/models/base"
|
|
20
|
+
autoload :EnumString, "omml/models/simple_types/enum_string"
|
|
21
|
+
autoload :CTAcc, "omml/models/ct_acc"
|
|
22
|
+
# ...
|
|
23
|
+
|
|
24
|
+
def self.eager_load!
|
|
25
|
+
constants.each { |name| const_get(name) }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`populate_context!` calls `Omml::Models.eager_load!` to trigger every
|
|
32
|
+
file load → class definition → auto-registration via `inherited`.
|
|
33
|
+
|
|
34
|
+
## Why
|
|
35
|
+
|
|
36
|
+
- Per global rule: "NEVER use `require_relative` for internal library code"
|
|
37
|
+
- Lazy loading: programmatic consumers can use individual models without
|
|
38
|
+
pulling in the entire 250-file tree
|
|
39
|
+
- Cleaner load path
|
|
40
|
+
- Removes Opal boot file's special-casing of `models.rb`
|
|
41
|
+
|
|
42
|
+
## Sub-module structure
|
|
43
|
+
|
|
44
|
+
- `lib/omml/models/simple_types.rb` becomes the parent for `ST*` autoloads
|
|
45
|
+
- `lib/omml/models/groups.rb` becomes the parent for `EG*` autoloads
|
|
46
|
+
- Each is itself autoloaded from `lib/omml/models.rb`
|
|
47
|
+
|
|
48
|
+
## Verification
|
|
49
|
+
|
|
50
|
+
- `bundle exec rspec` — all 306 examples pass
|
|
51
|
+
- `bundle exec ruby -e "require 'omml'; Omml::Models::CTAcc"` — lazy loads single file
|
|
52
|
+
- `Omml::Configuration.populate_context!` triggers full registration
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# 05 — Encapsulation cleanup
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
Remove forbidden patterns from library and spec code:
|
|
6
|
+
|
|
7
|
+
### `lib/omml/configuration/model_registry.rb`
|
|
8
|
+
|
|
9
|
+
Current:
|
|
10
|
+
```ruby
|
|
11
|
+
def clear_cache(klass, register_id)
|
|
12
|
+
klass.instance_variable_set(:@register, register_id)
|
|
13
|
+
klass.clear_cache(register_id) if klass.respond_to?(:clear_cache)
|
|
14
|
+
end
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Problem: `instance_variable_set` bypasses upstream's `set_register_context`
|
|
18
|
+
public method. `respond_to?` is duck typing.
|
|
19
|
+
|
|
20
|
+
Replace with: use upstream's public API. `Lutaml::Model::Serializable` has
|
|
21
|
+
`set_register_context(register_id)` which sets `@register` with a guard
|
|
22
|
+
against double-assignment. Call it directly. Remove the `respond_to?` check
|
|
23
|
+
since `clear_cache` is always defined on `Serializable` subclasses.
|
|
24
|
+
|
|
25
|
+
### `lib/omml/configuration/type_resolver.rb`
|
|
26
|
+
|
|
27
|
+
Current:
|
|
28
|
+
```ruby
|
|
29
|
+
def root_namespace_uri(root)
|
|
30
|
+
return unless root.respond_to?(:namespace)
|
|
31
|
+
root.namespace&.uri
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Problem: `respond_to?` for duck typing on a parsed XML document.
|
|
36
|
+
|
|
37
|
+
Replace with: check the document adapter's actual type. The parsed document
|
|
38
|
+
root is either an `Ox::Element`, `Nokogiri::XML::Element`, or
|
|
39
|
+
`Moxml::Element` depending on adapter. Rather than branching on adapter,
|
|
40
|
+
trust the upstream contract: the root returned by `xml_adapter.parse` always
|
|
41
|
+
responds to `namespace`. If it doesn't, that's an upstream bug — let it
|
|
42
|
+
NoMethodError naturally.
|
|
43
|
+
|
|
44
|
+
If a nil-check is genuinely needed for the test scenario at
|
|
45
|
+
`spec/omml_spec.rb:122` (Struct-based fake), restructure the test to use a
|
|
46
|
+
real adapter output instead.
|
|
47
|
+
|
|
48
|
+
### `spec/omml_spec.rb`
|
|
49
|
+
|
|
50
|
+
Current lines 301-320 use:
|
|
51
|
+
- `Omml::Configuration.send(:register_models_in, custom_context)` — private send
|
|
52
|
+
- `Omml::Models::OMath.instance_variable_get(:@register)` — IVAR get
|
|
53
|
+
|
|
54
|
+
Replace with: exercise the public API. Either expose a public accessor for
|
|
55
|
+
the register a class is bound to, or assert via behavior (parse and verify
|
|
56
|
+
the round-trip works in the custom context).
|
|
57
|
+
|
|
58
|
+
## Why
|
|
59
|
+
|
|
60
|
+
- Global rule: "NEVER use `send` to call private methods"
|
|
61
|
+
- Global rule: "NEVER use `instance_variable_set` or `instance_variable_get`"
|
|
62
|
+
- Global rule: "NEVER use `respond_to?` for type checking"
|
|
63
|
+
|
|
64
|
+
## Verification
|
|
65
|
+
|
|
66
|
+
- `grep -rn "instance_variable_set\|instance_variable_get\|\.send(:\|respond_to?" lib/` returns empty (excluding legitimately-needed external API calls)
|
|
67
|
+
- All specs pass
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 06 — `Omml::Configuration.register_in` public API
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
Add to `lib/omml/configuration/context_registry.rb`:
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
def register_in(consumer_id, fallback_to: nil)
|
|
9
|
+
populate_context!
|
|
10
|
+
Lutaml::Model::GlobalContext.create_context(
|
|
11
|
+
id: consumer_id.to_sym,
|
|
12
|
+
registry: Lutaml::Model::TypeRegistry.new,
|
|
13
|
+
fallback_to: Array(fallback_to || [:default, context_id]),
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Why
|
|
19
|
+
|
|
20
|
+
Consumers (Uniword) currently need to know:
|
|
21
|
+
- That `populate_context!` must run first
|
|
22
|
+
- The `Lutaml::Model::GlobalContext.create_context` API
|
|
23
|
+
- The right fallback chain (`[:default, :omml]`)
|
|
24
|
+
|
|
25
|
+
That's too much internal knowledge. `register_in(:uniword)` does it all.
|
|
26
|
+
|
|
27
|
+
The fallback chain `[:default, context_id]` lets consumer types resolve via
|
|
28
|
+
`:default` first (so consumer-defined types take precedence), then Omml
|
|
29
|
+
types via `:omml`. Order is overridable for advanced consumers.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
# In Uniword boot
|
|
35
|
+
Omml::Configuration.register_in(:uniword)
|
|
36
|
+
|
|
37
|
+
# Then parse
|
|
38
|
+
DocumentRoot.from_xml(xml, register: :uniword)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Verification
|
|
42
|
+
|
|
43
|
+
- New spec: register a `:test_consumer` context, parse embedded OMML,
|
|
44
|
+
assert resolution works end-to-end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 07 — `lutaml_default_register` coverage spec
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
`spec/omml/lutaml_default_register_spec.rb` — mirrors Mml's coverage spec.
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
RSpec.describe "Omml::Models lutaml_default_register coverage" do
|
|
9
|
+
before { Omml::Configuration.populate_context! }
|
|
10
|
+
|
|
11
|
+
it "every Serializable subclass returns :omml" do
|
|
12
|
+
classes = Omml::Models.constants
|
|
13
|
+
.map { |c| Omml::Models.const_get(c) }
|
|
14
|
+
.select { |c| c.is_a?(Class) && c < Lutaml::Model::Serializable }
|
|
15
|
+
|
|
16
|
+
expect(classes).not_to be_empty
|
|
17
|
+
|
|
18
|
+
wrong = classes.reject { |c| c.lutaml_default_register == :omml }
|
|
19
|
+
expect(wrong).to be_empty,
|
|
20
|
+
"Classes with wrong default register: " \
|
|
21
|
+
"#{wrong.map { |c| "#{c.name}=#{c.lutaml_default_register}" }.join(', ')}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "includes inherited subclasses (CTBookmark via CTBookmarkRange)" do
|
|
25
|
+
expect(Omml::Models::CTBookmark.lutaml_default_register).to eq(:omml)
|
|
26
|
+
expect(Omml::Models::CTBookmarkRange.lutaml_default_register).to eq(:omml)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "includes simple types via EnumString" do
|
|
30
|
+
expect(Omml::Models::STJc.lutaml_default_register).to eq(:omml)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Why
|
|
36
|
+
|
|
37
|
+
- Catches any future model class that forgets to inherit `Base`
|
|
38
|
+
- Documents the contract: every Omml model carries `:omml`
|
|
39
|
+
- Mirrors Mml's proven pattern
|
|
40
|
+
|
|
41
|
+
## Verification
|
|
42
|
+
|
|
43
|
+
All three examples pass.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 08 — Cross-context integration spec
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
`spec/omml/cross_context_spec.rb` — proves embedded Omml-in-foreign-parent
|
|
6
|
+
works end-to-end via `register_in`.
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
RSpec.describe "cross-context embedding (Uniword scenario)" do
|
|
10
|
+
before do
|
|
11
|
+
Omml::Configuration.register_in(:test_consumer)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
after do
|
|
15
|
+
Omml::Configuration.reset_context!(id: :test_consumer)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
let(:consumer_class) do
|
|
19
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
20
|
+
attribute :o_math, "Omml::Models::CTOMath", collection: true
|
|
21
|
+
|
|
22
|
+
xml do
|
|
23
|
+
element "container"
|
|
24
|
+
map_element "oMath", to: :o_math
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
let(:xml) do
|
|
30
|
+
<<~XML
|
|
31
|
+
<container xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">
|
|
32
|
+
<m:oMath>
|
|
33
|
+
<m:r><m:t>x</m:t></m:r>
|
|
34
|
+
</m:oMath>
|
|
35
|
+
</container>
|
|
36
|
+
XML
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "parses Omml embedded in a foreign-context parent" do
|
|
40
|
+
parsed = consumer_class.from_xml(xml, register: :test_consumer)
|
|
41
|
+
expect(parsed.o_math.first.r.first.t.first.content).to eq("x")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "round-trips through the consumer context" do
|
|
45
|
+
parsed = consumer_class.from_xml(xml, register: :test_consumer)
|
|
46
|
+
serialized = parsed.to_xml(register: :test_consumer)
|
|
47
|
+
expect(serialized).to include("<m:t>x</m:t>")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Why
|
|
53
|
+
|
|
54
|
+
- This is THE spec that proves the cross-context contract works
|
|
55
|
+
- Without it, the contributor's earlier claim was unverified
|
|
56
|
+
- Reproduces the actual Uniword scenario (Paragraph referencing Omml types)
|
|
57
|
+
|
|
58
|
+
## Known limitation
|
|
59
|
+
|
|
60
|
+
If `lutaml-model` 0.8.0 (currently pinned via Gemfile github override) doesn't
|
|
61
|
+
fully consult `lutaml_default_register` during parsing, this spec may fail
|
|
62
|
+
with "Attribute 'acc' not found". That's an upstream bug; pin to 0.8.17 or
|
|
63
|
+
file an issue upstream. Document the expected behavior in the spec comment.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# 09 — Spec cleanup
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
`spec/omml_spec.rb` line 301-320 — "registers schema models against the
|
|
6
|
+
context being populated" — uses:
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
Omml::Configuration.send(:register_models_in, custom_context)
|
|
10
|
+
Omml::Models::OMath.instance_variable_get(:@register)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Both violate global rules (private send, IVAR get).
|
|
14
|
+
|
|
15
|
+
## Fix
|
|
16
|
+
|
|
17
|
+
Restructure the spec to exercise public API:
|
|
18
|
+
|
|
19
|
+
1. Drop the private `register_models_in` call — instead use the public
|
|
20
|
+
`register_in(custom_context_id)` API (new from TODO 06). It performs the
|
|
21
|
+
same registration via the public path.
|
|
22
|
+
2. Drop the `instance_variable_get(:@register)` — instead assert via
|
|
23
|
+
behavior: parse a document in the custom context and verify the OMML
|
|
24
|
+
types resolve.
|
|
25
|
+
|
|
26
|
+
Alternatively, expose a public reader if behavior-based testing is
|
|
27
|
+
insufficient. Prefer behavior-based.
|
|
28
|
+
|
|
29
|
+
## Why
|
|
30
|
+
|
|
31
|
+
- Private methods are private for a reason
|
|
32
|
+
- IVAR access breaks encapsulation
|
|
33
|
+
- Tests should verify behavior, not implementation
|
|
34
|
+
|
|
35
|
+
## Verification
|
|
36
|
+
|
|
37
|
+
Updated spec passes without using `send` or `instance_variable_get`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 10 — Update stale CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
`CLAUDE.md` currently references:
|
|
6
|
+
|
|
7
|
+
> Root element wrappers (`o_math.rb`, `o_math_para.rb`) extend
|
|
8
|
+
> `CommonCode::RootModel` and use `omml_root_element` to declare themselves
|
|
9
|
+
> as XML root elements with the OMML namespace.
|
|
10
|
+
|
|
11
|
+
No `CommonCode::RootModel` module or `omml_root_element` method exists in
|
|
12
|
+
the codebase. The wrappers extend `Lutaml::Model::Serializable` directly
|
|
13
|
+
(will become `Omml::Models::Base` after TODO 02).
|
|
14
|
+
|
|
15
|
+
## Fix
|
|
16
|
+
|
|
17
|
+
Update the CLAUDE.md "Model layer" section to reflect:
|
|
18
|
+
|
|
19
|
+
- All Omml model classes (CT*, ST*, EG*, root wrappers) inherit from
|
|
20
|
+
`Omml::Models::Base`
|
|
21
|
+
- `Base` declares `lutaml_default_register :omml` declaratively
|
|
22
|
+
- `Base.inherited` auto-registers each subclass with `Omml::Configuration`
|
|
23
|
+
- No per-file `register_model` call is needed
|
|
24
|
+
|
|
25
|
+
Also document:
|
|
26
|
+
|
|
27
|
+
- The `Omml::Configuration.register_in(consumer_id)` public API
|
|
28
|
+
- The autoload-based loading in `lib/omml/models.rb`
|
|
29
|
+
- The cross-context contract (proven by `spec/omml/cross_context_spec.rb`)
|
|
30
|
+
|
|
31
|
+
## Why
|
|
32
|
+
|
|
33
|
+
Stale docs mislead future contributors. CLAUDE.md is the entry point for
|
|
34
|
+
AI-assisted work on this repo — accuracy matters.
|
|
@@ -23,8 +23,9 @@ module Omml
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def populate_context!
|
|
26
|
-
#
|
|
27
|
-
|
|
26
|
+
# Eager-load every model so its `inherited` hook fires and the class
|
|
27
|
+
# is registered before we replay registrations into the new context.
|
|
28
|
+
Omml::Models.eager_load!
|
|
28
29
|
reset_context!
|
|
29
30
|
register_models_in(populate_base_context)
|
|
30
31
|
end
|
|
@@ -37,6 +38,39 @@ module Omml
|
|
|
37
38
|
raise Omml::Errors::MissingContextError, normalized_id
|
|
38
39
|
end
|
|
39
40
|
|
|
41
|
+
# Register Omml's models into a consumer-owned context.
|
|
42
|
+
#
|
|
43
|
+
# Consumers (e.g. Uniword) call this once during boot to make every
|
|
44
|
+
# OMML schema type resolvable from the consumer's own context. The
|
|
45
|
+
# consumer's registry is populated directly (via register_models_in)
|
|
46
|
+
# rather than via fallback, so the Omml register does not need to be
|
|
47
|
+
# in the chain — the consumer becomes self-sufficient for OMML types.
|
|
48
|
+
#
|
|
49
|
+
# The default fallback is `[:default]` so consumer-defined types
|
|
50
|
+
# still resolve through lutaml-model's built-in type set when not
|
|
51
|
+
# shadowed by an Omml registration.
|
|
52
|
+
#
|
|
53
|
+
# @example Uniword boot
|
|
54
|
+
# Omml::Configuration.register_in(:uniword)
|
|
55
|
+
# Uniword::DocumentRoot.from_xml(xml, register: :uniword)
|
|
56
|
+
#
|
|
57
|
+
# @param consumer_id [Symbol, String] The consumer's context id.
|
|
58
|
+
# @param fallback_to [Array<Symbol>, nil] Override fallback chain.
|
|
59
|
+
# Defaults to `[:default]`.
|
|
60
|
+
# @return [Lutaml::Model::Context] The created context.
|
|
61
|
+
def register_in(consumer_id, fallback_to: nil)
|
|
62
|
+
normalized_id = normalize_context_id(consumer_id)
|
|
63
|
+
chain = Array(fallback_to || [:default])
|
|
64
|
+
populate_context!
|
|
65
|
+
create_context(
|
|
66
|
+
id: normalized_id,
|
|
67
|
+
registry: Lutaml::Model::TypeRegistry.new,
|
|
68
|
+
fallback_to: chain,
|
|
69
|
+
).tap do |type_context|
|
|
70
|
+
register_models_in(type_context)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
40
74
|
def reset_context!(id: context_id)
|
|
41
75
|
normalized_id = normalize_context_id(id)
|
|
42
76
|
existing_context = Lutaml::Model::GlobalContext.context(normalized_id)
|
|
@@ -52,8 +52,12 @@ module Omml
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def clear_cache(klass, register_id)
|
|
55
|
-
klass.
|
|
56
|
-
|
|
55
|
+
if klass.is_a?(Class) && klass < Lutaml::Model::Serializable
|
|
56
|
+
klass.set_register_context(register_id)
|
|
57
|
+
klass.clear_cache(register_id)
|
|
58
|
+
elsif klass.include?(Omml::Models::Registerable)
|
|
59
|
+
klass.register = register_id
|
|
60
|
+
end
|
|
57
61
|
end
|
|
58
62
|
end
|
|
59
63
|
end
|
data/lib/omml/configuration.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "configuration/adapter"
|
|
4
|
-
require_relative "configuration/context_registry"
|
|
5
|
-
require_relative "configuration/model_registry"
|
|
6
|
-
require_relative "configuration/type_resolver"
|
|
7
|
-
|
|
8
3
|
module Omml
|
|
9
4
|
module Configuration
|
|
5
|
+
autoload :Adapter, "omml/configuration/adapter"
|
|
6
|
+
autoload :ContextRegistry, "omml/configuration/context_registry"
|
|
7
|
+
autoload :ModelRegistry, "omml/configuration/model_registry"
|
|
8
|
+
autoload :TypeResolver, "omml/configuration/type_resolver"
|
|
9
|
+
|
|
10
10
|
extend Adapter
|
|
11
11
|
extend ContextRegistry
|
|
12
12
|
extend ModelRegistry
|