structure 3.6.1 → 3.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c4c9274f8938bdb75aee48a480ea324b9167ea1bb6d27aff8f831908e4083bb
4
- data.tar.gz: 9daeadaf79271deb8ea3469195081a6e7e3a318572bd4e1aa7997b2436223589
3
+ metadata.gz: 3ecf2906322a2c0abaa86ab756c7154139788867b214ee5b30a071adf5e044ff
4
+ data.tar.gz: c36c7b4dfc5656a74b056c72af11ab478fc97886a1638312dd389d252e1977b3
5
5
  SHA512:
6
- metadata.gz: 29ebca3cdff8e8750284b8f1c88b9fd5201ed93b387b2aa8c81872669fce7e52d8496b14d3546fa35b878df87a10e2539aa96789d9a9f0a9589a2c0f5bcf0867
7
- data.tar.gz: b176b7dad3f24d4bf54cf80d69dd115ccba5d75396e9d69ee319b09fee77460cf4f69348ade08a45bc6e07f836b8dc06b644c96548bf4e4efcb0ac8b29255fe5
6
+ metadata.gz: f6bcd31e925af5a25bb65b9ee1b2c4eff4654b7fbce6e7110d3c89195744b5ed7f3564a33dcf0b0cf19a373df315d955637388ccad8f6fa19aa394b21234e1d5
7
+ data.tar.gz: 3707eb84b1b0e702f4e13cf90b9048a3858ed71b07e399d969db4e4e36523fa4e9860c38fbe0d33a4b11336fb2b9f7389d136e80fa4ed280cd0301297102de56
@@ -69,7 +69,7 @@ module Structure
69
69
  if type == :boolean
70
70
  ["#{name}?".to_sym, name] unless name.to_s.end_with?("?")
71
71
  end
72
- end.compact.to_h
72
+ end.to_h
73
73
  end
74
74
  end
75
75
  end
data/lib/structure/rbs.rb CHANGED
@@ -66,7 +66,7 @@ module Structure
66
66
  lines << ""
67
67
 
68
68
  needs_parse_data = types.any? do |_attr, type|
69
- type == :self || type == [:self] || (type.is_a?(Array) && type.first == :array)
69
+ type == :self || type == [:self]
70
70
  end
71
71
 
72
72
  if needs_parse_data
@@ -111,13 +111,7 @@ module Structure
111
111
  when [:self]
112
112
  "Array[#{class_name} | parse_data]"
113
113
  when Array
114
- if type.first == :array && type.last == :self
115
- "Array[#{class_name} | parse_data]"
116
- elsif type.first == :array
117
- # For [:array, SomeType] format, use Array[untyped] since we coerce
118
- "Array[untyped]"
119
- elsif type.size == 1 && type.first == :self
120
- # [:self] is handled above, this shouldn't happen
114
+ if type.size == 1 && type.first == :self
121
115
  "Array[#{class_name} | parse_data]"
122
116
  elsif type.size == 1
123
117
  # Regular array type like [String], [Integer], etc.
@@ -136,16 +130,20 @@ module Structure
136
130
  def map_type_to_rbs(type, class_name)
137
131
  case type
138
132
  when Class
139
- type.name || "untyped"
133
+ if type == Array
134
+ "Array[untyped]"
135
+ elsif type == Hash
136
+ "Hash[untyped, untyped]"
137
+ else
138
+ type.name || "untyped"
139
+ end
140
+
140
141
  when :boolean
141
142
  "bool"
142
143
  when :self
143
144
  class_name || "untyped"
144
145
  when Array
145
- if type.size == 2 && type.first == :array
146
- element_type = map_type_to_rbs(type.last, class_name)
147
- "Array[#{element_type}]"
148
- elsif type.size == 1
146
+ if type.size == 1
149
147
  # Single element array means array of that type
150
148
  element_type = map_type_to_rbs(type.first, class_name)
151
149
  "Array[#{element_type}]"
@@ -36,20 +36,20 @@ module Structure
36
36
  self_referential
37
37
  when String
38
38
  string_class(type, context_class)
39
- when Array
40
- if type.length == 1
41
- array(type.first, context_class)
42
- else
43
- type
44
- end
39
+ when ->(t) { t.is_a?(Array) && t.length == 1 }
40
+ array(type.first, context_class)
41
+ when Hash
42
+ raise ArgumentError, "Cannot specify #{type.inspect} as type"
43
+ when ->(t) { t.respond_to?(:parse) }
44
+ parseable(type)
45
+ when ->(t) { t.respond_to?(:name) && t.name && Kernel.respond_to?(t.name) }
46
+ kernel(type)
47
+ when ->(t) { t.respond_to?(:call) }
48
+ type
49
+ when nil
50
+ type
45
51
  else
46
- if type.respond_to?(:parse)
47
- parseable(type)
48
- elsif type.respond_to?(:name) && type.name && Kernel.respond_to?(type.name)
49
- kernel(type)
50
- else
51
- type
52
- end
52
+ raise ArgumentError, "Cannot specify #{type.inspect} as type"
53
53
  end
