plutonium 0.41.0 → 0.41.1

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: 23a6720a12194c10b70fc95f5c9b4f2b8aca0906191ed4488238e13048004de4
4
- data.tar.gz: bc178cbd666304c9e170cac636a10ad4c066d104d2397428dc038640ab09091a
3
+ metadata.gz: db950a1455c885d7bdc26d86b267e95c8a7bd7cc61d5d3b2153d4225e7a40dcc
4
+ data.tar.gz: 6387d54982f72c298861147b167b59d768b32fa5f8ecccaa113c75b2d825df1d
5
5
  SHA512:
6
- metadata.gz: ba109a23c3f75f43a453a7a195a15e3b45e334f4751c2747b43c5b82cac6832b696f48bfe4d480015c4b9171e4f47de21f0981e9e40697f069432d592d1d0643
7
- data.tar.gz: 3bbbd2a4d1a69e92c87475b41c452a478f1ba671b413e6e01d358cbe813fc59b91e5c29b9a22e74549575ac5ed6c38d3f8225f02890a7ff376db1a78ede6c72c
6
+ metadata.gz: f9a9dcb55e48ce7d86d8b8ee697149a96025cbacee380869188fa6d6af98981456e388ae16c141f5fc7934f44f8c1044aa0696aa9afe83e5749c8733a56bb7a5
7
+ data.tar.gz: 7e1c4765aa8d761ef049e9824574442b534bd1272506d2298993555bbfe25be929c58518876839f7e2be422e3cbd670c4504439f9ad05a3bf6bf14d052468f8e
@@ -113,6 +113,19 @@ Use `{default:value}` syntax for default values:
113
113
  'status:string?{default:pending}' # Nullable with default
114
114
  ```
115
115
 
116
+ ### JSON/JSONB Default Values
117
+
118
+ Supports nested braces for structured defaults:
119
+
120
+ ```bash
121
+ 'metadata:jsonb{default:{}}' # Empty hash
122
+ 'tags:jsonb{default:[]}' # Empty array
123
+ 'settings:jsonb{default:{"theme":"dark"}}' # Object with values
124
+ 'config:jsonb?{default:{}}' # Nullable with empty hash default
125
+ ```
126
+
127
+ Default values are parsed as JSON first. If JSON parsing fails, the value is treated as a string (or coerced based on column type for integers, floats, and booleans).
128
+
116
129
  ### Decimal with Precision and Default
117
130
 
118
131
  ```bash
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [0.41.1] - 2026-02-09
2
+
3
+ ### 🚀 Features
4
+
5
+ - *(generators)* Support JSON default values for jsonb fields
1
6
  ## [0.41.0] - 2026-02-09
2
7
 
3
8
  ### 🚀 Features
@@ -96,7 +96,7 @@ Use `{default:value}` syntax to set default values:
96
96
  # String with default
97
97
  rails generate pu:res:scaffold Post 'status:string{default:draft}'
98
98
 
99
- # Boolean with default
99
+ # Boolean with default (supports true/false/yes/1)
100
100
  rails generate pu:res:scaffold Post 'active:boolean{default:true}'
101
101
 
102
102
  # Integer with default
@@ -107,12 +107,25 @@ rails generate pu:res:scaffold Product 'price:decimal{10,2,default:0}'
107
107
 
108
108
  # Nullable with default
109
109
  rails generate pu:res:scaffold Post 'category:string?{default:general}'
110
+
111
+ # JSONB with empty hash default
112
+ rails generate pu:res:scaffold Post 'metadata:jsonb{default:{}}'
113
+
114
+ # JSONB with empty array default
115
+ rails generate pu:res:scaffold Post 'tags:jsonb{default:[]}'
116
+
117
+ # JSONB with object default
118
+ rails generate pu:res:scaffold Post 'settings:jsonb{default:{"theme":"dark"}}'
110
119
  ```
111
120
 
112
121
  ::: tip Shell Quoting
113
122
  Always quote fields containing `?` or `{}` to prevent shell expansion.
114
123
  :::
115
124
 
125
+ ::: tip JSON Default Values
126
+ Default values are parsed as JSON first. This allows structured defaults like `{}` and `[]` for JSONB fields. If JSON parsing fails, the value is treated as a string (or coerced based on the column type for integers, floats, and booleans).
127
+ :::
128
+
116
129
  #### Associations
117
130
 
118
131
  ```bash
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- plutonium (0.40.0)
4
+ plutonium (0.41.0)
5
5
  action_policy (~> 0.7.0)
6
6
  listen (~> 3.8)
7
7
  pagy (~> 9.0)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- plutonium (0.40.0)
4
+ plutonium (0.41.0)
5
5
  action_policy (~> 0.7.0)
6
6
  listen (~> 3.8)
7
7
  pagy (~> 9.0)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- plutonium (0.40.0)
4
+ plutonium (0.41.0)
5
5
  action_policy (~> 0.7.0)
6
6
  listen (~> 3.8)
7
7
  pagy (~> 9.0)
@@ -73,11 +73,14 @@ module PlutoniumGenerators
73
73
  def parse(model_name, column_definition)
74
74
  # Protect content inside {} from being split on colons
75
75
  # e.g., "status:string{default:draft}" -> split correctly
76
+ # Handles nested braces like "data:jsonb{default:{}}"
76
77
  options_content = nil
77
78
  if column_definition.include?("{")
