omml 0.2.1 → 0.2.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.
Files changed (282) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop_todo.yml +12 -6
  4. data/CLAUDE.md +19 -9
  5. data/Gemfile +0 -3
  6. data/TODO.uniwordfix/00-overview.md +29 -0
  7. data/TODO.uniwordfix/01-revert-contributor-changes.md +25 -0
  8. data/TODO.uniwordfix/02-models-base.md +55 -0
  9. data/TODO.uniwordfix/03-migrate-models.md +38 -0
  10. data/TODO.uniwordfix/04-autoload-conversion.md +52 -0
  11. data/TODO.uniwordfix/05-encapsulation-cleanup.md +67 -0
  12. data/TODO.uniwordfix/06-register-in-api.md +44 -0
  13. data/TODO.uniwordfix/07-spec-coverage.md +43 -0
  14. data/TODO.uniwordfix/08-spec-cross-context.md +63 -0
  15. data/TODO.uniwordfix/09-spec-cleanup.md +37 -0
  16. data/TODO.uniwordfix/10-claude-md-stale.md +34 -0
  17. data/lib/omml/configuration/context_registry.rb +36 -2
  18. data/lib/omml/configuration/model_registry.rb +6 -2
  19. data/lib/omml/configuration/type_resolver.rb +0 -2
  20. data/lib/omml/configuration.rb +5 -5
  21. data/lib/omml/models/base.rb +16 -0
  22. data/lib/omml/models/ct_acc.rb +1 -3
  23. data/lib/omml/models/ct_acc_pr.rb +1 -3
  24. data/lib/omml/models/ct_anchor.rb +1 -3
  25. data/lib/omml/models/ct_attr.rb +1 -3
  26. data/lib/omml/models/ct_bar.rb +1 -3
  27. data/lib/omml/models/ct_bar_pr.rb +1 -3
  28. data/lib/omml/models/ct_bookmark.rb +0 -2
  29. data/lib/omml/models/ct_bookmark_range.rb +0 -2
  30. data/lib/omml/models/ct_border.rb +1 -3
  31. data/lib/omml/models/ct_border_box.rb +1 -3
  32. data/lib/omml/models/ct_border_box_pr.rb +1 -3
  33. data/lib/omml/models/ct_box.rb +1 -3
  34. data/lib/omml/models/ct_box_pr.rb +1 -3
  35. data/lib/omml/models/ct_br.rb +2 -4
  36. data/lib/omml/models/ct_break_bin.rb +1 -3
  37. data/lib/omml/models/ct_break_bin_sub.rb +1 -3
  38. data/lib/omml/models/ct_calendar_type.rb +1 -3
  39. data/lib/omml/models/ct_char.rb +1 -3
  40. data/lib/omml/models/ct_color.rb +1 -3
  41. data/lib/omml/models/ct_control.rb +1 -3
  42. data/lib/omml/models/ct_ctrl_pr.rb +1 -3
  43. data/lib/omml/models/ct_custom_xml_pr.rb +1 -3
  44. data/lib/omml/models/ct_custom_xml_run.rb +1 -3
  45. data/lib/omml/models/ct_d.rb +1 -3
  46. data/lib/omml/models/ct_d_pr.rb +1 -3
  47. data/lib/omml/models/ct_data_binding.rb +1 -3
  48. data/lib/omml/models/ct_decimal_number.rb +1 -3
  49. data/lib/omml/models/ct_drawing.rb +1 -3
  50. data/lib/omml/models/ct_east_asian_layout.rb +1 -3
  51. data/lib/omml/models/ct_effect_extent.rb +1 -3
  52. data/lib/omml/models/ct_em.rb +1 -3
  53. data/lib/omml/models/ct_empty.rb +1 -3
  54. data/lib/omml/models/ct_eq_arr.rb +1 -3
  55. data/lib/omml/models/ct_eq_arr_pr.rb +1 -3
  56. data/lib/omml/models/ct_f.rb +1 -3
  57. data/lib/omml/models/ct_f_pr.rb +1 -3
  58. data/lib/omml/models/ct_f_type.rb +1 -3
  59. data/lib/omml/models/ct_ff_check_box.rb +1 -3
  60. data/lib/omml/models/ct_ff_data.rb +1 -3
  61. data/lib/omml/models/ct_ff_help_text.rb +1 -3
  62. data/lib/omml/models/ct_ff_name.rb +1 -3
  63. data/lib/omml/models/ct_ff_status_text.rb +1 -3
  64. data/lib/omml/models/ct_ff_text_input.rb +1 -3
  65. data/lib/omml/models/ct_ff_text_type.rb +1 -3
  66. data/lib/omml/models/ct_ffdd_list.rb +1 -3
  67. data/lib/omml/models/ct_fit_text.rb +1 -3
  68. data/lib/omml/models/ct_fld_char.rb +1 -3
  69. data/lib/omml/models/ct_fonts.rb +1 -3
  70. data/lib/omml/models/ct_ftn_edn_ref.rb +1 -3
  71. data/lib/omml/models/ct_func.rb +1 -3
  72. data/lib/omml/models/ct_func_pr.rb +1 -3
  73. data/lib/omml/models/ct_graphical_object.rb +1 -3
  74. data/lib/omml/models/ct_graphical_object_data.rb +1 -3
  75. data/lib/omml/models/ct_graphical_object_frame_locking.rb +1 -3
  76. data/lib/omml/models/ct_group_chr.rb +1 -3
  77. data/lib/omml/models/ct_group_chr_pr.rb +1 -3
  78. data/lib/omml/models/ct_highlight.rb +1 -3
  79. data/lib/omml/models/ct_hps_measure.rb +1 -3
  80. data/lib/omml/models/ct_hyperlink.rb +1 -3
  81. data/lib/omml/models/ct_inline.rb +1 -3
  82. data/lib/omml/models/ct_integer2.rb +1 -3
  83. data/lib/omml/models/ct_integer255.rb +1 -3
  84. data/lib/omml/models/ct_lang.rb +1 -3
  85. data/lib/omml/models/ct_language.rb +1 -3
  86. data/lib/omml/models/ct_lim_loc.rb +1 -3
  87. data/lib/omml/models/ct_lim_low.rb +1 -3
  88. data/lib/omml/models/ct_lim_low_pr.rb +1 -3
  89. data/lib/omml/models/ct_lim_upp.rb +1 -3
  90. data/lib/omml/models/ct_lim_upp_pr.rb +1 -3
  91. data/lib/omml/models/ct_lock.rb +1 -3
  92. data/lib/omml/models/ct_m.rb +1 -3
  93. data/lib/omml/models/ct_m_pr.rb +1 -3
  94. data/lib/omml/models/ct_macro_name.rb +1 -3
  95. data/lib/omml/models/ct_manual_break.rb +1 -3
  96. data/lib/omml/models/ct_markup.rb +1 -3
  97. data/lib/omml/models/ct_markup_range.rb +0 -2
  98. data/lib/omml/models/ct_math_pr.rb +1 -3
  99. data/lib/omml/models/ct_mc.rb +1 -3
  100. data/lib/omml/models/ct_mc_pr.rb +1 -3
  101. data/lib/omml/models/ct_mcs.rb +1 -3
  102. data/lib/omml/models/ct_move_bookmark.rb +0 -2
  103. data/lib/omml/models/ct_mr.rb +1 -3
  104. data/lib/omml/models/ct_nary.rb +1 -3
  105. data/lib/omml/models/ct_nary_pr.rb +1 -3
  106. data/lib/omml/models/ct_non_visual_drawing_props.rb +1 -3
  107. data/lib/omml/models/ct_non_visual_graphic_frame_properties.rb +1 -3
  108. data/lib/omml/models/ct_o_math.rb +1 -3
  109. data/lib/omml/models/ct_o_math_arg.rb +1 -3
  110. data/lib/omml/models/ct_o_math_arg_pr.rb +1 -3
  111. data/lib/omml/models/ct_o_math_jc.rb +1 -3
  112. data/lib/omml/models/ct_o_math_para.rb +1 -3
  113. data/lib/omml/models/ct_o_math_para_pr.rb +1 -3
  114. data/lib/omml/models/ct_object.rb +0 -2
  115. data/lib/omml/models/ct_office_art_extension.rb +1 -3
  116. data/lib/omml/models/ct_office_art_extension_list.rb +1 -3
  117. data/lib/omml/models/ct_on_off.rb +1 -3
  118. data/lib/omml/models/ct_p_tab.rb +1 -3
  119. data/lib/omml/models/ct_perm.rb +1 -3
  120. data/lib/omml/models/ct_perm_start.rb +0 -2
  121. data/lib/omml/models/ct_phant.rb +1 -3
  122. data/lib/omml/models/ct_phant_pr.rb +1 -3
  123. data/lib/omml/models/ct_picture.rb +0 -2
  124. data/lib/omml/models/ct_picture_base.rb +1 -3
  125. data/lib/omml/models/ct_placeholder.rb +1 -3
  126. data/lib/omml/models/ct_point2_d.rb +1 -3
  127. data/lib/omml/models/ct_pos_h.rb +1 -3
  128. data/lib/omml/models/ct_pos_v.rb +1 -3
  129. data/lib/omml/models/ct_positive_size2_d.rb +1 -3
  130. data/lib/omml/models/ct_proof_err.rb +1 -3
  131. data/lib/omml/models/ct_r.rb +3 -3
  132. data/lib/omml/models/ct_r_pr.rb +1 -3
  133. data/lib/omml/models/ct_r_pr_change.rb +0 -2
  134. data/lib/omml/models/ct_r_pr_original.rb +1 -3
  135. data/lib/omml/models/ct_rad.rb +1 -3
  136. data/lib/omml/models/ct_rad_pr.rb +1 -3
  137. data/lib/omml/models/ct_rel.rb +1 -3
  138. data/lib/omml/models/ct_rpr.rb +1 -3
  139. data/lib/omml/models/ct_ruby.rb +1 -3
  140. data/lib/omml/models/ct_ruby_align.rb +1 -3
  141. data/lib/omml/models/ct_ruby_content.rb +1 -3
  142. data/lib/omml/models/ct_ruby_pr.rb +1 -3
  143. data/lib/omml/models/ct_run_track_change.rb +0 -2
  144. data/lib/omml/models/ct_s_pre.rb +1 -3
  145. data/lib/omml/models/ct_s_pre_pr.rb +1 -3
  146. data/lib/omml/models/ct_s_sub.rb +1 -3
  147. data/lib/omml/models/ct_s_sub_pr.rb +1 -3
  148. data/lib/omml/models/ct_s_sub_sup.rb +1 -3
  149. data/lib/omml/models/ct_s_sub_sup_pr.rb +1 -3
  150. data/lib/omml/models/ct_s_sup.rb +1 -3
  151. data/lib/omml/models/ct_s_sup_pr.rb +1 -3
  152. data/lib/omml/models/ct_script.rb +1 -3
  153. data/lib/omml/models/ct_sdt_combo_box.rb +1 -3
  154. data/lib/omml/models/ct_sdt_content_run.rb +1 -3
  155. data/lib/omml/models/ct_sdt_date.rb +1 -3
  156. data/lib/omml/models/ct_sdt_date_mapping_type.rb +1 -3
  157. data/lib/omml/models/ct_sdt_doc_part.rb +1 -3
  158. data/lib/omml/models/ct_sdt_drop_down_list.rb +1 -3
  159. data/lib/omml/models/ct_sdt_end_pr.rb +1 -3
  160. data/lib/omml/models/ct_sdt_list_item.rb +1 -3
  161. data/lib/omml/models/ct_sdt_pr.rb +1 -3
  162. data/lib/omml/models/ct_sdt_run.rb +1 -3
  163. data/lib/omml/models/ct_sdt_text.rb +1 -3
  164. data/lib/omml/models/ct_shd.rb +1 -3
  165. data/lib/omml/models/ct_shp.rb +1 -3
  166. data/lib/omml/models/ct_signed_hps_measure.rb +1 -3
  167. data/lib/omml/models/ct_signed_twips_measure.rb +1 -3
  168. data/lib/omml/models/ct_simple_field.rb +1 -3
  169. data/lib/omml/models/ct_smart_tag_pr.rb +1 -3
  170. data/lib/omml/models/ct_smart_tag_run.rb +1 -3
  171. data/lib/omml/models/ct_spacing_rule.rb +1 -3
  172. data/lib/omml/models/ct_string.rb +1 -3
  173. data/lib/omml/models/ct_style.rb +1 -3
  174. data/lib/omml/models/ct_sym.rb +1 -3
  175. data/lib/omml/models/ct_text.rb +1 -3
  176. data/lib/omml/models/ct_text_effect.rb +1 -3
  177. data/lib/omml/models/ct_text_scale.rb +1 -3
  178. data/lib/omml/models/ct_top_bot.rb +1 -3
  179. data/lib/omml/models/ct_track_change.rb +0 -2
  180. data/lib/omml/models/ct_track_change_numbering.rb +0 -2
  181. data/lib/omml/models/ct_twips_measure.rb +1 -3
  182. data/lib/omml/models/ct_un_signed_integer.rb +1 -3
  183. data/lib/omml/models/ct_underline.rb +1 -3
  184. data/lib/omml/models/ct_vertical_align_run.rb +1 -3
  185. data/lib/omml/models/ct_wordprocessing_empty.rb +24 -0
  186. data/lib/omml/models/ct_wordprocessing_on_off.rb +1 -3
  187. data/lib/omml/models/ct_wordprocessing_text.rb +26 -0
  188. data/lib/omml/models/ct_wrap_none.rb +1 -3
  189. data/lib/omml/models/ct_wrap_path.rb +1 -3
  190. data/lib/omml/models/ct_wrap_square.rb +1 -3
  191. data/lib/omml/models/ct_wrap_through.rb +1 -3
  192. data/lib/omml/models/ct_wrap_tight.rb +1 -3
  193. data/lib/omml/models/ct_wrap_top_bottom.rb +1 -3
  194. data/lib/omml/models/ct_x_align.rb +1 -3
  195. data/lib/omml/models/ct_y_align.rb +1 -3
  196. data/lib/omml/models/groups/eg_content_run_content.rb +1 -3
  197. data/lib/omml/models/groups/eg_math_content.rb +1 -3
  198. data/lib/omml/models/groups/eg_o_math_elements.rb +1 -3
  199. data/lib/omml/models/groups/eg_o_math_math_elements.rb +1 -3
  200. data/lib/omml/models/groups/eg_office_art_extension_list.rb +1 -3
  201. data/lib/omml/models/groups/eg_p_content.rb +1 -3
  202. data/lib/omml/models/groups/eg_r_pr.rb +1 -3
  203. data/lib/omml/models/groups/eg_r_pr_base.rb +1 -3
  204. data/lib/omml/models/groups/eg_r_pr_content.rb +1 -3
  205. data/lib/omml/models/groups/eg_r_pr_math.rb +1 -3
  206. data/lib/omml/models/groups/eg_range_markup_elements.rb +1 -3
  207. data/lib/omml/models/groups/eg_ruby_content.rb +1 -3
  208. data/lib/omml/models/groups/eg_run_inner_content.rb +5 -68
  209. data/lib/omml/models/groups/eg_run_level_elts.rb +1 -3
  210. data/lib/omml/models/groups/eg_script_style.rb +1 -3
  211. data/lib/omml/models/groups/eg_word_r_pr.rb +1 -3
  212. data/lib/omml/models/groups/eg_word_run_inner_content.rb +91 -0
  213. data/lib/omml/models/groups/eg_wrap_type.rb +1 -3
  214. data/lib/omml/models/groups.rb +0 -18
  215. data/lib/omml/models/o_math.rb +1 -3
  216. data/lib/omml/models/o_math_para.rb +1 -3
  217. data/lib/omml/models/registerable.rb +22 -0
  218. data/lib/omml/models/simple_types/enum_string.rb +5 -0
  219. data/lib/omml/models/simple_types/hex_binary.rb +0 -1
  220. data/lib/omml/models/simple_types/st_adj_coordinate.rb +2 -1
  221. data/lib/omml/models/simple_types/st_align_h.rb +0 -2
  222. data/lib/omml/models/simple_types/st_align_v.rb +0 -2
  223. data/lib/omml/models/simple_types/st_border.rb +0 -2
  224. data/lib/omml/models/simple_types/st_br_clear.rb +0 -2
  225. data/lib/omml/models/simple_types/st_br_type.rb +0 -2
  226. data/lib/omml/models/simple_types/st_break_bin.rb +0 -2
  227. data/lib/omml/models/simple_types/st_break_bin_sub.rb +0 -2
  228. data/lib/omml/models/simple_types/st_calendar_type.rb +0 -2
  229. data/lib/omml/models/simple_types/st_combine_brackets.rb +0 -2
  230. data/lib/omml/models/simple_types/st_coordinate.rb +0 -1
  231. data/lib/omml/models/simple_types/st_displaced_by_custom_xml.rb +0 -2
  232. data/lib/omml/models/simple_types/st_ed_grp.rb +0 -2
  233. data/lib/omml/models/simple_types/st_em.rb +0 -2
  234. data/lib/omml/models/simple_types/st_f_type.rb +0 -2
  235. data/lib/omml/models/simple_types/st_ff_text_type.rb +0 -2
  236. data/lib/omml/models/simple_types/st_fld_char_type.rb +0 -2
  237. data/lib/omml/models/simple_types/st_geom_guide_name.rb +0 -2
  238. data/lib/omml/models/simple_types/st_hex_color.rb +2 -1
  239. data/lib/omml/models/simple_types/st_highlight_color.rb +0 -2
  240. data/lib/omml/models/simple_types/st_hint.rb +0 -2
  241. data/lib/omml/models/simple_types/st_info_text_type.rb +0 -2
  242. data/lib/omml/models/simple_types/st_integer2.rb +0 -1
  243. data/lib/omml/models/simple_types/st_integer255.rb +0 -1
  244. data/lib/omml/models/simple_types/st_jc.rb +0 -2
  245. data/lib/omml/models/simple_types/st_lang.rb +2 -1
  246. data/lib/omml/models/simple_types/st_lim_loc.rb +0 -2
  247. data/lib/omml/models/simple_types/st_lock.rb +0 -2
  248. data/lib/omml/models/simple_types/st_on_off.rb +0 -2
  249. data/lib/omml/models/simple_types/st_p_tab_alignment.rb +0 -2
  250. data/lib/omml/models/simple_types/st_p_tab_leader.rb +0 -2
  251. data/lib/omml/models/simple_types/st_p_tab_relative_to.rb +0 -2
  252. data/lib/omml/models/simple_types/st_positive_coordinate.rb +0 -1
  253. data/lib/omml/models/simple_types/st_proof_err.rb +0 -2
  254. data/lib/omml/models/simple_types/st_rel_from_h.rb +0 -2
  255. data/lib/omml/models/simple_types/st_rel_from_v.rb +0 -2
  256. data/lib/omml/models/simple_types/st_ruby_align.rb +0 -2
  257. data/lib/omml/models/simple_types/st_script.rb +0 -2
  258. data/lib/omml/models/simple_types/st_sdt_date_mapping_type.rb +0 -2
  259. data/lib/omml/models/simple_types/st_shd.rb +0 -2
  260. data/lib/omml/models/simple_types/st_shp.rb +0 -2
  261. data/lib/omml/models/simple_types/st_spacing_rule.rb +0 -1
  262. data/lib/omml/models/simple_types/st_style.rb +0 -2
  263. data/lib/omml/models/simple_types/st_text_effect.rb +0 -2
  264. data/lib/omml/models/simple_types/st_text_scale.rb +0 -1
  265. data/lib/omml/models/simple_types/st_theme.rb +0 -2
  266. data/lib/omml/models/simple_types/st_theme_color.rb +0 -2
  267. data/lib/omml/models/simple_types/st_top_bot.rb +0 -2
  268. data/lib/omml/models/simple_types/st_underline.rb +0 -2
  269. data/lib/omml/models/simple_types/st_vertical_align_run.rb +0 -2
  270. data/lib/omml/models/simple_types/st_wrap_text.rb +0 -2
  271. data/lib/omml/models/simple_types/st_x_align.rb +0 -2
  272. data/lib/omml/models/simple_types/st_y_align.rb +0 -2
  273. data/lib/omml/models/simple_types/token.rb +5 -0
  274. data/lib/omml/models/simple_types/unsigned_int.rb +0 -1
  275. data/lib/omml/models/simple_types/unsigned_long.rb +0 -1
  276. data/lib/omml/models/simple_types.rb +0 -59
  277. data/lib/omml/models.rb +275 -177
  278. data/lib/omml/namespace.rb +2 -0
  279. data/lib/omml/version.rb +1 -1
  280. data/lib/omml/wordprocessing_namespace.rb +2 -0
  281. data/reference-docs/shared-math.xsd +1528 -0
  282. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2cc9754f1577e9e79ba68c5d841c61067b6c8eb88223a695d0a335a84724c177
