solara 0.2.4 → 0.3.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 +4 -4
- data/solara/lib/core/dashboard/brand/.DS_Store +0 -0
- data/solara/lib/core/dashboard/brand/BrandDetail.js +0 -1
- data/solara/lib/core/scripts/brand_config_manager.rb +10 -6
- data/solara/lib/core/scripts/code_generator.rb +491 -0
- data/solara/lib/solara/version.rb +1 -1
- metadata +4 -4
- data/solara/lib/core/scripts/brand_config_generator.rb +0 -246
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a886519a93f39b2e87df6f2f427fa72e6baf9d4f3520424a7c86c207cb684e56
|
4
|
+
data.tar.gz: e5f0f1673493a4177eaf70ba1af01ef6de900074f8c934c6b15600197f2af474
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd784de6b07c8208c290f6cdb00827e0aa2e44f0f10d19e584592bca6d1aa2d7c7bb195c599286500dbacb71662956b6247f1fb7ca82daf40f0c60ab0022e4ad
|
7
|
+
data.tar.gz: 0e920bd5ba5938eac7933c40708df506762620bb3466ff743851361f414845d396c363e66145008b3b46a539ca3633f2117ecee6c44cc862764f3014112a8df6
|
Binary file
|
@@ -13,13 +13,17 @@ class BrandConfigManager
|
|
13
13
|
Solara.logger.start_step("Generate #{name} for #{platform}")
|
14
14
|
config = load_config(FilePath.brand_config(@brand_key))
|
15
15
|
add_basic_brand_info(config, platform)
|
16
|
-
config_generator =
|
17
|
-
config,
|
18
|
-
|
19
|
-
|
20
|
-
platform
|
16
|
+
config_generator = CodeGenerator.new(
|
17
|
+
json: config,
|
18
|
+
language: language,
|
19
|
+
parent_class_name: 'BrandConfig',
|
21
20
|
)
|
22
|
-
|
21
|
+
output_dir = FilePath.generated_config(name, platform)
|
22
|
+
content = config_generator.generate
|
23
|
+
FileManager.create_file_if_not_exist(output_dir)
|
24
|
+
File.write(output_dir, content)
|
25
|
+
Solara.logger.debug("Generated brand config #{@output_dir} for: #{@language}")
|
26
|
+
|
23
27
|
Solara.logger.end_step("Generate #{name} for #{platform}")
|
24
28
|
end
|
25
29
|
|
@@ -0,0 +1,491 @@
|
|
1
|
+
module Language
|
2
|
+
Kotlin = 'kotlin'
|
3
|
+
Swift = 'swift'
|
4
|
+
Dart = 'dart'
|
5
|
+
|
6
|
+
def self.all
|
7
|
+
[Kotlin, Swift, Dart]
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.init(language)
|
11
|
+
if all.include?(language)
|
12
|
+
# Do something with the valid language
|
13
|
+
else
|
14
|
+
raise ArgumentError, "Invalid language. Please use one of: #{all.join(', ')}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class CodeGenerator
|
20
|
+
def initialize(
|
21
|
+
json:,
|
22
|
+
language:,
|
23
|
+
parent_class_name:)
|
24
|
+
@json = json
|
25
|
+
@language = language
|
26
|
+
@parent_class_name = parent_class_name
|
27
|
+
@generator = create_language_generator
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate
|
31
|
+
@generator.generate_content
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def create_language_generator
|
37
|
+
case @language
|
38
|
+
when Language::Kotlin
|
39
|
+
KotlinCodeGenerator.new(@json, @parent_class_name)
|
40
|
+
when Language::Swift
|
41
|
+
SwiftCodeGenerator.new(@json, @parent_class_name)
|
42
|
+
when Language::Dart
|
43
|
+
DartCodeGenerator.new(@json, @parent_class_name)
|
44
|
+
else
|
45
|
+
raise ArgumentError, "Unsupported language: #{@language}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class BaseCodeGenerator
|
51
|
+
def initialize(json, parent_class_name)
|
52
|
+
@parent_class_name = parent_class_name
|
53
|
+
@json = json
|
54
|
+
end
|
55
|
+
|
56
|
+
def generate_content
|
57
|
+
content = "// Generated by Solara\n"
|
58
|
+
content += language_specific_imports
|
59
|
+
|
60
|
+
classes = []
|
61
|
+
generate_classes(@parent_class_name, @json, classes, generate_json: true)
|
62
|
+
|
63
|
+
classes.reverse_each do |class_content|
|
64
|
+
content += class_content
|
65
|
+
content += "\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
content
|
69
|
+
end
|
70
|
+
|
71
|
+
def generate_classes(class_name, json, classes, generate_json: false)
|
72
|
+
content = class_declaration(class_name)
|
73
|
+
constructor_params = []
|
74
|
+
|
75
|
+
json.each do |key, value|
|
76
|
+
type = value_type(value, key)
|
77
|
+
content += property_declaration(key, type, json)
|
78
|
+
constructor_params << constructor_parameter(key, type)
|
79
|
+
end
|
80
|
+
|
81
|
+
content += property_declaration("asJson", "String", json)
|
82
|
+
|
83
|
+
content += constructor_declaration(class_name, constructor_params)
|
84
|
+
content += instance_declaration(class_name, json)
|
85
|
+
content += json_methods(json, class_name)
|
86
|
+
content += class_closing
|
87
|
+
|
88
|
+
classes << content
|
89
|
+
|
90
|
+
json.each do |key, value|
|
91
|
+
if value.is_a?(Hash)
|
92
|
+
nested_class_name = "#{key[0].upcase}#{key[1..-1]}" # Capitalize first character
|
93
|
+
generate_classes(nested_class_name, value, classes)
|
94
|
+
elsif value.is_a?(Array) && value.any? { |item| item.is_a?(Hash) }
|
95
|
+
nested_class_name = "#{key[0].upcase}#{key[1..-1]}Item" # Capitalize first character
|
96
|
+
generate_classes(nested_class_name, value.first, classes)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def class_declaration(class_name)
|
102
|
+
raise NotImplementedError, "Subclasses must implement class_declaration"
|
103
|
+
end
|
104
|
+
|
105
|
+
def property_declaration(key, type, json)
|
106
|
+
raise NotImplementedError, "Subclasses must implement property_declaration"
|
107
|
+
end
|
108
|
+
|
109
|
+
def constructor_parameter(key, type)
|
110
|
+
raise NotImplementedError, "Subclasses must implement constructor_parameter"
|
111
|
+
end
|
112
|
+
|
113
|
+
def constructor_declaration(class_name, params)
|
114
|
+
raise NotImplementedError, "Subclasses must implement constructor_declaration"
|
115
|
+
end
|
116
|
+
|
117
|
+
def instance_declaration(class_name, json)
|
118
|
+
raise NotImplementedError, "Subclasses must implement instance_declaration"
|
119
|
+
end
|
120
|
+
|
121
|
+
def json_methods(json, class_name)
|
122
|
+
raise NotImplementedError, "Subclasses must implement json_methods"
|
123
|
+
end
|
124
|
+
|
125
|
+
def class_closing
|
126
|
+
raise NotImplementedError, "Subclasses must implement class_closing"
|
127
|
+
end
|
128
|
+
|
129
|
+
def language_specific_imports
|
130
|
+
raise NotImplementedError, "Subclasses must implement language_specific_imports"
|
131
|
+
end
|
132
|
+
|
133
|
+
def value_type(value, class_prefix)
|
134
|
+
raise NotImplementedError, "Subclasses must implement value_type"
|
135
|
+
end
|
136
|
+
|
137
|
+
def value_for(value, class_prefix, indent)
|
138
|
+
raise NotImplementedError, "Subclasses must implement value_type"
|
139
|
+
end
|
140
|
+
|
141
|
+
def color_for(value)
|
142
|
+
raise NotImplementedError, "Subclasses must implement color_for"
|
143
|
+
end
|
144
|
+
|
145
|
+
def language_specific_null
|
146
|
+
raise NotImplementedError, "Subclasses must implement language_specific_null"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class KotlinCodeGenerator < BaseCodeGenerator
|
151
|
+
def language_specific_imports
|
152
|
+
"import android.graphics.Color\n" +
|
153
|
+
"import java.io.Serializable\n\n"
|
154
|
+
end
|
155
|
+
|
156
|
+
def class_declaration(class_name)
|
157
|
+
"\ndata class #{class_name}(\n"
|
158
|
+
end
|
159
|
+
|
160
|
+
def property_declaration(key, type, json)
|
161
|
+
if key == "asJson"
|
162
|
+
json_string = json.to_json.gsub('"', '\\"')
|
163
|
+
" val #{key}: String = \"#{json_string}\",\n"
|
164
|
+
else
|
165
|
+
" val #{key}: #{type},\n"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def constructor_parameter(key, type)
|
170
|
+
"val #{key}: #{type}"
|
171
|
+
end
|
172
|
+
|
173
|
+
def constructor_declaration(class_name, params)
|
174
|
+
"): Serializable {\n"
|
175
|
+
end
|
176
|
+
|
177
|
+
def instance_declaration(class_name, json)
|
178
|
+
" companion object {\n val instance = #{class_name}(\n#{json.map { |k, v| " #{k} = #{value_for(v, k, ' ')}" }.join(",\n")}\n )\n }\n"
|
179
|
+
end
|
180
|
+
|
181
|
+
def json_methods(json, class_name)
|
182
|
+
""
|
183
|
+
end
|
184
|
+
|
185
|
+
def class_closing
|
186
|
+
"}\n"
|
187
|
+
end
|
188
|
+
|
189
|
+
def value_for(value, class_prefix, indent)
|
190
|
+
case value
|
191
|
+
when String
|
192
|
+
if value.start_with?('#') && value.length == 7 # Assume it's a color
|
193
|
+
color_for(value)
|
194
|
+
else
|
195
|
+
"\"#{value}\"" # Use double quotes for Kotlin strings
|
196
|
+
end
|
197
|
+
when Integer, Float
|
198
|
+
value.to_s
|
199
|
+
when TrueClass, FalseClass
|
200
|
+
value.to_s
|
201
|
+
when Array
|
202
|
+
if value.empty?
|
203
|
+
"emptyList()" # Use Kotlin's emptyList() for empty arrays
|
204
|
+
elsif value.all? { |item| item.is_a?(Hash) }
|
205
|
+
array_items = value.map do |item|
|
206
|
+
item_values = item.map { |k, v| "#{k} = #{value_for(v, k, indent + ' ')}" }.join(",\n#{indent} ")
|
207
|
+
"#{class_prefix[0].upcase}#{class_prefix[1..-1]}Item(\n#{indent} #{item_values}\n#{indent} )"
|
208
|
+
end.join(",\n#{indent} ")
|
209
|
+
"listOf(\n#{indent} #{array_items}\n#{indent})"
|
210
|
+
else
|
211
|
+
array_items = value.map { |item| value_for(item, class_prefix, indent + ' ') }.join(", ")
|
212
|
+
"listOf(\n#{indent} #{array_items}\n#{indent})" # Use listOf for non-empty lists
|
213
|
+
end
|
214
|
+
when Hash
|
215
|
+
"#{class_prefix[0].upcase}#{class_prefix[1..-1]}.instance"
|
216
|
+
else
|
217
|
+
language_specific_null
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def value_type(value, class_prefix)
|
222
|
+
case value
|
223
|
+
when String then 'String'
|
224
|
+
when Integer then 'Int'
|
225
|
+
when Float then 'Float'
|
226
|
+
when TrueClass, FalseClass then 'Boolean'
|
227
|
+
when Array
|
228
|
+
if value.empty?
|
229
|
+
'List<Any>'
|
230
|
+
elsif value.all? { |item| item.is_a?(String) }
|
231
|
+
'List<String>'
|
232
|
+
elsif value.all? { |item| item.is_a?(Integer) }
|
233
|
+
'List<Int>'
|
234
|
+
elsif value.all? { |item| item.is_a?(Float) }
|
235
|
+
'List<Float>'
|
236
|
+
elsif value.all? { |item| item.is_a?(TrueClass) || item.is_a?(FalseClass) }
|
237
|
+
'List<Boolean>'
|
238
|
+
elsif value.all? { |item| item.is_a?(Hash) }
|
239
|
+
"List<#{class_prefix[0].upcase}#{class_prefix[1..-1]}Item>"
|
240
|
+
else
|
241
|
+
'List<Any>'
|
242
|
+
end
|
243
|
+
when Hash then "#{class_prefix[0].upcase}#{class_prefix[1..-1]}"
|
244
|
+
else 'Any'
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def color_for(value)
|
249
|
+
"Color.parseColor(\"#{value}\")"
|
250
|
+
end
|
251
|
+
|
252
|
+
def language_specific_null
|
253
|
+
'null'
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class SwiftCodeGenerator < BaseCodeGenerator
|
258
|
+
def language_specific_imports
|
259
|
+
"import UIKit\n\n"
|
260
|
+
end
|
261
|
+
|
262
|
+
def class_declaration(class_name)
|
263
|
+
"struct #{class_name}: Codable {\n"
|
264
|
+
end
|
265
|
+
|
266
|
+
def property_declaration(key, type, json)
|
267
|
+
if key == "asJson"
|
268
|
+
json_string = json.to_json.gsub('"', '\\"')
|
269
|
+
" let #{key}: String = \"#{json_string}\"\n"
|
270
|
+
else
|
271
|
+
" let #{key}: #{type}\n"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def constructor_parameter(key, type)
|
276
|
+
"#{key}: #{type}"
|
277
|
+
end
|
278
|
+
|
279
|
+
def constructor_declaration(class_name, params)
|
280
|
+
"\n init(\n#{params.map { |p| " #{p}"}.join(",\n")}) {\n#{params.map { |p| " self.#{p.split(':').first} = #{p.split(':').first}" }.join("\n")}\n }\n\n"
|
281
|
+
end
|
282
|
+
|
283
|
+
def instance_declaration(class_name, json)
|
284
|
+
" static let shared = #{class_name}(\n#{json.map { |k, v| " #{k}: #{value_for(v, k, ' ')}" }.join(",\n")}\n )\n"
|
285
|
+
end
|
286
|
+
|
287
|
+
def json_methods(json, class_name)
|
288
|
+
" func toJson() -> String? {\n" +
|
289
|
+
" let encoder = JSONEncoder()\n" +
|
290
|
+
" if let jsonData = try? encoder.encode(self) {\n" +
|
291
|
+
" return String(data: jsonData, encoding: .utf8)\n" +
|
292
|
+
" }\n" +
|
293
|
+
" return nil\n" +
|
294
|
+
" }\n\n" +
|
295
|
+
" static func fromJson(_ json: String) -> #{class_name}? {\n" +
|
296
|
+
" let decoder = JSONDecoder()\n" +
|
297
|
+
" if let jsonData = json.data(using: .utf8),\n" +
|
298
|
+
" let result = try? decoder.decode(#{class_name}.self, from: jsonData) {\n" +
|
299
|
+
" return result\n" +
|
300
|
+
" }\n" +
|
301
|
+
" return nil\n" +
|
302
|
+
" }\n"
|
303
|
+
end
|
304
|
+
|
305
|
+
def class_closing
|
306
|
+
"}\n"
|
307
|
+
end
|
308
|
+
|
309
|
+
def value_for(value, class_prefix, indent)
|
310
|
+
case value
|
311
|
+
when String
|
312
|
+
if value.start_with?('#') && value.length == 7 # Assume it's a color
|
313
|
+
color_for(value)
|
314
|
+
else
|
315
|
+
"\"#{value}\""
|
316
|
+
end
|
317
|
+
when Integer, Float
|
318
|
+
value.to_s
|
319
|
+
when TrueClass, FalseClass
|
320
|
+
value.to_s
|
321
|
+
when Array
|
322
|
+
if value.empty?
|
323
|
+
"[]"
|
324
|
+
elsif value.all? { |item| item.is_a?(Hash) }
|
325
|
+
array_items = value.map do |item|
|
326
|
+
item_values = item.map { |k, v| "#{k}: #{value_for(v, k, indent + ' ')}" }.join(",\n#{indent} ")
|
327
|
+
"#{class_prefix[0].upcase}#{class_prefix[1..-1]}Item(\n#{indent} #{item_values}\n#{indent} )"
|
328
|
+
end.join(",\n#{indent} ")
|
329
|
+
"[\n#{indent} #{array_items}\n#{indent}]"
|
330
|
+
else
|
331
|
+
array_items = value.map { |item| value_for(item, class_prefix, indent + ' ') }.join(", ")
|
332
|
+
"[\n#{indent} #{array_items}\n#{indent}]"
|
333
|
+
end
|
334
|
+
when Hash
|
335
|
+
"#{class_prefix[0].upcase}#{class_prefix[1..-1]}.shared"
|
336
|
+
else
|
337
|
+
language_specific_null
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def value_type(value, class_prefix)
|
342
|
+
case value
|
343
|
+
when String then 'String'
|
344
|
+
when Integer then 'Int'
|
345
|
+
when Float then 'Double'
|
346
|
+
when TrueClass, FalseClass then 'Bool'
|
347
|
+
when Array
|
348
|
+
if value.empty?
|
349
|
+
'[Any]'
|
350
|
+
elsif value.all? { |item| item.is_a?(String) }
|
351
|
+
'[String]'
|
352
|
+
elsif value.all? { |item| item.is_a?(Integer) }
|
353
|
+
'[Int]'
|
354
|
+
elsif value.all? { |item| item.is_a?(Float) }
|
355
|
+
'[Double]'
|
356
|
+
elsif value.all? { |item| item.is_a?(TrueClass) || item.is_a?(FalseClass) }
|
357
|
+
'[Bool]'
|
358
|
+
elsif value.all? { |item| item.is_a?(Hash) }
|
359
|
+
"[#{class_prefix[0].upcase}#{class_prefix[1..-1]}Item]"
|
360
|
+
else
|
361
|
+
'[Any]'
|
362
|
+
end
|
363
|
+
when Hash then "#{class_prefix[0].upcase}#{class_prefix[1..-1]}"
|
364
|
+
else 'Any'
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
def color_for(value)
|
369
|
+
r, g, b = value[1..2].to_i(16), value[3..4].to_i(16), value[5..6].to_i(16)
|
370
|
+
"UIColor(red: #{r}/255.0, green: #{g}/255.0, blue: #{b}/255.0, alpha: 1.0)"
|
371
|
+
end
|
372
|
+
|
373
|
+
def language_specific_null
|
374
|
+
'nil'
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
class DartCodeGenerator < BaseCodeGenerator
|
379
|
+
def language_specific_imports
|
380
|
+
"import 'package:flutter/material.dart';\n" +
|
381
|
+
"import 'dart:convert';\n\n"
|
382
|
+
end
|
383
|
+
|
384
|
+
def class_declaration(class_name)
|
385
|
+
"class #{class_name} {\n"
|
386
|
+
end
|
387
|
+
|
388
|
+
def property_declaration(key, type, json)
|
389
|
+
if key == "asJson"
|
390
|
+
json_string = json.to_json.gsub('"', '\\"')
|
391
|
+
" final String #{key} = \"#{json_string}\";\n"
|
392
|
+
else
|
393
|
+
" final #{type} #{key};\n"
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
def constructor_parameter(key, type)
|
398
|
+
"required this.#{key}"
|
399
|
+
end
|
400
|
+
|
401
|
+
def constructor_declaration(class_name, params)
|
402
|
+
params.empty? ? "\n const #{class_name}();\n\n" : "\n const #{class_name}({\n#{params.map { |p| " #{p}"}.join(",\n")}});\n\n"
|
403
|
+
end
|
404
|
+
|
405
|
+
def instance_declaration(class_name, json)
|
406
|
+
" static const #{class_name} instance = #{class_name}(\n#{json.map { |k, v| " #{k}: #{value_for(v, k, ' ')}" }.join(",\n")}\n );\n"
|
407
|
+
end
|
408
|
+
|
409
|
+
def json_methods(json, class_name)
|
410
|
+
" Map<String, dynamic> toJson() => {\n" +
|
411
|
+
" #{json.keys.map { |k| "'#{k}': #{k}" }.join(",\n ")}\n" +
|
412
|
+
" };\n\n" +
|
413
|
+
" factory #{class_name}.fromJson(Map<String, dynamic> json) => #{class_name}(\n" +
|
414
|
+
" #{json.keys.map { |k| "#{k}: json['#{k}']" }.join(",\n ")}\n" +
|
415
|
+
" );\n\n" +
|
416
|
+
" String toJsonString() => jsonEncode(toJson());\n\n" +
|
417
|
+
" factory #{class_name}.fromJsonString(String jsonString) =>\n" +
|
418
|
+
" #{class_name}.fromJson(jsonDecode(jsonString) as Map<String, dynamic>);\n"
|
419
|
+
end
|
420
|
+
|
421
|
+
def class_closing
|
422
|
+
"}\n"
|
423
|
+
end
|
424
|
+
|
425
|
+
def value_for(value, class_prefix, indent)
|
426
|
+
case value
|
427
|
+
when String
|
428
|
+
if value.start_with?('#') && value.length == 7 # Assume it's a color
|
429
|
+
color_for(value)
|
430
|
+
else
|
431
|
+
"\"#{value}\""
|
432
|
+
end
|
433
|
+
when Integer, Float
|
434
|
+
value.to_s
|
435
|
+
when TrueClass, FalseClass
|
436
|
+
value.to_s
|
437
|
+
when Array
|
438
|
+
if value.empty?
|
439
|
+
"[]"
|
440
|
+
elsif value.all? { |item| item.is_a?(Hash) }
|
441
|
+
array_items = value.map do |item|
|
442
|
+
item_values = item.map { |k, v| "#{k}: #{value_for(v, k, indent + ' ')}" }.join(",\n#{indent} ")
|
443
|
+
"#{class_prefix[0].upcase}#{class_prefix[1..-1]}Item(\n#{indent} #{item_values}\n#{indent} )"
|
444
|
+
end.join(",\n#{indent} ")
|
445
|
+
"[\n#{indent} #{array_items}\n#{indent}]"
|
446
|
+
else
|
447
|
+
array_items = value.map { |item| value_for(item, class_prefix, indent + ' ') }.join(", ")
|
448
|
+
"[\n#{indent} #{array_items}\n#{indent}]"
|
449
|
+
end
|
450
|
+
when Hash
|
451
|
+
"#{class_prefix[0].upcase}#{class_prefix[1..-1]}.instance"
|
452
|
+
else
|
453
|
+
language_specific_null
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
def value_type(value, class_prefix)
|
458
|
+
case value
|
459
|
+
when String then 'String'
|
460
|
+
when Integer then 'int'
|
461
|
+
when Float then 'double'
|
462
|
+
when TrueClass, FalseClass then 'bool'
|
463
|
+
when Array
|
464
|
+
if value.empty?
|
465
|
+
'List<dynamic>'
|
466
|
+
elsif value.all? { |item| item.is_a?(String) }
|
467
|
+
'List<String>'
|
468
|
+
elsif value.all? { |item| item.is_a?(Integer) }
|
469
|
+
'List<int>'
|
470
|
+
elsif value.all? { |item| item.is_a?(Float) }
|
471
|
+
'List<double>'
|
472
|
+
elsif value.all? { |item| item.is_a?(TrueClass) || item.is_a?(FalseClass) }
|
473
|
+
'List<bool>'
|
474
|
+
elsif value.all? { |item| item.is_a?(Hash) }
|
475
|
+
"List<#{class_prefix[0].upcase}#{class_prefix[1..-1]}Item>"
|
476
|
+
else
|
477
|
+
'List<dynamic>'
|
478
|
+
end
|
479
|
+
when Hash then "#{class_prefix[0].upcase}#{class_prefix[1..-1]}"
|
480
|
+
else 'dynamic'
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
def color_for(value)
|
485
|
+
"Color(0xFF#{value[1..-1]})"
|
486
|
+
end
|
487
|
+
|
488
|
+
def language_specific_null
|
489
|
+
'null'
|
490
|
+
end
|
491
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solara
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Malek Kamel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -236,12 +236,12 @@ files:
|
|
236
236
|
- solara/lib/core/doctor/validator/template/ios_template_validation_config.yml
|
237
237
|
- solara/lib/core/doctor/validator/template/template_validator.rb
|
238
238
|
- solara/lib/core/doctor/validator/validation_strategy.rb
|
239
|
-
- solara/lib/core/scripts/brand_config_generator.rb
|
240
239
|
- solara/lib/core/scripts/brand_config_manager.rb
|
241
240
|
- solara/lib/core/scripts/brand_exporter.rb
|
242
241
|
- solara/lib/core/scripts/brand_importer.rb
|
243
242
|
- solara/lib/core/scripts/brand_offboarder.rb
|
244
243
|
- solara/lib/core/scripts/brand_resources_manager.rb
|
244
|
+
- solara/lib/core/scripts/code_generator.rb
|
245
245
|
- solara/lib/core/scripts/directory_creator.rb
|
246
246
|
- solara/lib/core/scripts/file_manager.rb
|
247
247
|
- solara/lib/core/scripts/file_path.rb
|
@@ -356,7 +356,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
356
356
|
- !ruby/object:Gem::Version
|
357
357
|
version: '0'
|
358
358
|
requirements: []
|
359
|
-
rubygems_version: 3.5.
|
359
|
+
rubygems_version: 3.5.21
|
360
360
|
signing_key:
|
361
361
|
specification_version: 4
|
362
362
|
summary: Solara is a Ruby library that simplifies the management of white label apps
|
@@ -1,246 +0,0 @@
|
|
1
|
-
module Language
|
2
|
-
Kotlin = 'kotlin'
|
3
|
-
Swift = 'swift'
|
4
|
-
Dart = 'dart'
|
5
|
-
|
6
|
-
def self.all
|
7
|
-
[Kotlin, Swift, Dart]
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def init(language)
|
12
|
-
if Language.all.include?(language)
|
13
|
-
# Do something with the valid language
|
14
|
-
else
|
15
|
-
raise ArgumentError, "Invalid language. Please use one of: #{Language.all.join(', ')}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class BrandConfigGenerator
|
20
|
-
def initialize(config, output_dir, language, platform)
|
21
|
-
@output_dir = output_dir
|
22
|
-
@language = language
|
23
|
-
@platform = platform
|
24
|
-
@config = config
|
25
|
-
end
|
26
|
-
|
27
|
-
def generate
|
28
|
-
content = generate_config_content
|
29
|
-
FileManager.create_file_if_not_exist(@output_dir)
|
30
|
-
File.write(@output_dir, content)
|
31
|
-
Solara.logger.debug("Generated brand config #{@output_dir} for: #{@language}")
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def generate_config_content
|
37
|
-
content = "// Generated by Solara\n"
|
38
|
-
content += language_specific_imports
|
39
|
-
|
40
|
-
classes = []
|
41
|
-
generate_classes("BrandConfig", @config, classes)
|
42
|
-
|
43
|
-
classes.reverse_each do |class_content|
|
44
|
-
content += class_content
|
45
|
-
content += "\n"
|
46
|
-
end
|
47
|
-
|
48
|
-
content
|
49
|
-
end
|
50
|
-
|
51
|
-
def language_specific_imports
|
52
|
-
case @language
|
53
|
-
when 'dart'
|
54
|
-
"import 'package:flutter/material.dart';\n\n"
|
55
|
-
when 'kotlin'
|
56
|
-
"import android.graphics.Color\n\n"
|
57
|
-
when 'swift'
|
58
|
-
"import UIKit\n\n"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def generate_classes(class_name, config, classes)
|
63
|
-
content = class_declaration(class_name)
|
64
|
-
constructor_params = []
|
65
|
-
|
66
|
-
config.each do |key, value|
|
67
|
-
type = value_type(value, key)
|
68
|
-
content += property_declaration(key, type)
|
69
|
-
constructor_params << constructor_parameter(key, type)
|
70
|
-
end
|
71
|
-
|
72
|
-
content += constructor_declaration(class_name, constructor_params)
|
73
|
-
content += instance_declaration(class_name, config)
|
74
|
-
content += class_closing
|
75
|
-
|
76
|
-
classes << content
|
77
|
-
|
78
|
-
config.each do |key, value|
|
79
|
-
if value.is_a?(Hash)
|
80
|
-
nested_class_name = "#{key.capitalize}"
|
81
|
-
generate_classes(nested_class_name, value, classes)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def class_declaration(class_name)
|
87
|
-
case @language
|
88
|
-
when 'dart'
|
89
|
-
"class #{class_name} {\n"
|
90
|
-
when 'kotlin'
|
91
|
-
"data class #{class_name}(\n"
|
92
|
-
when 'swift'
|
93
|
-
"struct #{class_name} {\n"
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def property_declaration(key, type)
|
98
|
-
case @language
|
99
|
-
when 'dart'
|
100
|
-
" final #{type} #{key};\n"
|
101
|
-
when 'kotlin'
|
102
|
-
" val #{key}: #{type},\n"
|
103
|
-
when 'swift'
|
104
|
-
" let #{key}: #{type}\n"
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def constructor_parameter(key, type)
|
109
|
-
case @language
|
110
|
-
when 'dart'
|
111
|
-
"required this.#{key}"
|
112
|
-
when 'kotlin'
|
113
|
-
"val #{key}: #{type}"
|
114
|
-
when 'swift'
|
115
|
-
"#{key}: #{type}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def constructor_declaration(class_name, params)
|
120
|
-
case @language
|
121
|
-
when 'dart'
|
122
|
-
params.empty? ? "\n const #{class_name}();\n\n" : "\n const #{class_name}({#{params.join(', ')}});\n\n"
|
123
|
-
when 'kotlin'
|
124
|
-
") {\n"
|
125
|
-
when 'swift'
|
126
|
-
"\n init(#{params.join(', ')}) {\n#{params.map { |p| " self.#{p.split(':').first} = #{p.split(':').first}" }.join("\n")}\n }\n\n"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def instance_declaration(class_name, config)
|
131
|
-
case @language
|
132
|
-
when 'dart'
|
133
|
-
" static const #{class_name} instance = #{class_name}(\n#{config.map { |k, v| " #{k}: #{value_for(v, k, ' ')}" }.join(",\n")}\n );\n"
|
134
|
-
when 'kotlin'
|
135
|
-
"companion object {\n val instance = #{class_name}(\n#{config.map { |k, v| " #{k} = #{value_for(v, k, ' ')}" }.join(",\n")}\n )\n}\n"
|
136
|
-
when 'swift'
|
137
|
-
" static let shared = #{class_name}(\n#{config.map { |k, v| " #{k}: #{value_for(v, k, ' ')}" }.join(",\n")}\n )\n"
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def class_closing
|
142
|
-
@language == 'swift' ? "}\n" : "}\n"
|
143
|
-
end
|
144
|
-
|
145
|
-
def value_type(value, class_prefix)
|
146
|
-
case value
|
147
|
-
when String then language_specific_type('String')
|
148
|
-
when Integer then language_specific_type('Int')
|
149
|
-
when Float then language_specific_type('Double')
|
150
|
-
when TrueClass, FalseClass then language_specific_type('Bool')
|
151
|
-
when Array
|
152
|
-
if value.all? { |item| item.is_a?(String) }
|
153
|
-
language_specific_type('Array<String>')
|
154
|
-
elsif value.all? { |item| item.is_a?(Integer) }
|
155
|
-
language_specific_type('Array<Int>')
|
156
|
-
elsif value.all? { |item| item.is_a?(Float) }
|
157
|
-
language_specific_type('Array<Double>')
|
158
|
-
elsif value.all? { |item| item.is_a?(TrueClass) || item.is_a?(FalseClass) }
|
159
|
-
language_specific_type('Array<Bool>')
|
160
|
-
else
|
161
|
-
language_specific_type('Array<Any>')
|
162
|
-
end
|
163
|
-
when Hash then "#{class_prefix.capitalize}"
|
164
|
-
else
|
165
|
-
language_specific_type('Any')
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def language_specific_type(type)
|
170
|
-
case @language
|
171
|
-
when 'dart'
|
172
|
-
case type
|
173
|
-
when 'String' then 'String'
|
174
|
-
when 'Int' then 'int'
|
175
|
-
when 'Double' then 'double'
|
176
|
-
when 'Bool' then 'bool'
|
177
|
-
when 'Array<String>' then 'List<String>'
|
178
|
-
when 'Array<Int>' then 'List<int>'
|
179
|
-
when 'Array<Double>' then 'List<double>'
|
180
|
-
when 'Array<Bool>' then 'List<bool>'
|
181
|
-
when 'Array<Any>' then 'List<dynamic>'
|
182
|
-
when 'Any' then 'dynamic'
|
183
|
-
else type
|
184
|
-
end
|
185
|
-
when 'kotlin'
|
186
|
-
case type
|
187
|
-
when 'Bool' then 'Boolean'
|
188
|
-
when 'Array<String>' then 'List<String>'
|
189
|
-
when 'Array<Int>' then 'List<Int>'
|
190
|
-
when 'Array<Double>' then 'List<Double>'
|
191
|
-
when 'Array<Bool>' then 'List<Boolean>'
|
192
|
-
when 'Array<Any>' then 'List<Any>'
|
193
|
-
when 'Any' then 'Any'
|
194
|
-
else type
|
195
|
-
end
|
196
|
-
when 'swift'
|
197
|
-
type
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
def value_for(value, class_prefix, indent)
|
202
|
-
case value
|
203
|
-
when String
|
204
|
-
if value.start_with?('#') && value.length == 7 # Assume it's a color
|
205
|
-
color_for(value)
|
206
|
-
else
|
207
|
-
value.inspect
|
208
|
-
end
|
209
|
-
when Integer, Float
|
210
|
-
value.to_s
|
211
|
-
when TrueClass, FalseClass
|
212
|
-
value.to_s
|
213
|
-
when Array
|
214
|
-
array_items = value.map { |item| value_for(item, class_prefix, indent + ' ') }.join(', ')
|
215
|
-
"[\n#{indent} #{array_items}\n#{indent}]"
|
216
|
-
when Hash
|
217
|
-
"#{class_prefix.capitalize}.instance"
|
218
|
-
else
|
219
|
-
language_specific_null
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
def color_for(value)
|
224
|
-
case @language
|
225
|
-
when 'dart'
|
226
|
-
"Color(0xFF#{value[1..-1]})"
|
227
|
-
when 'kotlin'
|
228
|
-
"Color.parseColor(\"#{value}\")"
|
229
|
-
when 'swift'
|
230
|
-
"UIColor(red: #{hex_to_rgb(value[:red])}, green: #{hex_to_rgb(value[:green])}, blue: #{hex_to_rgb(value[:blue])}, alpha: 1.0)"
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
def hex_to_rgb(hex)
|
235
|
-
hex.to_i(16) / 255.0
|
236
|
-
end
|
237
|
-
|
238
|
-
def language_specific_null
|
239
|
-
case @language
|
240
|
-
when 'dart', 'swift'
|
241
|
-
'null'
|
242
|
-
when 'kotlin'
|
243
|
-
'null'
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|