circuitdata 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -29,7 +29,7 @@
29
29
  "description": "The name of the manufacturer"
30
30
  },
31
31
  "ipc-sm-840-class": {
32
- "type": "list",
32
+ "type": "string",
33
33
  "enum": ["T", "H"],
34
34
  "uom": null,
35
35
  "description": "Soldermask to meet IPC SM 840 Class."
@@ -180,12 +180,30 @@
180
180
  "thickness_min_micron": {
181
181
  "type": "number",
182
182
  "uom": ["um"],
183
- "description": "The minimum thickness in micrometer."
183
+ "description": "The minimum thickness."
184
184
  },
185
185
  "thickness_max_micron": {
186
186
  "type": "number",
187
187
  "uom": ["um"],
188
- "description": "The maximum thickness in micrometer."
188
+ "description": "The maximum thickness."
189
+ },
190
+ "presence_top_boolean": {
191
+ "type": "boolean",
192
+ "uom": null,
193
+ "description": "Indicates presence/capability on top"
194
+ },
195
+ "presence_bottom_boolean": {
196
+ "type": "boolean",
197
+ "uom": null,
198
+ "description": "Indicates presence/capability at bottom"
199
+ },
200
+ "numeric_single_or_range": {
201
+ "type": "string",
202
+ "pattern": "^[-+]?([0-9]*\\.[0-9]+|[0-9]+)$|^([0-9]*|[0-9].[0-9]*)\\.\\.\\.([0-9]|[0-9].[0-9]*)$"
203
+ },
204
+ "boolean_true_false_or_both": {
205
+ "type": "string",
206
+ "pattern": "^[t,T][r,R][u,U][e,E]$|^[f,F][a,A][l,L][s,S][e,E]$|^[t,T][r,R][u,U][e,E],.[f,F][a,A][l,L][s,S][e,E]$|^[f,F][a,A][l,L][s,S][e,E],.[t,T][r,R][u,U][e,E]$"
189
207
  },
190
208
  "range": {
191
209
  "type": "string",
@@ -201,64 +219,92 @@
201
219
  "elements": {
202
220
  "conductive_layer": {
203
221
  "count": {
204
- "type": "integer",
222
+ "type": "number",
205
223
  "uom": null,
206
- "description": "The number of conductive layers."
224
+ "description": "The number of conductive layers.",
225
+ "minimum": 1,
226
+ "maximum": 100,
227
+ "multipleOf": 1.0
207
228
  },
208
229
  "minimum_internal_track_width": {
209
230
  "type": "number",
210
- "uom": ["um"],
231
+ "uom": ["mm"],
211
232
  "description": "The minimum nominal width of conductors on internal/unplated layers (minimum track)."
212
233
  },
213
234
  "minimum_external_track_width": {
214
235
  "type": "number",
215
- "uom": ["um"],
236
+ "uom": ["mm"],
216
237
  "description": "The minimum nominal width of conductors on external/plated layers (minimum track). If only only one minimum track is present, is should be here."
217
238
  },
218
239
  "minimum_internal_spacing_width": {
219
240
  "type": "number",
220
- "uom": ["um"],
241
+ "uom": ["mm"],
221
242
  "description": "The minimum gap between two conductors on the internal layers."
222
243
  },
223
244
  "minimum_external_spacing_width": {
224
245
  "type": "number",
246
+ "uom": ["mm"],
225
247
  "description": "The minimum gap between two conductors on the external layers. If only one minimum gap is present, is should be here."
226
248
  },
227
249
  "external_base_copper_thickness": {
228
250
  "type": "number",
229
251
  "enum": [5.1, 8.5, 12, 17.1, 25.7, 34.3, 68.6, 102.9, 137.2, 171.5, 205.7, 240, 342.9, 480.1],
230
252
  "uom": ["um"],
231
- "description": "Finished base copper thickness following IPC Class on the up to two external layers in micrometer."
253
+ "description": "Finished base copper thickness following IPC Class on the up to two external layers."
232
254
  },
233
255
  "internal_base_copper_thickness": {
234
256
  "type": "number",
235
257
  "enum": [5.1, 8.5, 12, 17.1, 25.7, 34.3, 68.6, 102.9, 137.2, 171.5, 205.7, 240, 342.9, 480.1],
236
258
  "uom": ["um"],
237
- "description": "Finished base copper thickness following IPC Class on the internal layers in micrometer."
259
+ "description": "Finished base copper thickness following IPC Class on the internal layers."
238
260
  },
239
261
  "copper_foil_roughness": {
240
262
  "type": "string",
241
263
  "enum": ["S", "L", "V"],
242
- "uom": ["um"],
264
+ "enum_description": {
265
+ "S": "Standard profile",
266
+ "L": "Low profile",
267
+ "V": "Very low profile"
268
+ },
269
+ "uom": null,
243
270
  "description": "The roughness of the copper foil."
244
271
  },
245
272
  "copper_coverage_average": {
246
273
  "type": "number",
247
274
  "uom": ["%"],
248
- "description": "The average copper coverage of the board. UoM is percentage"
275
+ "description": "The average copper coverage of the board"
249
276
  }
250
277
  },
251
278
  "final_finish": {
252
279
  "finish": {
253
280
  "type": "string",
254
281
  "enum": ["none", "c_bare_copper", "isn", "iag", "enig", "enepig", "osp", "ht_osp", "g", "GS", "t_fused", "tlu_unfused", "dig", "gwb-1_ultrasonic", "gwb-2-thermosonic", "s_hasl", "b1_lfhasl"],
282
+ "enum_description": {
283
+ "none": "No final finish should be used",
284
+ "c_bare_copper": "AABUS",
285
+ "isn": "IPC-4554 Immersion Tin",
286
+ "iag": "IPC-4553 Immersion Silver",
287
+ "enig": "IPC-4552 Immersion Gold",
288
+ "enepig": "IPC-4556 ENEPIG",
289
+ "osp": "J-STD-003 Organic Solderability Preservative",
290
+ "ht_osp": "J-STD-003 High Temperature OSP",
291
+ "g": "ASTM-B-488 Gold for edge printed board connectors and areas not to be soldered",
292
+ "GS": "J-STD-003 Gold Electroplate on areas to be soldered",
293
+ "t_fused": "J-STD-003 Electrodeposited Tin-Lead (fused)",
294
+ "tlu_unfused": "J-STD-003 Electrodeposited Tin-Lead Unfused",
295
+ "dig": "J-STD-003 Direct Immersion Gold (Solderable Surface)",
296
+ "gwb-1_ultrasonic": "ASTM-B-488 Gold Electroplate for areas to be wire bonded (ultrasonic)",
297
+ "gwb-2-thermosonic": "ASTM-B-488 Gold Electroplate for areas to be wire bonded (thermosonic)",
298
+ "s_hasl": "J-STD-003_J-STD-006 Solder Coating over Bare Copper (HASL)",
299
+ "b1_lfhasl": "J-STD-003_J-STD-006 Lead-Free Solder Coating over Bare Copper (Lead-Free HASL, Lead free HASL)"
300
+ },
255
301
  "uom": null,
256
302
  "description": "The material/method/surface to be used in the finish."
257
303
  },
258
304
  "area": {
259
305
  "type": "number",
260
306
  "uom": ["dm2"],
261
- "description": "The area that the finish will cover, in square decimeter."
307
+ "description": "The area that the finish will cover"
262
308
  }
263
309
  },
264
310
  "soldermask": {
@@ -273,31 +319,11 @@
273
319
  "uom": null,
274
320
  "description": "The finish of the soldermask."
275
321
  },
276
- "min_thickness": {
277
- "type": "number",
278
- "uom": ["um"],
279
- "description": "The minimum thickness of the soldermask."
280
- },
281
- "max_thickness": {
282
- "type": "number",
283
- "uom": ["um"],
284
- "description": "The maximum thickness of the soldermask."
285
- },
286
322
  "material": {
287
323
  "type": "string",
288
324
  "uom": null,
289
325
  "description": "The name of a material that appears in the materials section"
290
326
  },
291
- "top": {
292
- "type": "boolean",
293
- "uom": null,
294
- "description": "Indicates soldermask presence/capability at top"
295
- },
296
- "bottom": {
297
- "type": "boolean",
298
- "uom": null,
299
- "description": "Indicates soldermask presence/capability at bottom"
300
- },
301
327
  "allow_touchups": {
302
328
  "type": "boolean",
303
329
  "uom": null,
@@ -309,16 +335,6 @@
309
335
  "type": "string",
310
336
  "uom": null,
311
337
  "description": "This describes the color based on the name of the color; white, yellow. If a specific color needs to be defined, this can be done custom -> colors section."
312
- },
313
- "top": {
314
- "type": "boolean",
315
- "uom": null,
316
- "description": "Indicates legend presence/capability at top"
317
- },
318
- "bottom": {
319
- "type": "boolean",
320
- "uom": null,
321
- "description": "Indicates legend presence/capability at bottom"
322
338
  }
323
339
  },
324
340
  "stiffener": {
@@ -350,16 +366,6 @@
350
366
  "uom": null,
351
367
  "description": "The total thickness of the coverlay"
352
368
  },
353
- "top": {
354
- "type": "boolean",
355
- "uom": null,
356
- "description": "Indicates coverlay presence/capability at top"
357
- },
358
- "bottom": {
359
- "type": "boolean",
360
- "uom": null,
361
- "description": "Indicates coverlay presence/capability at bottom"
362
- },
363
369
  "material": {
364
370
  "type": "string",
365
371
  "uom": null,
@@ -371,16 +377,6 @@
371
377
  "type": "integer",
372
378
  "uom": null,
373
379
  "description": ""
374
- },
375
- "top": {
376
- "type": "boolean",
377
- "uom": null,
378
- "description": "Indicates peelable mask presence/capability at top"
379
- },
380
- "bottom": {
381
- "type": "boolean",
382
- "uom": null,
383
- "description": "Indicates peelable mask presence/capability at bottom"
384
380
  }
385
381
  },
386
382
  "kapton_tape": {
@@ -388,40 +384,6 @@
388
384
  "type": "boolean",
389
385
  "uom": null,
390
386
  "description": "If alternative to DuPont™ Kapton® HN general-purpose film can be used"
391
- },
392
- "top": {
393
- "type": "boolean",
394
- "uom": null,
395
- "description": "Indicates kapton tape presence/capability at top"
396
- },
397
- "bottom": {
398
- "type": "boolean",
399
- "uom": null,
400
- "description": "Indicates kapton tape presence/capability at top"
401
- }
402
- },
403
- "conductive_carbon_print": {
404
- "top": {
405
- "type": "boolean",
406
- "uom": null,
407
- "description": "Indicates Conductive Carbon Print presence/capability at top"
408
- },
409
- "bottom": {
410
- "type": "boolean",
411
- "uom": null,
412
- "description": "Indicates Conductive Carbon Print presence/capability at bottom"
413
- }
414
- },
415
- "silver_print": {
416
- "top": {
417
- "type": "boolean",
418
- "uom": null,
419
- "description": "Indicates silver print presence/capability at top"
420
- },
421
- "bottom": {
422
- "type": "boolean",
423
- "uom": null,
424
- "description": "Indicates silver print presence/capability at bottom"
425
387
  }
426
388
  },