4
- data.tar.gz: 3c4677636e4951c68c99fe9af4ce85f7348a12cbec14f9b68431eccda957523b
3
+ metadata.gz: 6940ed4402f47df60c0de9c7768720da480a64725187434630c836bd77d31e66
4
+ data.tar.gz: 73f8119d2186095dfd4d061db0c2d096b7412ca984c713ed2b2933a6bb32e011
5
5
  SHA512:
6
- metadata.gz: 98fe061fee5b9906b97db4cf6826c86638d2c7ec8b9a2b16538b9027f404abac6a3f2062db6d02c25a35d70f55c738f219a2387c7c421049bee7c5a899de7b85
7
- data.tar.gz: fb05a68e0d0855cfb314a3531857b920ee4b56113a7986f943a2800c25bf99957efefb0a110cda39fc82ffbdbb517c140754a65491fb464d057fb1d29784d3ad
6
+ metadata.gz: 4545f65afd76963f930e49396b165e183e1b6b440edfcfb80c7c2b2cc80a6df7531589b2071fdcb61990c65265394ca66432f29b632f0837ea86446f1cee7c32
7
+ data.tar.gz: 5cb71bf1eef0b864459dd7bce5ef5f9aabcf1a3132c3dacbc1aeb3e940cfd0becce3ee5c444dc8f54f859403c37ae3bc37100a62ab7a57da91c116fafec11bd2
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  .rspec_status
2
2
  Gemfile.lock
