class_from_son 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 53b0a1472f3977c2b06e85680263efdd69178894
4
+ data.tar.gz: 70b7f897adb5ee1b4ead08b90517eb2d1d412dad
5
+ SHA512:
6
+ metadata.gz: da824ddb560f2d64d476b3602d6e8c24c59f62ee62a367bf2b532813877d5151fdc45daae57ccc90aea77b3e9acf04054c1218e6fa5b60a4da284ba8ae9728f8
7
+ data.tar.gz: 640658471d76f8fa525f8057d6dc7a7159a56c093f498f165d1f570f9da6c0dce11f1e9c32ac09d0b016a2dcc2fd5d6a9575c58a481a3aae17a0ac9145b2173e
data/LICENCE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 Richard Morrisby
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.txt ADDED
@@ -0,0 +1,43 @@
1
+ This gem will attempt to generate code of a class of an object representing the contents of a Serialised-Object-Notation (SON) string (or file). E.g. it will generate code of a object's class from some JSON.
2
+
3
+ The intention with this gem is to provide assistance with creating code that can translate JSON (or other SON) into a proper Object.
4
+
5
+ If the SON looks to have a nested object structure (e.g. a Contact object looks to hold a PhoneNumber object) then multiple classes will be generated.
6
+
7
+ Limitations :
8
+
9
+ SON : will only process JSON
10
+ Code : will only generate Ruby or Java
11
+
12
+ Usage : require the gem, then invoke as follows :
13
+
14
+ ClassFromSON.generate_from_file :ruby, a_file.json
15
+
16
+ or
17
+
18
+ ClassFromSON.generate :ruby, my_json_string, :json
19
+
20
+
21
+ Method parameter explanations :
22
+
23
+ # Will generate classes from a SON file
24
+ # Regardless of whether or not files are written, this will return an array of hashes; each hash represents a file, with two keys : :name for filename (without extension), and :contents for file contents
25
+ #
26
+ # dest_lang is symbol
27
+ # file is filename & path
28
+ # source_lang is symbol or nil (if nil, source language will be determined from the file extension)
29
+ # make_file flag defaults to true; set to false if you do not want files to be created by this method
30
+ # force_file flag is false; set to true if you wish to overwrite matching destination files (use with caution!)
31
+ def ClassFromSON.generate_from_file(dest_lang, file, source_lang, make_file = true, force_file = false)
32
+
33
+
34
+
35
+ # Will generate classes from a SON string
36
+ # Regardless of whether or not files are written, this will return an array of hashes; each hash represents a file, with two keys : :name for filename (without extension), and :contents for file contents
37
+ #
38
+ # dest_lang is symbol
39
+ # file is filename & path
40
+ # source_lang is symbol
41
+ # make_file flag defaults to true; set to false if you do not want files to be created by this method
42
+ # force_file flag is false; set to true if you wish to overwrite matching destination files (use with caution!)
43
+ def ClassFromSON.generate(dest_lang, source, source_lang, make_file = true, force_file = false)
data/build.rb ADDED
@@ -0,0 +1,51 @@
1
+ # Simple build script
2
+ # Deletes the existing gem file (if present)
3
+ # Uninstalls the gem
4
+ # Builds the gem
5
+ # Installs the new gem
6
+
7
+ # Note : when calling system (or backticks, etc.) th enew process starts at the system default, not the current working directory.
8
+ # Therefore we need to use a syntax of : system "cd #{gem_dir} && #{i_cmd}"
9
+
10
+ # Run from this directory!
11
+
12
+ gem_dir = Dir.getwd
13
+
14
+ # Delete existing .gem files in the dir
15
+
16
+ gemfiles = Dir.entries(gem_dir).collect {|q| q if q =~ /.gem$/}.compact
17
+
18
+ gemfiles.each do |q|
19
+ File.delete q
20
+ puts "Deleted #{q}"
21
+ end
22
+
23
+ gemfiles = Dir.entries(gem_dir).collect {|q| q if q =~ /.gem$/}.compact
24
+ raise "Gem has not been deleted" unless gemfiles.size == 0
25
+
26
+ # Uninstall, build, install
27
+ gemspecs = Dir.entries(gem_dir).collect {|q| q if q =~ /.gemspec$/}.compact
28
+
29
+ raise "Did not find a .gemspec in #{gem_dir}" if gemspecs.size < 1
30
+ raise "Found more than one .gemspec in #{gem_dir}" if gemspecs.size > 1
31
+
32
+ gemspec = gemspecs[0]
33
+
34
+ gemname = File.basename(gemspec, File.extname(gemspec))
35
+
36
+ u_cmd = "gem uninstall #{gemname}"
37
+ system u_cmd
38
+
39
+ b_cmd = "gem build #{gemspec}"
40
+ system "cd #{gem_dir} && #{b_cmd}"
41
+
42
+ gemfiles = Dir.entries(gem_dir).collect {|q| q if q =~ /.gem$/}.compact
43
+ raise "Gem was not built" unless gemfiles.size == 1
44
+
45
+ gemfile = gemfiles[0]
46
+ raise "Gem file is not for the expected gem, expected a #{gemname} gem but found #{gemfile}" unless gemfile =~ /^#{gemname}/
47
+
48
+ i_cmd = "gem install #{gemfile}"
49
+ system "cd #{gem_dir} && #{i_cmd}"
50
+
51
+ puts "Gem #{gemname} built & installed"
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'class_from_son'
3
+ s.version = '0.1.0'
4
+ s.licenses = ['MIT']
5
+ s.summary = "Generates classes from SON (e.g. JSON)"
6
+ s.description = "This gem will attempt to generate code of a class of an object representing the contents of a Serialised-Object-Notation (SON) string (or file). E.g. it will generate code of a object's class from some JSON."
7
+ s.authors = ["Richard Morrisby"]
8
+ s.email = 'rmorrisby@gmail.com'
9
+ s.files = ["lib/class_from_SON.rb"]
10
+ s.homepage = 'https://rubygems.org/gems/class_from_son'
11
+ s.required_ruby_version = '>=1.9'
12
+ s.files = Dir['**/**']
13
+ s.test_files = Dir["test/test*.rb"]
14
+ s.has_rdoc = true
15
+ end
@@ -0,0 +1,413 @@
1
+ require "json"
2
+ require "more-ruby"
3
+
4
+ # A utility to convert an input file of string-object notation, e.g. JSON, XML, YAML, and generate code that looks like a class of your desired language
5
+
6
+ class ClassFromSON
7
+
8
+ @@target_languages = [:java, :ruby]
9
+ @@input_modes = [:json]
10
+
11
+ def error(message)
12
+ puts "ERROR : #{message}"
13
+ end
14
+
15
+ def error_and_exit(message)
16
+ puts "ERROR : #{message}"
17
+ exit
18
+ end
19
+
20
+ def self.error_and_exit(message)
21
+ puts "ERROR : #{message}"
22
+ exit
23
+ end
24
+
25
+ def warn(message)
26
+ puts "WARN : #{message}"
27
+ end
28
+
29
+ def convert_ruby_type_to_type(type, value_types = [])
30
+ # puts "#{__method__} called with type '#{type.inspect}', #{type.class}, #{value_types.inspect}"
31
+ # Because type is an instance of Class, we need to compare names, so convert to String
32
+ # Use .to_s instead of .name to cope with custom types (i.e. types that are themselves new classes to be generated)
33
+ case type.to_s
34
+ when "Fixnum", "Integer"
35
+ converted = convert_fixnum_to_type
36
+ when "Float"
37
+ converted = convert_float_to_type
38
+ when "String"
39
+ converted = convert_string_to_type
40
+ when "TrueClass", "FalseClass"
41
+ converted = convert_boolean_to_type
42
+ when "Array"
43
+ converted = convert_array_to_type(value_types)
44
+ when "Hash"
45
+ converted = convert_hash_to_type(value_types)
46
+ when "NilClass" # default nil to String
47
+ converted = convert_string_to_type
48
+ else
49
+ converted = convert_custom_class_type(type)
50
+ end
51
+ # puts "Converted '#{type.inspect}' to #{converted}"
52
+ converted
53
+ end
54
+
55
+ # Translate "Fixnum" into the desired output language
56
+ def convert_fixnum_to_type
57
+ case @language
58
+ when :java
59
+ return "int"
60
+ else
61
+ error_and_exit "Could not convert to output language #{@language}"
62
+ end
63
+ end
64
+
65
+ # Translate "Fixnum" into the desired output language
66
+ def convert_float_to_type
67
+ case @language
68
+ when :java
69
+ return "float"
70
+ else
71
+ error_and_exit "Could not convert to output language #{@language}"
72
+ end
73
+ end
74
+
75
+ # Translate "Fixnum" into the desired output language
76
+ def convert_boolean_to_type
77
+ case @language
78
+ when :java
79
+ return "boolean"
80
+ else
81
+ error_and_exit "Could not convert to output language #{@language}"
82
+ end
83
+ end
84
+
85
+ # Translate "String" into the desired output language
86
+ def convert_string_to_type
87
+ case @language
88
+ when :java
89
+ return "String"
90
+ else
91
+ error_and_exit "Could not convert to output language #{@language}"
92
+ end
93
+ end
94
+
95
+ # Translate "Array" into the desired output language
96
+ # Also needs the 'value types', i.e. what type is this array? A list of strings? A list of ints?
97
+ def convert_array_to_type(value_types)
98
+ error_and_exit "Detected an array, but could not determine the type of its children; found #{value_types.size} possibilities" unless value_types.size == 1
99
+ case @language
100
+ when :java
101
+ return "List<#{convert_ruby_type_to_type(value_types[0])}>"
102
+ else
103
+ error_and_exit "Could not convert to output language #{@language}"
104
+ end
105
+ end
106
+
107
+ # Translate "Hash" into the desired output language
108
+ # Also needs the 'value types', i.e. what type is this hash? A map of strings to booleans? A map of ints to strings?
109
+ def convert_hash_to_type(value_types)
110
+ error_and_exit "Detected a hash, but could not determine the type of its keys and values; found #{value_types.size} possibilities" unless value_types.size == 2
111
+ case @language
112
+ when :java
113
+ return "HashMap<#{convert_ruby_type_to_type(value_types[0])}, #{convert_ruby_type_to_type(value_types[1])}>"
114
+ else
115
+ error_and_exit "Could not convert to output language #{@language}"
116
+ end
117
+ end
118
+
119
+ # Returns code representing the start of the class
120
+ def convert_custom_class_type(type)
121
+ case @language
122
+ when :java, :ruby
123
+ return type.capitalize_first_letter_only
124
+ else
125
+ error_and_exit "Could not convert to output language #{@language}"
126
+ end
127
+ end
128
+
129
+ def generate_top_level_name
130
+ case @language
131
+ when :java
132
+ return "generatedFrom#{@mode.capitalize}"
133
+ when :ruby
134
+ return "generated_from_#{@mode}"
135
+ else
136
+ error_and_exit "Could not convert to output language #{@language}"
137
+ end
138
+ end
139
+
140
+ # Returns an appropriately-formatted classname for the given name
141
+ def generate_classname(name)
142
+ case @language
143
+ when :java, :ruby
144
+ return name.capitalize_first_letter_only
145
+ else
146
+ error_and_exit "Could not convert to output language #{@language}"
147
+ end
148
+ end
149
+
150
+ # Returns an appropriately-formatted filename for the given name
151
+ def generate_filename(name)
152
+ case @language
153
+ when :java
154
+ return name.capitalize_first_letter_only + @extension
155
+ when :ruby
156
+ return name.downcase + @extension # TODO convert camelcase to underbarised case
157
+ else
158
+ error_and_exit "Could not convert to output language #{@language}"
159
+ end
160
+ end
161
+
162
+ def set_file_extension_for_language
163
+ case @language
164
+ when :java
165
+ @extension = ".java"
166
+ when :ruby
167
+ @extension = ".rb"
168
+ else
169
+ error_and_exit "Could not convert to output language #{@language}"
170
+ end
171
+ end
172
+
173
+ # Returns code representing the start of the class
174
+ def generate_class_start(name)
175
+ case @language
176
+ when :java
177
+ start = "public class #{convert_custom_class_type(name)} {"
178
+ when :ruby
179
+ start = "class #{convert_custom_class_type(name)}"
180
+ else
181
+ error_and_exit "Could not convert to output language #{@language}"
182
+ end
183
+ start
184
+ end
185
+
186
+ # Returns code representing the end of the class
187
+ def generate_class_end
188
+ case @language
189
+ when :java
190
+ class_end = "}"
191
+ when :ruby
192
+ class_end = "end"
193
+ else
194
+ error_and_exit "Could not convert to output language #{@language}"
195
+ end
196
+ class_end
197
+ end
198
+
199
+ def generate_getter_and_setter(type, name)
200
+ lines = []
201
+ case @language
202
+ when :java
203
+ class_name = convert_custom_class_type(name)
204
+ lines << "\t"
205
+ lines << "\tpublic #{type} get#{class_name}() {"
206
+ lines << "\t\treturn #{name};"
207
+ lines << "\t}"
208
+ lines << "\t"
209
+ lines << "\tpublic void set#{class_name}(#{type} #{name}) {"
210
+ lines << "\t\tthis.#{name} = #{name};"
211
+ lines << "\t}"
212
+ else
213
+ error_and_exit "Could not convert to output language #{@language}"
214
+ end
215
+ lines
216
+ end
217
+
218
+ # Returns code representing each of the supplied attributes
219
+ def generate_code_from_attributes(attributes)
220
+ case @language
221
+ when :java
222
+ return generate_java_code_from_attributes(attributes)
223
+ when :ruby
224
+ return generate_ruby_code_from_attributes(attributes)
225
+ else
226
+ error_and_exit "Could not convert to output language #{@language}"
227
+ end
228
+ end
229
+
230
+ # Returns Java code representing each of the supplied attributes
231
+ def generate_java_code_from_attributes(attributes)
232
+ code = []
233
+ # Instance variables
234
+ attributes.each do |att|
235
+ code << "\tpublic #{convert_ruby_type_to_type(att[:type], att[:value_types])} #{att[:name]};"
236
+ end
237
+
238
+ #TODO constructor
239
+
240
+ # Getters & setters
241
+ attributes.each do |att|
242
+ code << generate_getter_and_setter(convert_ruby_type_to_type(att[:type], att[:value_types]), att[:name])
243
+ end
244
+ code
245
+ end
246
+
247
+ # Returns Ruby code representing each of the supplied attributes
248
+ def generate_ruby_code_from_attributes(attributes)
249
+ code = []
250
+ names = []
251
+ attributes.each {|att| names << att[:name]}
252
+
253
+ # Instance variables
254
+ names.each do |name|
255
+ code << "\tattr_accessor #{name.to_sym.inspect}"
256
+ end
257
+ code << "" # An empty string is enough to trigger a newline
258
+
259
+ # Constructor
260
+ code << "\tdef initialize(#{names.join(", ")})"
261
+ names.each do |name|
262
+ code << "\t\t@#{name} = #{name}"
263
+ end
264
+ code << "\tend"
265
+
266
+ code
267
+ end
268
+
269
+ # From the supplied hash, generates code representing a class in the desired language (as a string)
270
+ # Returns an array of hashes; each hash represents a file, with two keys : :name for filename (without extension), and :contents for file contents
271
+ def generate_output_classes(hash, top_level_classname = nil)
272
+ classname = generate_classname(top_level_classname)
273
+ filename = generate_filename(classname)
274
+ files = []
275
+ this_file = {:name => classname, :name_with_ext => filename}
276
+ lines = []
277
+ lines << generate_class_start(classname)
278
+ attributes = [] # array of hashes; keys => :name, :type, :value_types # :type, :value_types ([]) are initially kept as Ruby class names
279
+
280
+ hash.each_pair do |k, v|
281
+ attribute = {:name => k}
282
+ if v.class == Array
283
+ attribute[:type] = Array
284
+ if v[0].class == Hash
285
+ new_files = generate_output_classes(v[0], k)
286
+ attribute[:value_types] = [new_files[0][:name]]
287
+ files += new_files
288
+ else
289
+ # Array only contains primitives, not objects
290
+ attribute[:value_types] = [v[0].class]
291
+ end
292
+ elsif v.class == Hash
293
+ new_files = generate_output_classes(v, k)
294
+ attribute[:type] = new_files[0][:name]
295
+ files += new_files
296
+ else
297
+ attribute[:type] = v.class
298
+ end
299
+ attributes << attribute
300
+ end
301
+
302
+ lines << generate_code_from_attributes(attributes)
303
+ lines << generate_class_end
304
+ lines.flatten!
305
+ this_file[:contents] = lines.join("\n")
306
+ files.insert(0, this_file)
307
+ files
308
+ end
309
+
310
+ ###################################################################################################
311
+ # The Generator
312
+ ###################################################################################################
313
+
314
+ # Will generate classes from a SON file
315
+ # Regardless of whether or not files are written, this will return an array of hashes; each hash represents a file, with two keys : :name for filename (without extension), and :contents for file contents
316
+ #
317
+ # dest_lang is symbol
318
+ # file is filename & path
319
+ # source_lang is symbol or nil (if nil, source language will be determined from the file extension)
320
+ # make_file flag defaults to true; set to false if you do not want files to be created by this method
321
+ # force_file flag is false; set to true if you wish to overwrite matching destination files (use with caution!)
322
+ def ClassFromSON.generate_from_file(dest_lang, file, source_lang = nil, make_file = true, force_file = false)
323
+
324
+ error_and_exit "Could not locate file #{file}" unless File.exists?(file)
325
+
326
+ source_lang ||= File.extname(file).gsub(".", "")
327
+ source = File.readlines(file).join
328
+ ClassFromSON.generate(dest_lang, source, source_lang, make_file, force_file)
329
+
330
+ # o = ClassFromSON.new
331
+ # o.generate(dest_lang, source, source_lang, make_file)
332
+ end
333
+
334
+ # Will generate classes from a SON string
335
+ # Regardless of whether or not files are written, this will return an array of hashes; each hash represents a file, with two keys : :name for filename (without extension), and :contents for file contents
336
+ #
337
+ # dest_lang is symbol
338
+ # file is filename & path
339
+ # source_lang is symbol
340
+ # make_file flag defaults to true; set to false if you do not want files to be created by this method
341
+ # force_file flag is false; set to true if you wish to overwrite matching destination files (use with caution!)
342
+ def ClassFromSON.generate(dest_lang, source, source_lang, make_file = true, force_file = false)
343
+ o = ClassFromSON.new
344
+ o.generate(dest_lang, source, source_lang, make_file, force_file)
345
+ end
346
+
347
+ # Will generate classes from a SON string.
348
+ # Regardless of whether or not files are written, this will return an array of hashes; each hash represents a file, with two keys : :name for filename (without extension), and :contents for file contents
349
+ #
350
+ # dest_lang is symbol
351
+ # file is filename & path
352
+ # source_lang is symbol
353
+ # make_file flag defaults to true; set to false if you do not want files to be created by this method
354
+ # force_file flag is false; set to true if you wish to overwrite matching destination files (use with caution!)
355
+ def generate(dest_lang, source, source_lang, make_file = true, force_file = false)
356
+
357
+ error_and_exit "Please supply first argument as a Symbol" unless dest_lang.class == Symbol
358
+
359
+ @language = dest_lang
360
+ if @@target_languages.include?(@language)
361
+ # may proceed
362
+ # TODO other target languages, e.g. C#, Python
363
+ else
364
+ error_and_exit "Cannot generate language #{@language}; can only generate #{@@target_languages.join(", ")}"
365
+ end
366
+ @extension = set_file_extension_for_language
367
+
368
+ @mode = source_lang.to_sym
369
+ if @@input_modes.include?(@mode)
370
+ # may proceed
371
+ else
372
+ error_and_exit "Cannot parse input language #{@mode}; can only parse #{@@input_modes.join(", ")}"
373
+ end
374
+
375
+ case @mode
376
+ when :json
377
+ hash = JSON.parse(source)
378
+ # TODO other input languages, e.g. XML, YAML
379
+ else
380
+ error_and_exit "Cannot parse mode #{@mode}"
381
+ end
382
+
383
+ # If we have read in an array instead of a hash, then take the first element of the array
384
+ # This assumes that each element in this top-level array has the same structure, which is reasonable
385
+ if hash.class == Array && hash.size > 0
386
+ hash = hash.shift
387
+ end
388
+
389
+ error_and_exit "Input file did not have a hash / map of key-value pairs; could not parse" unless hash.class == Hash
390
+
391
+ error_and_exit "Input file hash / map was empty" if hash.empty?
392
+
393
+ top_level_classname = generate_top_level_name
394
+ output_classes = generate_output_classes(hash, top_level_classname).flatten # returns an array
395
+
396
+ if make_file
397
+ output_classes.each do |out|
398
+ name = out[:name_with_ext]
399
+ contents = out[:contents]
400
+ unless force_file
401
+ error_and_exit "Want to generate output file #{name}, but that file already exists" if File.exists?(name)
402
+ end
403
+ File.open(name, "w+") do |f|
404
+ f.puts contents
405
+ end
406
+ puts "Wrote out file #{name}"
407
+ end
408
+
409
+ puts "Please inspect generated code files and adjust names and types accordingly"
410
+ end
411
+ output_classes
412
+ end
413
+ end
data/test/Address.java ADDED
@@ -0,0 +1,38 @@
1
+ public class Address {
2
+ public String streetAddress;
3
+ public String city;
4
+ public String state;
5
+ public String postalCode;
6
+
7
+ public String getStreetAddress() {
8
+ return streetAddress;
9
+ }
10
+
11
+ public void setStreetAddress(String streetAddress) {
12
+ this.streetAddress = streetAddress;
13
+ }
14
+
15
+ public String getCity() {
16
+ return city;
17
+ }
18
+
19
+ public void setCity(String city) {
20
+ this.city = city;
21
+ }
22
+
23
+ public String getState() {
24
+ return state;
25
+ }
26
+
27
+ public void setState(String state) {
28
+ this.state = state;
29
+ }
30
+
31
+ public String getPostalCode() {
32
+ return postalCode;
33
+ }
34
+
35
+ public void setPostalCode(String postalCode) {
36
+ this.postalCode = postalCode;
37
+ }
38
+ }
@@ -0,0 +1,74 @@
1
+ public class GeneratedFromJson {
2
+ public String firstName;
3
+ public String lastName;
4
+ public boolean isAlive;
5
+ public int age;
6
+ public Address address;
7
+ public List<PhoneNumbers> phoneNumbers;
8
+ public List<String> children;
9
+ public String spouse;
10
+
11
+ public String getFirstName() {
12
+ return firstName;
13
+ }
14
+
15
+ public void setFirstName(String firstName) {
16
+ this.firstName = firstName;
17
+ }
18
+
19
+ public String getLastName() {
20
+ return lastName;
21
+ }
22
+
23
+ public void setLastName(String lastName) {
24
+ this.lastName = lastName;
25
+ }
26
+
27
+ public boolean getIsAlive() {
28
+ return isAlive;
29
+ }
30
+
31
+ public void setIsAlive(boolean isAlive) {
32
+ this.isAlive = isAlive;
33
+ }
34
+
35
+ public int getAge() {
36
+ return age;
37
+ }
38
+
39
+ public void setAge(int age) {
40
+ this.age = age;
41
+ }
42
+
43
+ public Address getAddress() {
44
+ return address;
45
+ }
46
+
47
+ public void setAddress(Address address) {
48
+ this.address = address;
49
+ }
50
+
51
+ public List<PhoneNumbers> getPhoneNumbers() {
52
+ return phoneNumbers;
53
+ }
54
+
55
+ public void setPhoneNumbers(List<PhoneNumbers> phoneNumbers) {
56
+ this.phoneNumbers = phoneNumbers;
57
+ }
58
+
59
+ public List<String> getChildren() {
60
+ return children;
61
+ }
62
+
63
+ public void setChildren(List<String> children) {
64
+ this.children = children;
65
+ }
66
+
67
+ public String getSpouse() {
68
+ return spouse;
69
+ }
70
+
71
+ public void setSpouse(String spouse) {
72
+ this.spouse = spouse;
73
+ }
74
+ }
@@ -0,0 +1,20 @@
1
+ public class PhoneNumbers {
2
+ public String type;
3
+ public String number;
4
+
5
+ public String getType() {
6
+ return type;
7
+ }
8
+
9
+ public void setType(String type) {
10
+ this.type = type;
11
+ }
12
+
13
+ public String getNumber() {
14
+ return number;
15
+ }
16
+
17
+ public void setNumber(String number) {
18
+ this.number = number;
19
+ }
20
+ }
data/test/address.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Address
2
+ attr_accessor :streetAddress
3
+ attr_accessor :city
4
+ attr_accessor :state
5
+ attr_accessor :postalCode
6
+
7
+ def initialize(streetAddress, city, state, postalCode)
8
+ @streetAddress = streetAddress
9
+ @city = city
10
+ @state = state
11
+ @postalCode = postalCode
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ class Generated_from_json
2
+ attr_accessor :firstName
3
+ attr_accessor :lastName
4
+ attr_accessor :isAlive
5
+ attr_accessor :age
6
+ attr_accessor :address
7
+ attr_accessor :phoneNumbers
8
+ attr_accessor :children
9
+ attr_accessor :spouse
10
+
11
+ def initialize(firstName, lastName, isAlive, age, address, phoneNumbers, children, spouse)
12
+ @firstName = firstName
13
+ @lastName = lastName
14
+ @isAlive = isAlive
15
+ @age = age
16
+ @address = address
17
+ @phoneNumbers = phoneNumbers
18
+ @children = children
19
+ @spouse = spouse
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ class PhoneNumbers
2
+ attr_accessor :type
3
+ attr_accessor :number
4
+
5
+ def initialize(type, number)
6
+ @type = type
7
+ @number = number
8
+ end
9
+ end
@@ -0,0 +1,73 @@
1
+ require "test/unit"
2
+ require_relative "../lib/class_from_son"
3
+
4
+ class TestEx < Test::Unit::TestCase
5
+
6
+ def switch_dir
7
+ # Change this to more easily find the test files
8
+ @previous_wd ||= Dir.getwd # Preserve the old wd so we can switch back to it after the test
9
+ Dir.chdir File.expand_path(File.dirname(__FILE__))
10
+ end
11
+
12
+ # Switch to the same directory as this unit-test file
13
+ def setup
14
+ switch_dir
15
+ end
16
+
17
+ def test_class_from_json_to_ruby
18
+ json = File.readlines("testjson.json").join
19
+
20
+ address_filename = "address.rb"
21
+ top_level_filename = "generated_from_json.rb"
22
+ phonenumbers_filename = "phonenumbers.rb"
23
+
24
+ expected_address = File.readlines(address_filename).join.chomp # trim off any trailing whitespace (irrelevent for this test)
25
+ expected_example = File.readlines(top_level_filename).join.chomp # trim off any trailing whitespace (irrelevent for this test)
26
+ expected_phonenumbers = File.readlines(phonenumbers_filename).join.chomp # trim off any trailing whitespace (irrelevent for this test)
27
+
28
+ generated = nil
29
+ assert_nothing_raised("ClassFromSON.generate exited with an error") { generated = ClassFromSON.generate(:ruby, json, :json, false) }
30
+
31
+ assert_equal(3, generated.size, "Failed to correctly generate all Ruby classes from JSON")
32
+
33
+ assert_equal(top_level_filename, generated[0][:name_with_ext], "Failed to correctly generate top-level generated_from_json example filename")
34
+ assert_equal(address_filename, generated[1][:name_with_ext], "Failed to correctly generate address example filename")
35
+ assert_equal(phonenumbers_filename, generated[2][:name_with_ext], "Failed to correctly generate phonenumbers example filename")
36
+
37
+ assert_equal(expected_example, generated[0][:contents], "Failed to correctly generate top-level GeneratedFromJson example Ruby class from JSON")
38
+ assert_equal(expected_address, generated[1][:contents], "Failed to correctly generate Address example Ruby class from JSON")
39
+ assert_equal(expected_phonenumbers, generated[2][:contents], "Failed to correctly generate PhoneNumbers example Ruby class from JSON")
40
+ end
41
+
42
+
43
+ def test_class_from_json_to_java
44
+ json = File.readlines("testjson.json").join
45
+
46
+ address_filename = "Address.java"
47
+ top_level_filename = "GeneratedFromJson.java"
48
+ phonenumbers_filename = "PhoneNumbers.java"
49
+
50
+ expected_address = File.readlines(address_filename).join.chomp # trim off any trailing whitespace (irrelevent for this test)
51
+ expected_example = File.readlines(top_level_filename).join.chomp # trim off any trailing whitespace (irrelevent for this test)
52
+ expected_phonenumbers = File.readlines(phonenumbers_filename).join.chomp # trim off any trailing whitespace (irrelevent for this test)
53
+
54
+ generated = nil
55
+ assert_nothing_raised("ClassFromSON.generate exited with an error") { generated = ClassFromSON.generate(:java, json, :json, false) }
56
+
57
+ assert_equal(3, generated.size, "Failed to correctly generate all Java classes from JSON")
58
+
59
+ assert_equal(top_level_filename, generated[0][:name_with_ext], "Failed to correctly generate top-level GeneratedFromJson example filename")
60
+ assert_equal(address_filename, generated[1][:name_with_ext], "Failed to correctly generate Address example filename")
61
+ assert_equal(phonenumbers_filename, generated[2][:name_with_ext], "Failed to correctly generate PhoneNumbers example filename")
62
+
63
+ assert_equal(expected_example, generated[0][:contents], "Failed to correctly generate top-level GeneratedFromJson example Java class from JSON")
64
+ assert_equal(expected_address, generated[1][:contents], "Failed to correctly generate Address example Java class from JSON")
65
+ assert_equal(expected_phonenumbers, generated[2][:contents], "Failed to correctly generate PhoneNumbers example Java class from JSON")
66
+ end
67
+
68
+
69
+ # Restore the working directory to what it was previously
70
+ def teardown
71
+ Dir.chdir @previous_wd # restore the working directory to what it was previously
72
+ end
73
+ end
@@ -0,0 +1,28 @@
1
+ {
2
+ "firstName": "John",
3
+ "lastName": "Smith",
4
+ "isAlive": true,
5
+ "age": 25,
6
+ "address": {
7
+ "streetAddress": "21 2nd Street",
8
+ "city": "New York",
9
+ "state": "NY",
10
+ "postalCode": "10021-3100"
11
+ },
12
+ "phoneNumbers": [
13
+ {
14
+ "type": "home",
15
+ "number": "212 555-1234"
16
+ },
17
+ {
18
+ "type": "office",
19
+ "number": "646 555-4567"
20
+ },
21
+ {
22
+ "type": "mobile",
23
+ "number": "123 456-7890"
24
+ }
25
+ ],
26
+ "children": [],
27
+ "spouse": null
28
+ }
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: class_from_son
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Richard Morrisby
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: This gem will attempt to generate code of a class of an object representing
14
+ the contents of a Serialised-Object-Notation (SON) string (or file). E.g. it will
15
+ generate code of a object's class from some JSON.
16
+ email: rmorrisby@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - LICENCE.txt
22
+ - README.txt
23
+ - build.rb
24
+ - class_from_son.gemspec
25
+ - lib/class_from_SON.rb
26
+ - test/Address.java
27
+ - test/GeneratedFromJson.java
28
+ - test/PhoneNumbers.java
29
+ - test/address.rb
30
+ - test/generated_from_json.rb
31
+ - test/phonenumbers.rb
32
+ - test/test_class_from_son.rb
33
+ - test/testjson.json
34
+ homepage: https://rubygems.org/gems/class_from_son
35
+ licenses:
36
+ - MIT
37
+ metadata: {}
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.9'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 2.6.7
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: Generates classes from SON (e.g. JSON)
58
+ test_files:
59
+ - test/test_class_from_son.rb