78
- column_definition = column_definition.gsub(/\{([^}]*)\}/) do |match|
79
- options_content = $1
80
- "{OPTIONS}"
79
+ options_content = extract_braced_content(column_definition)
80
+ if options_content
81
+ start_idx = column_definition.index("{")
82
+ end_idx = start_idx + options_content.length + 2 # +2 for { and }
83
+ column_definition = column_definition[0...start_idx] + "{OPTIONS}" + column_definition[end_idx..]
81
84
  end
82
85
  end
83
86
 
@@ -141,15 +144,17 @@ module PlutoniumGenerators
141
144
  class_name_value = nil
142
145
 
143
146
  if type&.include?("{")
144
- # Extract default:value
145
- if (match = type.match(/\{([^}]*default:([^,}]+)[^}]*)\}/))
146
- default_value = match[2]
147
+ options_content = extract_braced_content(type)
148
+
149
+ # Extract default:value (supports nested braces like {})
150
+ if options_content&.include?("default:")
151
+ default_value = extract_option_value(options_content, "default")
147
152
  type = remove_option_from_type(type, "default", default_value)
148
153
  end
149
154
 
150
155
  # Extract class_name:Value
151
- if (match = type.match(/\{([^}]*class_name:([^,}]+)[^}]*)\}/))
152
- class_name_value = match[2]
156
+ if options_content&.include?("class_name:")
157
+ class_name_value = extract_option_value(options_content, "class_name")
153
158
  type = remove_option_from_type(type, "class_name", class_name_value)
154
159
  end
155
160
  end
@@ -166,18 +171,82 @@ module PlutoniumGenerators
166
171
  [parsed_type, parsed_options]
167
172
  end
168
173
 
174
+ # Extracts content inside outermost braces, respecting nested braces
175
+ # e.g., "jsonb{default:{}}" -> "default:{}"
176
+ def extract_braced_content(str)
177
+ start_idx = str.index("{")
178
+ return nil unless start_idx
179
+
180
+ depth = 0
181
+ (start_idx...str.length).each do |i|
182
+ case str[i]
183
+ when "{"
184
+ depth += 1
185
+ when "}"
186
+ depth -= 1
187
+ return str[(start_idx + 1)...i] if depth == 0
188
+ end
189
+ end
190
+ nil
191
+ end
192
+
193
+ # Extracts option value, handling nested braces
194
+ # e.g., from "default:{},other:val" extracts "{}" for "default"
195
+ def extract_option_value(content, option_name)
196
+ prefix = "#{option_name}:"
197
+ start_idx = content.index(prefix)
198
+ return nil unless start_idx
199
+
200
+ value_start = start_idx + prefix.length
201
+ return nil if value_start >= content.length
202
+
203
+ # Check if value starts with a brace (nested structure)
204
+ if content[value_start] == "{"
205
+ depth = 0
206
+ (value_start...content.length).each do |i|
207
+ case content[i]
208
+ when "{"
209
+ depth += 1
210
+ when "}"
211
+ depth -= 1
212
+ return content[value_start..i] if depth == 0
213
+ end
214
+ end
215
+ content[value_start..]
216
+ else
217
+ # Simple value - read until comma or end
218
+ end_idx = content.index(",", value_start) || content.length
219
+ content[value_start...end_idx]
220
+ end
221
+ end
222
+
169
223
  def remove_option_from_type(type, option_name, option_value)
170
224
  escaped_value = Regexp.escape(option_value)
171
- type.gsub(/\{[^}]*\}/) do |match|
172
- content = match[1..-2] # Remove { and }
173
- cleaned = content
174
- .gsub(/,?\s*#{option_name}:#{escaped_value}/, "")
175
- .gsub(/#{option_name}:#{escaped_value},?\s*/, "")
176
- cleaned.empty? ? "" : "{#{cleaned}}"
177
- end
225
+ options_content = extract_braced_content(type)
226
+ return type unless options_content
227
+
228
+ cleaned = options_content
229
+ .gsub(/,?\s*#{option_name}:#{escaped_value}/, "")
230
+ .gsub(/#{option_name}:#{escaped_value},?\s*/, "")
231
+
232
+ prefix = type[0...type.index("{")]
233
+ cleaned.empty? ? prefix : "#{prefix}{#{cleaned}}"
178
234
  end
179
235
 
236
+ # Coerces default value using JSON parsing for structured types,
237
+ # with type-based fallback for primitives
180
238
  def coerce_default_value(value, type)
239
+ # Try JSON for structured types (hashes, arrays)
240
+ parsed = JSON.parse(value)
241
+ return parsed if parsed.is_a?(Hash) || parsed.is_a?(Array)
242
+
243
+ # For primitives, use type-based coercion
244
+ coerce_primitive_value(value, type)
245
+ rescue JSON::ParserError
246
+ coerce_primitive_value(value, type)
247
+ end
248
+
249
+ def coerce_primitive_value(value, type)
181
250
  case type&.to_s
182
251
  when "integer"
183
252
  value.to_i
@@ -1,5 +1,5 @@
1
1
  module Plutonium
2
- VERSION = "0.41.0"
2
+ VERSION = "0.41.1"
3
3
  NEXT_MAJOR_VERSION = VERSION.split(".").tap { |v|
4
4
  v[1] = v[1].to_i + 1
5
5
  v[2] = 0
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radioactive-labs/plutonium",
3
- "version": "0.41.0",
3
+ "version": "0.41.1",
4
4
  "description": "Build production-ready Rails apps in minutes, not days. Convention-driven, fully customizable, AI-ready.",
5
5
  "type": "module",
6
6
  "main": "src/js/core.js",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plutonium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.41.0
4
+ version: 0.41.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Froelich