427
389
  "inner_packaging": {
@@ -630,34 +592,6 @@
630
592
  "description": "The weight of the array measured in grams."
631
593
  }
632
594
  },
633
- "inner_packaging": {
634
- "type_of_bag": {
635
- "type": "string",
636
- "enum": ["a", "b", "c", "d"],
637
- "uom": null,
638
- "description": "The material of the bag to be used"
639
- },
640
- "hic": {
641
- "type": "boolean",
642
- "uom": null,
643
- "description": "True to include a Humidity Indicator Card (HIC), False to not"
644
- },
645
- "esd": {
646
- "type": "boolean",
647
- "uom": null,
648
- "description": "True to indicate that packaging for ESD-sensitive required."
649
- },
650
- "desiccant": {
651
- "type": "boolean",
652
- "uom": null,
653
- "description": "True to indicate that a desiccant material is required."
654
- },
655
- "vacuum": {
656
- "type": "boolean",
657
- "uom": null,
658
- "description": "True to indicate that vacuum is needed for shrinkage - no heat rap or shrink rap allowed."
659
- }
660
- },
661
595
  "mechanical": {
662
596
  "edge_bevelling": {
663
597
  "type": "boolean",
@@ -779,6 +713,9 @@
779
713
  },
780
714
  "standards": {
781
715
  "type": "array",
716
+ "items": {
717
+ "type": "string"
718
+ },
782
719
  "uom": null,
783
720
  "description": "Possible values are the ones listed in the subelement \"standards\" but typical will be \"ul\" and \"rohs\". Separate by comma."
784
721
  },
@@ -925,35 +862,35 @@
925
862
  "type": "string",
926
863
  "enum": ["1", "2", "3", "3A", "3S", "3M"],
927
864
  "uom": null,
928
- "description": "Requirements according to IPC 6012 class."
865
+ "description": "Requirements according to IPC 6012 class"
929
866
  },
