codgen 0.0.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 +7 -0
- data/.gitignore +14 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/codgen.iml +142 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/idea/.name +1 -0
- data/.idea/idea/codgen.iml +140 -0
- data/.idea/idea/encodings.xml +5 -0
- data/.idea/idea/misc.xml +5 -0
- data/.idea/idea/modules.xml +9 -0
- data/.idea/idea/scopes/scope_settings.xml +5 -0
- data/.idea/idea/vcs.xml +7 -0
- data/.idea/idea/workspace.xml +830 -0
- data/.idea/inspectionProfiles/Project_Default.xml +7 -0
- data/.idea/inspectionProfiles/profiles_settings.xml +7 -0
- data/.idea/misc.xml +5 -0
- data/.idea/modules.xml +9 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/vcs.xml +7 -0
- data/.idea/workspace.xml +1213 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +2 -0
- data/bin/codgen.rb +50 -0
- data/codgen.gemspec +27 -0
- data/lib/codgen/auto_style.rb +111 -0
- data/lib/codgen/constants.rb +5 -0
- data/lib/codgen/debug_helper.rb +15 -0
- data/lib/codgen/flattener.rb +32 -0
- data/lib/codgen/logger.rb +8 -0
- data/lib/codgen/mapping.rb +40 -0
- data/lib/codgen/statement.rb +163 -0
- data/lib/codgen/template.rb +40 -0
- data/lib/codgen/version.rb +3 -0
- data/lib/codgen.rb +59 -0
- data/test/behavior/basic_test.rb +10 -0
- data/test/behavior/output_spec.rb +36 -0
- data/test/behavior/test_utils.rb +16 -0
- data/test/data/Input/CreateDatabase.sql +10 -0
- data/test/data/Input/DbContextTemplate.cs +18 -0
- data/test/data/Input/ModelTemplate.cs +26 -0
- data/test/data/Input/example-data.json +75 -0
- data/test/data/Input/hello_world.txt.mustache +1 -0
- data/test/data/Input/model_template.rb.ctemp +8 -0
- data/test/data/Input/test_conditionals +7 -0
- data/test/data/Input/test_escapes +6 -0
- data/test/data/Map.json +36 -0
- data/test/data/Output/CreateMyBlogDatabase.sql +17 -0
- data/test/data/Output/MsMvc/MyBlog/DAL/MyBlogContext.cs +17 -0
- data/test/data/Output/MsMvc/MyBlog/Models/Comment.cs +14 -0
- data/test/data/Output/MsMvc/MyBlog/Models/Post.cs +17 -0
- data/test/data/Output/Ruby/my_blog/Models/comment.rb +3 -0
- data/test/data/Output/Ruby/my_blog/Models/post.rb +3 -0
- data/test/data/config.json +25 -0
- data/test/data/expected_output/MsMvc/MyBlog/DAL/MyBlogContext.cs +17 -0
- data/test/data/expected_output/MsMvc/MyBlog/Models/Comment.cs +14 -0
- data/test/data/expected_output/MsMvc/MyBlog/Models/Post.cs +17 -0
- data/test/data/expected_output/Ruby/my_blog/Models/comment.rb +3 -0
- data/test/data/expected_output/Ruby/my_blog/Models/post.rb +3 -0
- data/test/data/test.mustache +5 -0
- data/test/unit/codgen_engine_spec.rb +12 -0
- data/test/unit/flattener_spec.rb +51 -0
- data/test/unit/template_spec.rb +30 -0
- metadata +222 -0
    
        data/Gemfile
    ADDED
    
    
    
        data/LICENSE.txt
    ADDED
    
    | @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            Copyright (c) 2014 Matthew Beatty
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            MIT License
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 6 | 
            +
            a copy of this software and associated documentation files (the
         | 
| 7 | 
            +
            "Software"), to deal in the Software without restriction, including
         | 
| 8 | 
            +
            without limitation the rights to use, copy, modify, merge, publish,
         | 
| 9 | 
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 10 | 
            +
            permit persons to whom the Software is furnished to do so, subject to
         | 
| 11 | 
            +
            the following conditions:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            The above copyright notice and this permission notice shall be
         | 