3
+ TODO.bugs/
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-05-01 11:14:00 UTC using RuboCop version 1.86.1.
3
+ # on 2026-07-15 04:20:07 UTC using RuboCop version 1.86.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -12,8 +12,14 @@ Metrics/AbcSize:
12
12
  Exclude:
13
13
  - 'spec/omml_fixture_round_trip_spec.rb'
14
14
 
15
- # Offense count: 1
16
- # Configuration parameters: .
17
- # SupportedStyles: have_received, receive
18
- RSpec/MessageSpies:
19
- EnforcedStyle: receive
15
+ # Offense count: 2
16
+ # Configuration parameters: IgnoredMetadata.
17
+ RSpec/DescribeClass:
18
+ Exclude:
19
+ - '**/spec/features/**/*'
20
+ - '**/spec/requests/**/*'
21
+ - '**/spec/routing/**/*'
22
+ - '**/spec/system/**/*'
23
+ - '**/spec/views/**/*'
24
+ - 'spec/omml/qualified_namespace_spec.rb'
25
+ - 'spec/omml/wordprocessing_in_math_spec.rb'
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. Models are registered via `Omml::Configuration.register_model(Class)` at load time, and the context is populated lazily on first parse. Custom contexts can be created for downstream libraries (e.g., Plurimath) that fall back to the OMML context.
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
- Three categories of models, all extending `Lutaml::Model::Serializable`:
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
- - **Complex types** (`ct_*.rb`) — represent OMML schema complex types. Each defines attributes and XML element mappings, and self-registers via `Omml::Configuration.register_model`.
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
- Root element wrappers (`o_math.rb`, `o_math_para.rb`) extend `CommonCode::RootModel` and use `omml_root_element` to declare themselves as XML root elements with the OMML namespace.
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/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.
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
data/Gemfile CHANGED
@@ -5,9 +5,6 @@ source "https://rubygems.org"
5
5
  gemspec
6
6
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
7
7
 
8
- # TODO: remove this override once lutaml-model 0.8.0 is released.
9
- gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main"
10
-
11
8
  gem "pry"
12
9
  gem "rake", "~> 12.0"
13
10
  gem "rspec", "~> 3.0"
@@ -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
- # Trigger the model manifest autoload before replaying registrations.
27
- Omml.const_get(:Models)
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)