930
867
  "ipc_6013_class": {
931
868
  "type": "string",
932
869
  "enum": ["1", "2", "3"],
933
870
  "uom": null,
934
- "description": "Requirements according to IPC 6013 for flexible or rigid-flex boards."
871
+ "description": "Requirements according to IPC 6013 for flexible or rigid-flex boards"
935
872
  },
936
- "ipc_6018":{
873
+ "ipc_6018": {
937
874
  "type": "boolean",
938
875
  "uom": null,
939
- "description": "IPC-6018 Microwave End Product Board Inspection and Test."
876
+ "description": "IPC-6018 Microwave End Product Board Inspection and Test"
940
877
  }
941
878
  },
942
879
  "testing": {
943
880
  "netlist": {
944
881
  "type": "boolean",
945
882
  "uom": null,
946
- "description": "100% Netlist testing according to IPC-D-356, ODB++ or IPC2581."
883
+ "description": "100% Netlist testing according to IPC-D-356, ODB++ or IPC2581"
947
884
  },
948
885
  "allow_generate_netlist": {
949
886
  "type": "boolean",
950
887
  "uom": null,
951
- "description": "Allow Netlist to be generated from Gerber or other file format if needed."
888
+ "description": "Allow Netlist to be generated from Gerber or other file format if needed"
952
889
  },
953
890
  "hipot": {
954
891
  "type": "boolean",
955
892
  "uom": null,
956
- "description": " HiPot Test (Dielectric Withstanding Voltage Test)."
893
+ "description": "HiPot Test (Dielectric Withstanding Voltage Test)"
957
894
  },
958
895
  "4_wire": {
959
896
  "type": "boolean",
@@ -1136,4 +1073,4 @@
1136
1073
  }
1137
1074
  }