| 14 | 
            +
            included in all copies or substantial portions of the Software.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 17 | 
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 18 | 
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 19 | 
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 20 | 
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 21 | 
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 22 | 
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            # Codgen
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            A cross language template base code generator, capable of generating multiple applications from a common model.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
             | 
| 6 | 
            +
            ## Installation
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            Add this line to your application's Gemfile:
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            ```ruby
         | 
| 11 | 
            +
            gem 'codgen'
         | 
| 12 | 
            +
            ```
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            And then execute:
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                $ bundle
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            Or install it yourself as:
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                $ gem install codgen
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            ## Usage
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                $ codgen codgen_config.json
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            ## Contributing
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            1. Fork it ( https://github.com/[my-github-username]/codgen/fork )
         | 
| 29 | 
            +
            2. Create your feature branch (`git checkout -b my-new-feature`)
         | 
| 30 | 
            +
            3. Commit your changes (`git commit -am 'Add some feature'`)
         | 
| 31 | 
            +
            4. Push to the branch (`git push origin my-new-feature`)
         | 
| 32 | 
            +
            5. Create a new Pull Request
         | 
    
        data/Rakefile
    ADDED
    
    
    
        data/bin/codgen.rb
    ADDED
    
    | @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            require 'ostruct'
         | 
| 3 | 
            +
            require 'json'
         | 
| 4 | 
            +
            require_relative '../lib/codgen'
         | 
| 5 | 
            +
            require_relative '../lib/codgen/logger'
         | 
| 6 | 
            +
            require 'fileutils'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            class CommandLineArguments
         | 
| 9 | 
            +
              def initialize(arguments)
         | 
| 10 | 
            +
                if arguments.count < 1
         | 
| 11 | 
            +
                  puts 'Help file placeholder'
         | 
| 12 | 
            +
                  exit 0
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                if arguments.count == 1
         | 
| 16 | 
            +
                  @json_config = arguments[0]
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              attr_reader :json_config
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            def get_file_contents(filepath)
         | 
| 24 | 
            +
              if File.exist?(filepath)
         | 
| 25 | 
            +
                File.read(filepath)
         | 
| 26 | 
            +
              else
         | 
| 27 | 
            +
                Logger.error('Could not find file "'+filepath+'"')
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
             | 
| 32 | 
            +
            def write_file_contents(filepath, content)
         | 
| 33 | 
            +
              dir = File.dirname(filepath)
         | 
| 34 | 
            +
              FileUtils.mkpath(dir) unless Dir.exists?(dir)
         | 
| 35 | 
            +
              File.write(filepath, content)
         | 
| 36 | 
            +
            end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
             | 
| 39 | 
            +
            def main(args)
         | 
| 40 | 
            +
              if args.json_config != nil
         | 
| 41 | 
            +
                json_config_text = get_file_contents(args.json_config)
         | 
| 42 | 
            +
                json_config = JSON.parse(json_config_text)
         | 
| 43 | 
            +
                output = Codgen.run(json_config)
         | 
| 44 | 
            +
                output.each do |path, text|
         | 
| 45 | 
            +
                  write_file_contents(path, text)
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            main(CommandLineArguments.new(ARGV))
         | 
    
        data/codgen.gemspec
    ADDED
    
    | @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 2 | 
            +
            lib = File.expand_path('../lib', __FILE__)
         | 
| 3 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 4 | 
            +
            require 'codgen/version'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Gem::Specification.new do |spec|
         | 
| 7 | 
            +
              spec.name          = "codgen"
         | 
| 8 | 
            +
              spec.version       = Codgen::VERSION
         | 
| 9 | 
            +
              spec.authors       = ["Matthew Beatty"]
         | 
| 10 | 
            +
              spec.email         = ["beattyml1@gmail.com"]
         | 
| 11 | 
            +
              spec.summary       = 'Generate multiple applications from a common model written in JSON using template'
         | 
| 12 | 
            +
              spec.description   = ''
         | 
| 13 | 
            +
              spec.homepage      = ""
         | 
| 14 | 
            +
              spec.license       = "MIT"
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              spec.files         = `git ls-files -z`.split("\x0")
         | 
| 17 | 
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         | 
| 18 | 
            +
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         | 
| 19 | 
            +
              spec.require_paths = ["lib"]
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              spec.add_development_dependency "bundler", "~> 1.7"
         | 
| 22 | 
            +
              spec.add_development_dependency "rake", "~> 10.0"
         | 
| 23 | 
            +
              spec.add_development_dependency "rspec"
         | 
| 24 | 
            +
              spec.add_runtime_dependency "mustache"
         | 
| 25 | 
            +
              spec.add_runtime_dependency "json"
         | 
| 26 | 
            +
              spec.add_runtime_dependency "activerecord"
         | 
| 27 | 
            +
            end
         | 
| @@ -0,0 +1,111 @@ | |
| 1 | 
            +
            require 'active_support/inflector'
         | 
| 2 | 
            +
            require 'ostruct'
         | 
| 3 | 
            +
            module Codgen
         | 
| 4 | 
            +
              module AutoStyle
         | 
| 5 | 
            +
                public
         | 
| 6 | 
            +
                def self.style(json_object)
         | 
| 7 | 
            +
                  if json_object.is_a?(Array)
         | 
| 8 | 
            +
                    json_object.each { |child| style(child) }
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  add_props = Hash.new
         | 
| 12 | 
            +
                  if json_object.is_a?(Hash)
         | 
| 13 | 
            +
                    json_object.each do |key, value|
         | 
| 14 | 
            +
                      if value.is_a?(Hash) || value.is_a?(Array)
         | 
| 15 | 
            +
                        style(value)
         | 
| 16 | 
            +
                        next
         | 
| 17 | 
            +
                      end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      if key.index(/[@$ #]/)
         | 
| 20 | 
            +
                        add_props.store(key, value)
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  add_props.each do |key, value|
         | 
| 26 | 
            +
                    add_property_group(json_object, key, value, AutoStyle.method(:to_camel), '!camelCase')
         | 
| 27 | 
            +
                    add_property_group(json_object, key, value, AutoStyle.method(:to_cap_camel), '!CapCamel')
         | 
| 28 | 
            +
                    add_property_group(json_object, key, value, AutoStyle.method(:to_underscore), '!underscored')
         | 
| 29 | 
            +
                    add_property_group(json_object, key, value, AutoStyle.method(:to_camel), '!CAPS_UNDERSCORE')
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
             | 
| 34 | 
            +
                def self.style_state_variable(input_string)
         | 
| 35 | 
            +
                  [to_camel(input_string), to_cap_camel(input_string), to_underscore(input_string), to_underscore(input_string)]
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
             | 
| 39 | 
            +
                private
         | 
| 40 | 
            +
                def self.add_property_group(json_object, key, value, translate, explicit_postfix)
         | 
| 41 | 
            +
                  should_translate_val = value != nil && value.is_a?(String) && key.index('@')
         | 
| 42 | 
            +
                  is_plural = key.index('?plural')
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  new_key = translate.call(key)
         | 
| 45 | 
            +
                  new_value = should_translate_val ? translate.call(value) : value
         | 
| 46 | 
            +
                  explicit_key = new_key + explicit_postfix
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  requantified = pluralize_singularize(new_key, explicit_key, new_value, is_plural, should_translate_val)
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  try_add_prop(json_object, new_key, new_value)
         | 
| 51 | 
            +
                  try_add_prop(json_object, explicit_key, new_value)
         | 
| 52 | 
            +
                  try_add_prop(json_object, requantified.short_key, requantified.value)
         | 
| 53 | 
            +
                  try_add_prop(json_object, requantified.explicit_key, requantified.value)
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def self.pluralize_singularize(short_key, explicit_key, value, is_plural, should_translate_value)
         | 
| 57 | 
            +
                  return_obj = OpenStruct.new
         | 
| 58 | 
            +
                  if is_plural
         | 
| 59 | 
            +
                    return_obj.short_key = short_key.singularize
         | 
| 60 | 
            +
                    return_obj.explicit_key = explicit_key.singularize
         | 
| 61 | 
            +
                    return_obj.value = should_translate_value ? value.singularize : value
         | 
| 62 | 
            +
                  else
         | 
| 63 | 
            +
                    return_obj.short_key = short_key.pluralize
         | 
| 64 | 
            +
                    return_obj.explicit_key = explicit_key.pluralize
         | 
| 65 | 
            +
                    return_obj.value = should_translate_value ? value.pluralize : value
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                  return_obj
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
             | 
| 71 | 
            +
                def self.try_add_prop(json_object, key, value)
         | 
| 72 | 
            +
                  unless json_object.has_key?(key)
         | 
| 73 | 
            +
                    json_object.store(key, value)
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
             | 
| 78 | 
            +
                def self.to_universal(input_string)
         | 
| 79 | 
            +
                  output_string = input_string.tr('$?@', '')
         | 
| 80 | 
            +
                  output_string.split(' ')
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
             | 
| 84 | 
            +
                def self.to_cap_camel(input_string)
         | 
| 85 | 
            +
                  universal = to_universal(input_string)
         | 
| 86 | 
            +
                  universal.each {|word| word[0] = word[0].upcase}
         | 
| 87 | 
            +
                  universal.join('')
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
             | 
| 91 | 
            +
                def self.to_camel(input_string)
         | 
| 92 | 
            +
                  universal = to_universal(input_string)
         | 
| 93 | 
            +
                  universal[1...universal.count].each {|word| word[0] = word[0].upcase}
         | 
| 94 | 
            +
                  universal.join('')
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
             | 
| 98 | 
            +
                def self.to_underscore(input_string)
         | 
| 99 | 
            +
                  universal = to_universal(input_string)
         | 
| 100 | 
            +
                  output = universal.join('_')
         | 
| 101 | 
            +
                  output.downcase
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
             | 
| 105 | 
            +
                def self.to_allcaps_underscore(input_string)
         | 
| 106 | 
            +
                  universal = to_universal(input_string)
         | 
| 107 | 
            +
                  output = universal.join('_')
         | 
| 108 | 
            +
                  output.upcase
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
              end
         | 
| 111 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            module DebugHelper
         | 
| 2 | 
            +
              @@event_log = nil
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def self.write_file(name, text)
         | 
| 5 | 
            +
                File.write('../debug_data/'+name, text)
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def self.log_event(message)
         | 
| 9 | 
            +
                if @@event_log == nil
         | 
| 10 | 
            +
                  @@event_log = File.new('../debug_data/event_log', 'w+')
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  @@event_log.write("#{Time.now}: #{message}\n")
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            module Codgen
         | 
| 2 | 
            +
              module Flattener
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                # @param [Hash] data_root: the JSON root hash
         | 
| 5 | 
            +
                # @param [Array[String]] properties
         | 
| 6 | 
            +
                # @return [Array, Hash]
         | 
| 7 | 
            +
                def self.merge(data_root, properties)
         | 
| 8 | 
            +
                  current = data_root
         | 
| 9 | 
            +
                  obj_out = Hash.new
         | 
| 10 | 
            +
                  properties.each { |property|
         | 
| 11 | 
            +
                    current.each {|prop, val| obj_out[prop] = val}
         | 
| 12 | 
            +
                    current = current[property]
         | 
| 13 | 
            +
                  }
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  if current.is_a?(Hash)
         | 
| 16 | 
            +
                    current.each {|prop, val| obj_out[prop] = val}
         | 
| 17 | 
            +
                    return obj_out
         | 
| 18 | 
            +
                  elsif current.is_a?(Array)
         | 
| 19 | 
            +
                    array_out = Array.new
         | 
| 20 | 
            +
                    current.each { |child|
         | 
| 21 | 
            +
                      if child.is_a?(Hash)
         | 
| 22 | 
            +
                        array_obj = Hash.new
         | 
| 23 | 
            +
                        obj_out.each {|prop, val| array_obj[prop] = val}
         | 
| 24 | 
            +
                        child.each {|prop, val| array_obj[prop] = val}
         | 
| 25 | 
            +
                        array_out.push(array_obj)
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
                    }
         | 
| 28 | 
            +
                    return array_out
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            module Codgen
         | 
| 2 | 
            +
              module Mapping
         | 
| 3 | 
            +
                def self.map_object(json_data, json_map_data)
         | 
| 4 | 
            +
                  if json_data != nil && json_data.is_a?(Array)
         | 
| 5 | 
            +
                    json_data.each do |item|
         | 
| 6 | 
            +
                      map_object(item, json_map_data)
         | 
| 7 | 
            +
                    end
         | 
| 8 | 
            +
                    return
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  add_entries = Hash.new
         | 
| 12 | 
            +
                  json_data.each do |key, value|
         | 
| 13 | 
            +
                    if value != nil && value.is_a?(Array) || value.is_a?(Hash)
         | 
| 14 | 
            +
                      map_object(value, json_map_data)
         | 
| 15 | 
            +
                      next
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    map = json_map_data[key]
         | 
| 19 | 
            +
                    if map != nil
         | 
| 20 | 
            +
                      map.each do |output_key, value_map|
         | 
| 21 | 
            +
                        unless json_data.has_key?(output_key)
         | 
| 22 | 
            +
                          output_value = value
         | 
| 23 | 
            +
                          value_map.each do |match_expr, map_value|
         | 
| 24 | 
            +
                            if Regexp.new(match_expr).match(value)
         | 
| 25 | 
            +
                              output_value = map_value
         | 
| 26 | 
            +
                              break
         | 
| 27 | 
            +
                            end
         | 
| 28 | 
            +
                          end
         | 
| 29 | 
            +
                          add_entries.store(output_key, output_value)
         | 
| 30 | 
            +
                        end
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  add_entries.each do |key, value|
         | 
| 36 | 
            +
                    json_data.store(key, value)
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| @@ -0,0 +1,163 @@ | |
| 1 | 
            +
            require_relative 'constants'
         | 
| 2 | 
            +
            require_relative 'logger'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            # Not currently used but will be for an up coming feature that used be implemented in an older version
         | 
| 5 | 
            +
            module Codgen
         | 
| 6 | 
            +
              class Statement
         | 
| 7 | 
            +
                include RegularExpressions
         | 
| 8 | 
            +
                def initialize(operator=nil)
         | 
| 9 | 
            +
                  @words = Array.new
         | 
| 10 | 
            +
                  @operator = operator
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def evaluate(get_val)
         | 
| 14 | 
            +
                  if @operator == nil
         | 
| 15 | 
            +
                    expand
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  current_val = nil
         | 
| 19 | 
            +
                  @words.each do |child|
         | 
| 20 | 
            +
                    current_val = accumulate(current_val, child, @operator, get_val)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                  current_val
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                attr_reader :operator
         | 
| 26 | 
            +
                attr_reader :words
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def accumulate(before, child, operator, get_val)
         | 
| 29 | 
            +
                  if operator == nil && child.is_a?(Statement)
         | 
| 30 | 
            +
                    accumulate(before, child, child.operator, get_val)
         | 
| 31 | 
            +
                  else
         | 
| 32 | 
            +
                    value = child.is_a?(String) ? get_val.call(child) : child.evaluate(get_val)
         | 
| 33 | 
            +
                    case operator
         | 
| 34 | 
            +
                      when :and
         | 
| 35 | 
            +
                        before = before != nil ? before : true
         | 
| 36 | 
            +
                        before && value
         | 
| 37 | 
            +
                      when :or
         | 
| 38 | 
            +
                        before = before != nil ? before : false
         | 
| 39 | 
            +
                        before || value
         | 
| 40 | 
            +
                      when :not
         | 
| 41 | 
            +
                        !value
         | 
| 42 | 
            +
                      else
         | 
| 43 | 
            +
                        value
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def expand
         | 
| 49 | 
            +
                  expand_not
         | 
| 50 | 
            +
                  expand_and
         | 
| 51 | 
            +
                  expand_or
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def parse(text)
         | 
| 55 | 
            +
                  count = -1
         | 
| 56 | 
            +
                  text.each_char do |current_char|
         | 
| 57 | 
            +
                    count+=1
         | 
| 58 | 
            +
                    if current_char == ' '
         | 
| 59 | 
            +
                      next 1
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    if current_char.index(IDENTIFIER_REGEX)
         | 
| 63 | 
            +
                      if @words.length > 0 && @words[-1].index(IDENTIFIER_REGEX)
         | 
| 64 | 
            +
                        @words[-1] = @words[-1] + current_char
         | 
| 65 | 
            +
                      else
         | 
| 66 | 
            +
                        @words.push(current_char)
         | 
| 67 | 
            +
                      end
         | 
| 68 | 
            +
                    elsif current_char.index(/[&|!]/)
         | 
| 69 | 
            +
                      @words.push(current_char)
         | 
| 70 | 
            +
                    elsif current_char == '('
         | 
| 71 | 
            +
                      @words.push(Statement.new)
         | 
| 72 | 
            +
                      count += @words[-1].parse(text[count...text.length])
         | 
| 73 | 
            +
                    elsif current_char == ')'
         | 
| 74 | 
            +
                      return count
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                def expand_not
         | 
| 80 | 
            +
                  new_words = Array.new
         | 
| 81 | 
            +
                  @words.each do |word|
         | 
| 82 | 
            +
                    if word.is_a?(Statement)
         | 
| 83 | 
            +
                      new_words.push(word)
         | 
| 84 | 
            +
                    elsif word.index(/not|!/)
         | 
| 85 | 
            +
                      if new_words[-1].is_a?(Statement) && new_words[-1].operator == :not
         | 
| 86 | 
            +
                        new_words.delete_at(-1)
         | 
| 87 | 
            +
                      else
         | 
| 88 | 
            +
                        new_words.push(Statement.new(:not))
         | 
| 89 | 
            +
                      end
         | 
| 90 | 
            +
                    else
         | 
| 91 | 
            +
                      if new_words[-1].is_a?(Statement) && new_words[-1].operator == :not && new_words[-1].words.count == 0
         | 
| 92 | 
            +
                        new_words[-1].words.push(word)
         | 
| 93 | 
            +
                      else
         | 
| 94 | 
            +
                        new_words.push(word)
         | 
| 95 | 
            +
                      end
         | 
| 96 | 
            +
                    end
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                  @words = new_words
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def expand_and
         | 
| 102 | 
            +
                  new_words = Array.new
         | 
| 103 | 
            +
                  index = -1
         | 
| 104 | 
            +
                  skip = false
         | 
| 105 | 
            +
                  @words.each do |word|
         | 
| 106 | 
            +
                    index+=1
         | 
| 107 | 
            +
                    if skip
         | 
| 108 | 
            +
                      skip = false
         | 
| 109 | 
            +
                      next
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
                    if word.is_a?(Statement)
         | 
| 112 | 
            +
                      new_words.push(word)
         | 
| 113 | 
            +
                    elsif word.index(/and|&/)
         | 
| 114 | 
            +
                      if new_words.count > 0 && @words.count > index + 1
         | 
| 115 | 
            +
                        left = new_words[-1]
         | 
| 116 | 
            +
                        new_words.delete_at(-1)
         | 
| 117 | 
            +
                        new_words.push(Statement.new(:and))
         | 
| 118 | 
            +
                        new_words[-1].words.push(left)
         | 
| 119 | 
            +
                        new_words[-1].words.push(@words[index+1])
         | 
| 120 | 
            +
                        skip = true
         | 
| 121 | 
            +
                        next
         | 
| 122 | 
            +
                      else
         | 
| 123 | 
            +
                        throw 'Operator must have a left and a right side'
         | 
| 124 | 
            +
                      end
         | 
| 125 | 
            +
                    else
         | 
| 126 | 
            +
                      new_words.push(word)
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
                  @words = new_words
         | 
| 130 | 
            +
                end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                def expand_or
         | 
| 133 | 
            +
                  new_words = Array.new
         | 
| 134 | 
            +
                  index = -1
         | 
| 135 | 
            +
                  skip = false
         | 
| 136 | 
            +
                  @words.each do |word|
         | 
| 137 | 
            +
                    index+=1
         | 
| 138 | 
            +
                    if skip
         | 
| 139 | 
            +
                      skip = false
         | 
| 140 | 
            +
                      next
         | 
| 141 | 
            +
                    end
         | 
| 142 | 
            +
                    if word.is_a?(Statement)
         | 
| 143 | 
            +
                      new_words.push(word)
         | 
| 144 | 
            +
                    elsif word.index(/or|\|/)
         | 
| 145 | 
            +
                      if new_words.count > 0 && @words.count > index + 1
         | 
| 146 | 
            +
                        left = new_words[-1]
         | 
| 147 | 
            +
                        new_words.delete_at(-1)
         | 
| 148 | 
            +
                        new_words.push(Statement.new(:or))
         | 
| 149 | 
            +
                        new_words[-1].words.push(left)
         | 
| 150 | 
            +
                        new_words[-1].words.push(@words[index+1])
         | 
| 151 | 
            +
                        skip = true
         | 
| 152 | 
            +
                        next
         | 
| 153 | 
            +
                      else
         | 
| 154 | 
            +
                        throw 'Operator must have a left and a right side'
         | 
| 155 | 
            +
                      end
         | 
| 156 | 
            +
                    else
         | 
| 157 | 
            +
                      new_words.push(word)
         | 
| 158 | 
            +
                    end
         | 
| 159 | 
            +
                  end
         | 
| 160 | 
            +
                  @words = new_words
         | 
| 161 | 
            +
                end
         | 
| 162 | 
            +
              end
         | 
| 163 | 
            +
            end
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            require 'mustache'
         | 
| 2 | 
            +
            require_relative 'flattener'
         | 
| 3 | 
            +
            require_relative 'logger'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Codgen
         | 
| 6 | 
            +
              class Template
         | 
| 7 | 
            +
                # @param [JSON_Hash] template_info
         | 
| 8 | 
            +
                # @param [JSON_Hash] data_root
         | 
| 9 | 
            +
                def initialize(template_info, data_root)
         | 
| 10 | 
            +
                  @input_path = template_info['in']
         | 
| 11 | 
            +
                  @out_raw = template_info['out']
         | 
| 12 | 
            +
                  source_raw = template_info['source']
         | 
| 13 | 
            +
                  source_location = source_raw != nil ? source_raw.split('.') : []
         | 
| 14 | 
            +
                  @data = Flattener.merge(data_root, source_location)
         | 
| 15 | 
            +
                  @template = Template.get_template(@input_path)
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def fill_template
         | 
| 19 | 
            +
                  if @data.is_a?(Array)
         | 
| 20 | 
            +
                    output = Hash.new
         | 
| 21 | 
            +
                    @data.each { |data|
         | 
| 22 | 
            +
                      output.store(Mustache.render(@out_raw, data), Mustache.render(@template, data))
         | 
| 23 | 
            +
                    }
         | 
| 24 | 
            +
                    return output
         | 
| 25 | 
            +
                  elsif @data.is_a?(Hash)
         | 
| 26 | 
            +
                  return { Mustache.render(@out_raw, data) => Mustache.render(@template, @data) }
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def self.get_template(path)
         | 
| 31 | 
            +
                  if File.exist?(path)
         | 
| 32 | 
            +
                    File.read(path)
         | 
| 33 | 
            +
                  else
         | 
| 34 | 
            +
                    Logger.error('Could not find file "'+path+'"')
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                attr_reader :data, :input_path, :template # Used to unit test
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
    
        data/lib/codgen.rb
    ADDED
    
    | @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            require_relative 'codgen/version'
         | 
| 2 | 
            +
            require_relative 'codgen/auto_style'
         | 
| 3 | 
            +
            require_relative 'codgen/mapping'
         | 
| 4 | 
            +
            require_relative 'codgen/logger'
         | 
| 5 | 
            +
            require_relative 'codgen/template'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Codgen
         | 
| 8 | 
            +
              def self.run(json_config)
         | 
| 9 | 
            +
                map = json_config['map']
         | 
| 10 | 
            +
                map = get_data_if_not_data(map)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                json_data  = json_config['data']
         | 
| 13 | 
            +
                json_data = get_data_if_not_data(json_data)
         | 
| 14 | 
            +
                json_data = map_and_style(map, json_data)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                #File.write('temp.json', JSON.pretty_unparse(json_data)) # frequently used debug code
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                filled_templates = Array.new
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                templates = json_config['templates']
         | 
| 21 | 
            +
                templates.each do |template_info|
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  template = Template.new(template_info, json_data)
         | 
| 24 | 
            +
                  template.fill_template.each {|filled_template| filled_templates.push(filled_template)}
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                filled_templates
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              def self.get_data_if_not_data(filepath_or_data)
         | 
| 31 | 
            +
                if filepath_or_data != nil
         | 
| 32 | 
            +
                  if filepath_or_data.is_a?(String)
         | 
| 33 | 
            +
                    file = File.read(filepath_or_data)
         | 
| 34 | 
            +
                    JSON.parse(file)
         | 
| 35 | 
            +
                  elsif filepath_or_data.is_a?(Hash)
         | 
| 36 | 
            +
                    filepath_or_data
         | 
| 37 | 
            +
                  else
         | 
| 38 | 
            +
                    Logger.error('In json config file: "map" must be either a filepath or a json object.')
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
             | 
| 44 | 
            +
              class FilledTemplate
         | 
| 45 | 
            +
                attr_accessor :path
         | 
| 46 | 
            +
                attr_accessor :text
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
             | 
| 50 | 
            +
              def self.map_and_style(map_data, json_data)
         | 
| 51 | 
            +
                if map_data
         | 
| 52 | 
            +
                  Mapping.map_object(json_data, map_data)
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                AutoStyle.style(json_data)
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                json_data
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            require 'rspec'
         | 
| 2 | 
            +
            require 'fileutils'
         | 
| 3 | 
            +
            require_relative 'test_utils'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe 'Application' do
         | 
| 6 | 
            +
              $expectation_directory = 'expected_output'
         | 
| 7 | 
            +
              $output_directory = 'Output'
         | 
| 8 | 
            +
              before :all do
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                FileUtils.rm_rf($output_directory)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                puts `../../bin/codgen.rb config.json`
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              it 'should produce all expected files' do
         | 
| 16 | 
            +
                files_and_folders = Dir.glob($expectation_directory+'/**/*')
         | 
| 17 | 
            +
                files_and_folders.each do |expected_file_path|
         | 
| 18 | 
            +
                  if File.file?(expected_file_path)
         | 
| 19 | 
            +
                    output_file_path = expected_file_path.sub($expectation_directory, $output_directory)
         | 
| 20 | 
            +
                    expect(File.exist?(output_file_path)).to be_truthy
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
             | 
| 26 | 
            +
              files_and_folders = Dir.glob($expectation_directory+'/**/*')
         | 
| 27 | 
            +
              files_and_folders.each do |expected_file_path|
         | 
| 28 | 
            +
                if File.file?(expected_file_path)
         | 
| 29 | 
            +
                  output_file_path = expected_file_path.sub($expectation_directory, $output_directory)
         | 
| 30 | 
            +
                  it "should produce file: '#{output_file_path}' that is the same as '#{expected_file_path}'" do
         | 
| 31 | 
            +
                    expect(File.exist?(output_file_path)).to be_truthy
         | 
| 32 | 
            +
                    expect(FileUtils.cmp(output_file_path, expected_file_path)).to be_truthy
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            module TestUtils
         | 
| 2 | 
            +
              def self.recursive_compare(expectation_directory, output_directory)
         | 
| 3 | 
            +
                files_and_folders = Dir.glob(expectation_directory+'/**/*')
         | 
| 4 | 
            +
                error = false
         | 
| 5 | 
            +
                files_and_folders.each do |expected_file_path|
         | 
| 6 | 
            +
                  if File.file?(expected_file_path)
         | 
| 7 | 
            +
                    output_file_path = expected_file_path.sub(expectation_directory, output_directory)
         | 
| 8 | 
            +
                    unless FileUtils.cmp(expected_file_path, output_file_path)
         | 
| 9 | 
            +
                      puts "Error: '#{output_file_path}' does not match '#{expected_file_path}"
         | 
| 10 | 
            +
                      error = true
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
                return error
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         |