54
54
  end
55
55
 
@@ -75,7 +75,7 @@ module Structure
75
75
  private
76
76
 
77
77
  def boolean
78
- @boolean ||= ->(val) { BOOLEAN_TRUTHY.include?(val) }
78
+ ->(val) { BOOLEAN_TRUTHY.include?(val) }
79
79
  end
80
80
 
81
81
  def self_referential
@@ -92,8 +92,15 @@ module Structure
92
92
 
93
93
  def string_class(class_name, context_class)
94
94
  resolved_class = nil
95
+ mutex = Mutex.new
96
+
95
97
  proc do |value|
96
- resolved_class ||= Structure::Types.resolve_class(class_name, context_class)
98
+ unless resolved_class
99
+ mutex.synchronize do
100
+ resolved_class ||= Structure::Types.resolve_class(class_name, context_class)
101
+ end
102
+ end
103
+
97
104
  if resolved_class.respond_to?(:parse)
98
105
  resolved_class.parse(value) # steep:ignore
99
106
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Structure
4
- VERSION = "3.6.1"
4
+ VERSION = "3.6.3"
5
5
  end
data/lib/structure.rb CHANGED
@@ -26,21 +26,24 @@ module Structure
26
26
  # @type var klass: untyped
27
27
  klass = Data.define(*builder.attributes)
28
28
 
29
- # capture metadata and attach to class
29
+ # capture all metadata and attach to class - no closure capture needed
30
+ mappings = builder.mappings
31
+ coercions = builder.coercions(klass)
32
+ predicates = builder.predicate_methods
33
+ after = builder.after_parse_callback
34
+
30
35
  meta = {
31
36
  attributes: builder.attributes.freeze,
32
37
  types: builder.types.freeze,
33
38
  defaults: builder.defaults.freeze,
39
+ mappings: mappings.freeze,
40
+ coercions: coercions.freeze,
41
+ predicates: predicates.freeze,
42
+ after: after,
34
43
  }.freeze
35
44
  klass.instance_variable_set(:@__structure_meta__, meta)
36
45
  klass.singleton_class.attr_reader(:__structure_meta__)
37
46
 
38
- # capture locals for method generation
39
- mappings = builder.mappings
40
- coercions = builder.coercions(klass)
41
- predicates = builder.predicate_methods
42
- after = builder.after_parse_callback
43
-
44
47
  # Define predicate methods
45
48
  predicates.each do |pred, attr|
46
49
  klass.define_method(pred) { !!public_send(attr) }
@@ -63,7 +66,7 @@ module Structure
63
66
  end
64
67
 
65
68
  # parse accepts JSON-ish hashes + kwargs override
66
- klass.define_singleton_method(:parse) do |data = {}, **kwargs|
69
+ klass.singleton_class.define_method(:parse) do |data = {}, **kwargs|
67
70
  return data if data.is_a?(self)
68
71
 
69
72
  unless data.respond_to?(:merge!)
@@ -77,13 +80,14 @@ module Structure
77
80
  # @type var final: Hash[Symbol, untyped]
78
81
  final = {}
79
82
 
80
- # TODO: `__structure_meta__` exists but seems not to return the types it defines, so going untyped for now
81
- #
82
83
  # @type var meta: untyped
83
84
  meta = __structure_meta__
84
85
 
85
86
  attributes = meta.fetch(:attributes)
86
87
  defaults = meta.fetch(:defaults)
88
+ mappings = meta.fetch(:mappings)
89
+ coercions = meta.fetch(:coercions)
90
+ after = meta.fetch(:after)
87
91
 
88
92
  attributes.each do |attr|
89
93
  source = mappings[attr] || attr.to_s
@@ -95,7 +99,8 @@ module Structure
95
99
 
96
100
  coercion = coercions[attr]
97
101
  if coercion && !value.nil?
98
- # self-referential types need class context to call parse
102
+ # Procs (not lambdas) need class context for self-referential parsing
103
+ # Lambdas and other callables use direct invocation
99
104
  value =
100
105
  if coercion.is_a?(Proc) && !coercion.lambda?
101
106
  instance_exec(value, &coercion) # steep:ignore
data/sig/structure.rbs CHANGED
@@ -3,9 +3,12 @@ module Structure
3
3
  def __structure_meta__: () -> {
4
4
  attributes: Array[Symbol],
5
5
  types: Hash[Symbol, untyped],
6
- defaults: Hash[Symbol, untyped]
6
+ defaults: Hash[Symbol, untyped],
7
+ mappings: Hash[Symbol, String],
8
+ coercions: Hash[Symbol, untyped],
9
+ predicates: Hash[Symbol, Symbol],
10
+ after: untyped
7
11
  }
8
- def parse: (?Hash[String | Symbol, untyped] data, **untyped kwargs) -> instance
9
12
  end
10
13
 
11
14
  def self.new: () ?{ (Structure::Builder) [self: Structure::Builder] -> void } -> untyped
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: 3.6.1
4
+ version: 3.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hakan Ensari