1138
1075
  }
1139
- }
1076
+ }
@@ -0,0 +1,176 @@
1
+ class Circuitdata::Tools
2
+ def initialize()
3
+ require 'json'
4
+ @schema_path = File.join(File.dirname(__FILE__), 'schema_files/v1/ottp_circuitdata_schema.json')
5
+ @definitions_path = File.join(File.dirname(__FILE__), 'schema_files/v1/ottp_circuitdata_schema_definitions.json')
6
+ @ra = {}
7
+ end
8
+
9
+ def update_ra(type, key, value)
10
+ parsed_elements = Circuitdata.read_json(@definitions_path)[2]
11
+ unless @ra[:structured].has_key? key
12
+ @ra[:structured][key] = {
13
+ :type => value[:type],
14
+ :elements => {},
15
+ :name => nil,
16
+ :description => nil,
17
+ :aliases => nil
18
+ }
19
+ @ra[:structured][key][:name] = value[:name] if value.has_key? :name
20
+ @ra[:structured][key][:description] = value[:decription] if value.has_key? :decription
21
+ if value.has_key? :aliases
22
+ @ra[:structured][key][:aliases] = value[:aliases] unless value[:aliases] == ""
23
+ end
24
+ end
25
+ if value.has_key? :properties
26
+ value[:properties].each do |skey, svalue|
27
+ unless @ra[:structured][key][:elements].has_key? skey
28
+ @ra[:structured][key][:elements][skey] = {
29
+ :in_product_generic => false,
30
+ :in_product_stackup => false,
31
+ :in_profile_default => false,
32
+ :in_profile_enforced => false,
33
+ :in_profile_restricted => false,
34
+ :in_capabilities => false,
35
+ :type => nil,
36
+ :arrayitems => nil,
37
+ :enum => nil,
38
+ :description => nil,
39
+ :uom => nil,
40
+ :minimum => nil,
41
+ :maximum => nil,
42
+ :in_profile_restricted_regex => nil,
43
+ :in_capabilities_regex => nil
44
+ }
45
+ if svalue.has_key? :$ref
46
+ elements = svalue[:$ref].split('/')
47
+ if elements.length < 5
48
+ element = parsed_elements[elements[1].to_sym][elements[2].to_sym][elements[3].to_sym]
49
+ else
50
+ element = parsed_elements[elements[1].to_sym][elements[2].to_sym][elements[3].to_sym][elements[4].to_sym]
51
+ end
52
+ else
53
+ element = nil
54
+ [:rigid_conductive_layer, :flexible_conductive_layer].include? key.to_sym ? newkey = :conductive_layer : newkey = key.to_sym
55
+ if parsed_elements[:definitions][:elements].has_key? newkey
56
+ element = parsed_elements[:definitions][:elements][newkey][skey.to_sym] if parsed_elements[:definitions][:elements][newkey].has_key? skey.to_sym
57
+ end
58
+ end
59
+ unless element.nil?
60
+ if element.has_key? :type
61
+ @ra[:structured][key][:elements][skey][:type] = element[:type]
62
+ if element[:type] == "array"
63
+ if element.has_key? :items and element[:items].has_key? :type
64
+ @ra[:structured][key][:elements][skey][:arrayitems] == element[:items][:type]
65
+ end
66
+ end
67
+ end
68
+ @ra[:structured][key][:elements][skey][:enum] = element[:enum] if element.has_key? :enum
69
+ @ra[:structured][key][:elements][skey][:description] = element[:description] if element.has_key? :description
70
+ @ra[:structured][key][:elements][skey][:uom] = element[:uom] if element.has_key? :uom
71
+ @ra[:structured][key][:elements][skey][:minimum] = element[:minimum] if element.has_key? :minimum
72
+ @ra[:structured][key][:elements][skey][:maximum] = element[:maximum] if element.has_key? :maximum
73
+ end
74
+ else
75
+ if [:in_profile_restricted, :in_capabilities].include? type
76
+ case @ra[:structured][key][:elements][skey][:type]
77
+ when *["number", "integer", "boolean", "string"]
78
+ @ra[:structured][key][:elements][skey][:type] = "number" if @ra[:structured][key][:elements][skey][:type] == "integer"
79
+ unless ( svalue.has_key? :type and svalue[:type] == "array" ) and ( svalue.has_key? :items and svalue[:items].has_key? :type and svalue[:items][:type] == @ra[:structured][key][:elements][skey][:type])
80
+ (@ra[:errors][type][key] ||= {})[skey] = "Type is #{@ra[:structured][key][:elements][skey][:type]}, wrong check"
81
+ end
82
+ when "array"
83
+ unless svalue.has_key? :type and svalue[:type] == "array"
84
+ (@ra[:errors][type][key] ||= {})[skey] = "Type is #{@ra[:structured][key][:elements][skey][:type]}, wrong check"
85
+ end
86
+ else
87
+ puts "unknown type #{@ra[:structured][key][:elements][skey][:type]} in #{key}, #{skey}"
88
+ end
89
+ end
90
+ end
91
+ @ra[:structured][key][:elements][skey][type] = true
92
+ end
93
+ end
94
+ end
95
+
96
+ def create_structure
97
+ @ra[:structured] = {}
98
+ @ra[:errors] = {:in_profile_restricted => {}, :in_capabilities => {}}
99
+ parsed_schema = Circuitdata.read_json(@schema_path)[2]
100
+ parsed_schema[:properties][:open_trade_transfer_package][:properties][:products][:patternProperties]["^(?!generic$).*".to_sym][:properties][:printed_circuits_fabrication_data][:properties].each do |key, value|
101
+ self.update_ra(:in_product_generic, key, value)
102
+ end
103
+ ["defaults", "restricted", "enforced"].each do |sym|
104
+ parsed_schema[:properties][:open_trade_transfer_package][:properties][:profiles][:properties][sym.to_sym][:properties][:printed_circuits_fabrication_data][:properties].each do |key, value|
105
+ case sym
106
+ when "defaults"
107
+ t = :in_profile_default
108
+ when "restricted"
109
+ t = :in_profile_restricted
110
+ when "enforced"
111
+ t = :in_profile_enforced
112
+ end
113
+ self.update_ra(t, key, value)
114
+ end
115
+ end
116
+ parsed_schema[:properties][:open_trade_transfer_package][:properties][:capabilities][:properties][:printed_circuits_fabrication_data][:properties].each do |key, value|
117
+ self.update_ra(:in_capabilities, key, value)
118
+ end
119
+ @ra[:structured].sort.to_h
120
+ @ra[:structured].delete(:version)
121
+ return @ra
122
+ end
123
+
124
+ def create_documentation(ra)
125
+ ra[:documentation] = "## Elements and tags\n"
126
+ ra[:structured].each do |element_key, element_value|
127
+ ra[:documentation] += "[#{element_key}](##{element_key.to_s.downcase.tr(" ", "-")})\n"
128
+ element_value[:elements].each do |e_key, e_value|
129
+ ra[:documentation] += "* #{e_key}\n"
130
+ end
131
+ ra[:documentation] += "\n"
132
+ end
133
+ ra[:structured].each do |element_key, element_value|
134
+ ra[:documentation] += "### #{element_key}\n"
135
+ ra[:documentation] += "Name: #{element_value[:name]}\n" unless element_value[:name].nil?
136
+ ra[:documentation] += "Aliases: #{element_value[:aliases]}\n" unless element_value[:aliases].nil?
137
+ ra[:documentation] += "#{element_value[:description]}\n" unless element_value[:description].nil?
138
+ ra[:documentation] += "\n"
139
+ element_value[:elements].each do |e_key, e_value|
140
+ ra[:documentation] += "#### #{e_key}\n"
141
+ ra[:documentation] += "Aliases: #{e_value[:aliases]}\n" unless e_value[:aliases].nil?
142
+ ra[:documentation] += "#{e_value[:description]}\n" unless e_value[:description].nil?
143
+ ra[:documentation] += "Unit of Measure: #{e_value[:uom][0]}\n" unless e_value[:uom].nil?
144
+ unless e_value[:type].nil?
145
+ if e_value[:type] == "array"
146
+ if e_value[:arrayitems].nil?
147
+ ra[:documentation] += "Type: #{e_value[:type].capitalize} of unknown type\n"
148
+ else
149
+ ra[:documentation] += "Type: #{e_value[:type].capitalize} of #{e_value[:arrayitems].capitalize}\n"
150
+ end
151
+ else
152
+ ra[:documentation] += "Type: #{e_value[:type].capitalize}\n"
153
+ end
154
+ end
155
+ if e_value.has_key? :enum and not e_value[:enum].nil?
156
+ ra[:documentation] += "Use one of these values:\n"
157
+ e_value[:enum].each do |ev|
158
+ ra[:documentation] += "* #{ev}\n"
159
+ end
160
+ end
161
+ ra[:documentation] += "Use in:\n"
162
+ e_value[:in_product_generic] ? ra[:documentation] += "* *Generic product section: Allowed*\n" : ra[:documentation] += "* *Generic product section: Disallowed*\n"
163
+ e_value[:in_product_stackup] ? ra[:documentation] += "* *Stackup product section: Allowed*\n" : ra[:documentation] += "* *Gtackup product section: Disallowed*\n"
164
+ e_value[:in_profile_default] ? ra[:documentation] += "* *Profile defaults section: Allowed*\n" : ra[:documentation] += "* *Profile defaults section: Disallowed*\n"
165
+ e_value[:in_profile_enforced] ? ra[:documentation] += "* *Profile enforced section: Allowed*\n" : ra[:documentation] += "* *Profile enforced section: Disallowed*\n"
166
+ e_value[:in_profile_restricted] ? ra[:documentation] += "* *Profile restricted section: Allowed*\n" : ra[:documentation] += "* *Profile restricted section: Disallowed*\n"
167
+ ra[:documentation] += "* - Value in restricted section must match regex #{e_value[:in_profile_restricted_regex]}\n" unless e_value[:in_profile_restricted_regex].nil?
168
+ e_value[:in_capabilities] ? ra[:documentation] += "* *Capabilites section: Allowed*\n" : ra[:documentation] += "* *Capabilities section: Disallowed*\n"
169
+ ra[:documentation] += "* - Value in capabilites section must match regex #{e_value[:in_capabilities_regex]}\n" unless e_value[:in_capabilities_regex].nil?
170
+ ra[:documentation] += "\n"
171
+ end
172
+ end
173
+ puts ra[:documentation]
174
+ end
175
+
176
+ end