structure 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2306964e18c5a78a2241c8c42071fd0a1921fa8c7c54cf210b0825b20607cfca
4
- data.tar.gz: c43cf42996e715fe5c1ca1b57bf91ee411405f188382b2d10c0b593a66e90a6f
3
+ metadata.gz: 74f52e94d4ffd40ad55e291a4ab72c108af572be936ee2d1571fef47699af2cb
4
+ data.tar.gz: d2ef8dee35e2ea236237d1c34c68bc6877039145553f7e1b16729ea84e3778c4
5
5
  SHA512:
6
- metadata.gz: 314242bf300d7ec09e1755647d4a8fef224eb2c64fc0b6d945be386b7af008c22146e80993f1e753f1df9cc5b37841242b7ab4a14d71cde42732952e288fc196
7
- data.tar.gz: 72dcc21770b4f57b67fd64b8ad69695f02d0a55b2622717f63795b8da32aa8b45cd61db146b2e2c4dc02683588ca74dba831437652afaed9bb25fc159195d0b5
6
+ metadata.gz: f0c4192ddde552c7cd4a583aca9bdad31aeb64dc039680e965944696ee7648cc9a423017ab8cea5546c708ae72b7920c21fb2c96e74442932f24a3a85faa6b81
7
+ data.tar.gz: 03d4ea5fda959cd3bcd60b79a323b8dbfaeb5792c22b809179b7f84f76aab36845e7dbcd2f30567cf7922eea9995997cfb730cd69e8dd726cebded2bd076f540
data/lib/structure/rbs.rb CHANGED
@@ -4,6 +4,10 @@ require "fileutils"
4
4
  require "pathname"
5
5
 
6
6
  module Structure
7
+ # Generates RBS type signatures for Structure classes
8
+ #
9
+ # Note: Custom methods defined in Structure blocks are not included and must be manually added to RBS files. This is
10
+ # consistent with how Ruby's RBS tooling handles Data classes.
7
11
  module RBS
8
12
  class << self
9
13
  def emit(klass)
@@ -63,50 +67,66 @@ module Structure
63
67
  [attr, rbs_type != "untyped" ? "#{rbs_type}?" : rbs_type]
64
68
  end.to_h
65
69
 
66
- # Mark optional attributes with ? prefix in keyword params
67
- keyword_params = attributes.map do |attr|
68
- prefix = required.include?(attr) ? "" : "?"
69
- "#{prefix}#{attr}: #{rbs_types[attr]}"
70
- end.join(", ")
70
+ # Sort keyword params: required first, then optional (with ? prefix)
71
+ # Within each group, maintain declaration order
72
+ required_params = required.map { |attr| "#{attr}: #{rbs_types[attr]}" }
73
+ optional_params = (attributes - required).map { |attr| "?#{attr}: #{rbs_types[attr]}" }
74
+ keyword_params = (required_params + optional_params).join(", ")
71
75
  positional_params = attributes.map { |attr| rbs_types[attr] }.join(", ")
72
76
 
73
- lines << " def self.new: (#{keyword_params}) -> #{class_name}"
74
- lines << " | (#{positional_params}) -> #{class_name}"
75
- lines << ""
76
-
77
77
  needs_parse_data = types.any? do |_attr, type|
78
78
  type == :self || type == [:self]
79
79
  end
80
80
 
81
+ # Generate type alias first if needed (RBS::Sorter puts types at top)
81
82
  if needs_parse_data
82
- lines << " type parse_data = {"
83
- attributes.each do |attr|
83
+ lines << " type parse_data = { " + attributes.map { |attr|
84
84
  type = types.fetch(attr, nil)
85
85
  parse_type = parse_data_type(type, class_name)
86
- lines << " ?#{attr}: #{parse_type},"
87
- end
88
- lines[-1] = lines[-1].chomp(",")
89
- lines << " }"
90
- lines << ""
86
+ "?#{attr}: #{parse_type}"
87
+ }.join(", ") + " }"
88
+ end
89
+
90
+ lines << " def self.new: (#{keyword_params}) -> #{class_name}"
91
+ lines << " | (#{positional_params}) -> #{class_name}"
92
+ lines << ""
93
+ lines << " def self.[]: (#{keyword_params}) -> #{class_name}"
94
+ lines << " | (#{positional_params}) -> #{class_name}"
95
+ lines << ""
96
+
97
+ # Generate members tuple type
98
+ members_tuple = attributes.map { |attr| ":#{attr}" }.join(", ")
99
+ lines << " def self.members: () -> [ #{members_tuple} ]"
100
+ lines << ""
101
+
102
+ # Generate parse method signatures
103
+ if needs_parse_data
91
104
  lines << " def self.parse: (?parse_data data) -> #{class_name}"
92
105
  lines << " | (?Hash[String, untyped] data) -> #{class_name}"
93
106
  else
94
- # For structures without special types, just use Hash
95
- lines << " def self.parse: (?(Hash[String | Symbol, untyped]), **untyped) -> #{class_name}"
107
+ # Remove optional parentheses to match RBS::Sorter style
108
+ lines << " def self.parse: (?Hash[String | Symbol, untyped], **untyped) -> #{class_name}"
96
109
  end
97
110
  lines << ""
98
111
 
99
- attributes.each do |attr|
112
+ # Sort attr_reader lines alphabetically (RBS::Sorter does this)
113
+ attributes.sort.each do |attr|
100
114
  lines << " attr_reader #{attr}: #{rbs_types[attr]}"
101
115
  end
102
- lines << ""
103
116
 
104
- types.each do |attr, type|
105
- if type == :boolean && !attr.to_s.end_with?("?")
117
+ # Add boolean predicates
118
+ boolean_predicates = types.sort.select { |attr, type| type == :boolean && !attr.to_s.end_with?("?") }
119
+ unless boolean_predicates.empty?
120
+ lines << ""
121
+ boolean_predicates.each do |attr, _type|
106
122
  lines << " def #{attr}?: () -> bool"
107
123
  end
108
124
  end
109
125
 
126
+ # Instance members method comes after attr_readers and predicates
127
+ lines << " def members: () -> [ #{members_tuple} ]"
128
+ lines << ""
129
+
110
130
  hash_type = attributes.map { |attr| "#{attr}: #{rbs_types[attr]}" }.join(", ")
111
131
  lines << " def to_h: () -> { #{hash_type} }"
112
132
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Structure
4
- VERSION = "4.0.0"
4
+ VERSION = "4.1.0"
5
5
  end
data/lib/structure.rb CHANGED
@@ -26,6 +26,22 @@ module Structure
26
26
  # @type var klass: untyped
27
27
  klass = Data.define(*builder.attributes)
28
28
 
29
+ # Enable custom method definitions by evaluating block on the class
30
+ if block
31
+ # Provide temporary dummy DSL methods to prevent NoMethodError during class_eval
32
+ klass.define_singleton_method(:attribute) { |*args, **kwargs, &blk| }
33
+ klass.define_singleton_method(:attribute?) { |*args, **kwargs, &blk| }
34
+ klass.define_singleton_method(:after_parse) { |&blk| }
35
+
36
+ # Evaluate block in class context for method definitions
37
+ klass.class_eval(&block)
38
+
39
+ # Remove temporary DSL methods
40
+ klass.singleton_class.send(:remove_method, :attribute)
41
+ klass.singleton_class.send(:remove_method, :attribute?)
42
+ klass.singleton_class.send(:remove_method, :after_parse)
43
+ end
44
+
29
45
  # Override initialize to make optional attributes truly optional
30
46
  optional_attrs = builder.optional
31
47
  unless optional_attrs.empty?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: structure
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hakan Ensari