ginny 0.5.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 +7 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +138 -0
- data/.solargraph.yml +14 -0
- data/.travis.yml +7 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +68 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +61 -0
- data/LICENSE.txt +21 -0
- data/README.md +147 -0
- data/Rakefile +10 -0
- data/bin/console +9 -0
- data/bin/docs +2 -0
- data/bin/setup +8 -0
- data/exe/ginny +23 -0
- data/ginny.gemspec +43 -0
- data/lib/ginny.rb +6 -0
- data/lib/ginny/error.rb +5 -0
- data/lib/ginny/load.rb +45 -0
- data/lib/ginny/mod.rb +32 -0
- data/lib/ginny/models/attr.rb +73 -0
- data/lib/ginny/models/class.rb +94 -0
- data/lib/ginny/models/func.rb +86 -0
- data/lib/ginny/models/param.rb +90 -0
- data/lib/ginny/string/comment.rb +19 -0
- data/lib/ginny/string/indent.rb +52 -0
- data/lib/ginny/symbolize.rb +31 -0
- data/lib/ginny/version.rb +3 -0
- metadata +203 -0
    
        data/Rakefile
    ADDED
    
    
    
        data/bin/console
    ADDED
    
    
    
        data/bin/docs
    ADDED
    
    
    
        data/bin/setup
    ADDED
    
    
    
        data/exe/ginny
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "ginny"
         | 
| 4 | 
            +
            require "optparse"
         | 
| 5 | 
            +
            options = {}
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            optparse = OptionParser.new do |opts|
         | 
| 8 | 
            +
              opts.banner = "Usage: ginny [options] [path]"
         | 
| 9 | 
            +
              options[:verbose] = false
         | 
| 10 | 
            +
              opts.on("-v", "--verbose", "Output more information") do
         | 
| 11 | 
            +
                options[:verbose] = true
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
              opts.on("-h", "--help", "Display this screen") do
         | 
| 14 | 
            +
                puts(opts)
         | 
| 15 | 
            +
                exit(0)
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            optparse.parse!
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ARGV.each do |f|
         | 
| 22 | 
            +
              Ginny::Class.create(Ginny.load_file(f)).generate()
         | 
| 23 | 
            +
            end
         | 
    
        data/ginny.gemspec
    ADDED
    
    | @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            lib = File.expand_path("lib", __dir__)
         | 
| 2 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 3 | 
            +
            require "ginny/version"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Gem::Specification.new do |spec|
         | 
| 6 | 
            +
              spec.name          = "ginny"
         | 
| 7 | 
            +
              spec.version       = Ginny::VERSION
         | 
| 8 | 
            +
              spec.required_ruby_version = ">= 2.3.0"
         | 
| 9 | 
            +
              spec.description   = "Generate Ruby code."
         | 
| 10 | 
            +
              spec.summary       = spec.description
         | 
| 11 | 
            +
              spec.authors       = ["Clay Dunston"]
         | 
| 12 | 
            +
              spec.email         = ["dunstontc@gmail.com"]
         | 
| 13 | 
            +
              spec.homepage      = "https://github.com/tcd/ginny"
         | 
| 14 | 
            +
              spec.license       = "MIT"
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              spec.metadata = {
         | 
| 17 | 
            +
                "homepage_uri" => spec.homepage,
         | 
| 18 | 
            +
                "source_code_uri" => spec.homepage,
         | 
| 19 | 
            +
                "documentation_uri" => "https://www.rubydoc.info/gems/ginny/#{spec.version}",
         | 
| 20 | 
            +
                "changelog_uri" => "https://github.com/tcd/ginny/blob/master/CHANGELOG.md",
         | 
| 21 | 
            +
                "yard.run" => "yri", # use "yard" to build full HTML docs.
         | 
| 22 | 
            +
              }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              # Specify which files should be added to the gem when it is released.
         | 
| 25 | 
            +
              # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
         | 
| 26 | 
            +
              spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
         | 
| 27 | 
            +
                `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
              spec.bindir        = "exe"
         | 
| 30 | 
            +
              spec.executables   = ["ginny"]
         | 
| 31 | 
            +
              spec.require_paths = ["lib"]
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              spec.add_development_dependency "bundler", "~> 2.0"
         | 
| 34 | 
            +
              spec.add_development_dependency "coveralls", "~> 0.8.23"
         | 
| 35 | 
            +
              spec.add_development_dependency "minitest", "~> 5.0"
         | 
| 36 | 
            +
              spec.add_development_dependency "minitest-focus", "~> 1.1"
         | 
| 37 | 
            +
              spec.add_development_dependency "minitest-reporters", "~> 1.4"
         | 
| 38 | 
            +
              spec.add_development_dependency "pry", "~> 0.12.2"
         | 
| 39 | 
            +
              spec.add_development_dependency "rake", "~> 10.0"
         | 
| 40 | 
            +
              spec.add_development_dependency "simplecov"
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              spec.add_runtime_dependency "dry-inflector", "~> 0.2.0"
         | 
| 43 | 
            +
            end
         | 
    
        data/lib/ginny.rb
    ADDED
    
    
    
        data/lib/ginny/error.rb
    ADDED
    
    
    
        data/lib/ginny/load.rb
    ADDED
    
    | @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            require "pathname"
         | 
| 2 | 
            +
            require "json"
         | 
| 3 | 
            +
            require "yaml"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Ginny
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              # Load data from a YAML file.
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              # @param path [String]
         | 
| 10 | 
            +
              # @return [Hash<Symbol>]
         | 
| 11 | 
            +
              def self.load_yaml(path)
         | 
| 12 | 
            +
                return Ginny.symbolize_keys(YAML.load_file(File.expand_path(path)))
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              # Load data from a JSON file.
         | 
| 16 | 
            +
              #
         | 
| 17 | 
            +
              # @param path [String]
         | 
| 18 | 
            +
              # @return [Hash<Symbol>]
         | 
| 19 | 
            +
              def self.load_json(path)
         | 
| 20 | 
            +
                return JSON.parse(File.read(File.expand_path(path)), symbolize_names: true)
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              # @param path [String] Path of the file to determine the extension of.
         | 
| 24 | 
            +
              # @return [String]
         | 
| 25 | 
            +
              def self.get_extension(path)
         | 
| 26 | 
            +
                file = Pathname.new(path)
         | 
| 27 | 
            +
                return file.extname.downcase
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              # Load data from a YAML or JSON file.
         | 
| 31 | 
            +
              #
         | 
| 32 | 
            +
              # @param path [String]
         | 
| 33 | 
            +
              # @return [Hash<Symbol>]
         | 
| 34 | 
            +
              def self.load_file(path)
         | 
| 35 | 
            +
                case Pathname.new(path).extname.downcase
         | 
| 36 | 
            +
                when ".yml", ".yaml"
         | 
| 37 | 
            +
                  return self.load_yaml(path)
         | 
| 38 | 
            +
                when ".json"
         | 
| 39 | 
            +
                  return self.load_json(path)
         | 
| 40 | 
            +
                else
         | 
| 41 | 
            +
                  raise Ginny::Error "invalid file type"
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            end
         | 
    
        data/lib/ginny/mod.rb
    ADDED
    
    | @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            module Ginny
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              # Used to generate a [module](https://ruby-doc.org/core-2.6.5/doc/syntax/modules_and_classes_rdoc.html).
         | 
| 4 | 
            +
              #
         | 
| 5 | 
            +
              # More accurately, wrap the `body` (first argument) with any following module definitions (additional arguments).
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              # @example
         | 
| 8 | 
            +
              #   Ginny.mod("puts('Hello World')", "Level1", "Level2")
         | 
| 9 | 
            +
              #   #=> module Level1
         | 
| 10 | 
            +
              #         module Level2
         | 
| 11 | 
            +
              #           puts('Hello World')
         | 
| 12 | 
            +
              #         end
         | 
| 13 | 
            +
              #       end
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              # @param body [String] Name of module namespaces.
         | 
| 16 | 
            +
              # @param names [String,Array<String>] Name of module namespaces.
         | 
| 17 | 
            +
              # @return [String]
         | 
| 18 | 
            +
              def self.mod(body, *names)
         | 
| 19 | 
            +
                names.flatten!
         | 
| 20 | 
            +
                count = names.length
         | 
| 21 | 
            +
                return body unless count.positive?()
         | 
| 22 | 
            +
                level = 0
         | 
| 23 | 
            +
                head = []
         | 
| 24 | 
            +
                tail = []
         | 
| 25 | 
            +
                names.each do |name|
         | 
| 26 | 
            +
                  head.push("module #{name}".indent(level))
         | 
| 27 | 
            +
                  tail.unshift("end".indent(level))
         | 
| 28 | 
            +
                  level += 2
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
                return (head + [body&.indent(level)] + tail).compact.join("\n")
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            module Ginny
         | 
| 2 | 
            +
              # Used to generate an instance variable with getters/setters.
         | 
| 3 | 
            +
              #
         | 
| 4 | 
            +
              # [attr]: https://docs.ruby-lang.org/en/master/Module.html#method-i-attr
         | 
| 5 | 
            +
              # [attr_accessor]: https://docs.ruby-lang.org/en/master/Module.html#method-i-attr_accessor
         | 
| 6 | 
            +
              # [attr_reader]: https://docs.ruby-lang.org/en/master/Module.html#method-i-attr_reader
         | 
| 7 | 
            +
              class Attr
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                # Name of the attribute.
         | 
| 10 | 
            +
                # @return [String]
         | 
| 11 | 
            +
                attr_accessor :name
         | 
| 12 | 
            +
                # Description of the attribute. [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) is supported.
         | 
| 13 | 
            +
                # @return [String]
         | 
| 14 | 
            +
                attr_accessor :description
         | 
| 15 | 
            +
                # [Type](https://rubydoc.info/gems/yard/file/docs/GettingStarted.md#Declaring_Types) of the attribute.
         | 
| 16 | 
            +
                # @return [String]
         | 
| 17 | 
            +
                attr_accessor :type
         | 
| 18 | 
            +
                # Default value for the attribute; set in it's Class's `initialize` function.
         | 
| 19 | 
            +
                # @return [String]
         | 
| 20 | 
            +
                attr_accessor :default
         | 
| 21 | 
            +
                # If `true`, an `attr_reader` will be generated for the attribute instead of an `attr_accessor`.
         | 
| 22 | 
            +
                # @return [Boolean]
         | 
| 23 | 
            +
                attr_accessor :read_only
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                # @return [void]
         | 
| 26 | 
            +
                def initialize()
         | 
| 27 | 
            +
                  self.read_only = false
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                # Constructor for an Attr. Use `create`, not `new`.
         | 
| 31 | 
            +
                #
         | 
| 32 | 
            +
                # @param args [Hash]
         | 
| 33 | 
            +
                # @return [Attr]
         | 
| 34 | 
            +
                def self.create(args = {})
         | 
| 35 | 
            +
                  a = Ginny::Attr.new()
         | 
| 36 | 
            +
                  a.name        = args[:name]
         | 
| 37 | 
            +
                  a.description = args[:description]
         | 
| 38 | 
            +
                  a.type        = args[:type]
         | 
| 39 | 
            +
                  a.read_only   = args[:read_only]
         | 
| 40 | 
            +
                  return a
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                # @param array [Array<Hash>]
         | 
| 44 | 
            +
                # @return [Array<Ginny::Attr>]
         | 
| 45 | 
            +
                def self.from_array(array)
         | 
| 46 | 
            +
                  return array.map { |f| self.create(f) }
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                # @return [String]
         | 
| 50 | 
            +
                def render()
         | 
| 51 | 
            +
                  parts = []
         | 
| 52 | 
            +
                  parts << (@description&.length&.positive? ? @description.comment : nil)
         | 
| 53 | 
            +
                  parts << "@return [#{self.type}]".comment
         | 
| 54 | 
            +
                  parts << "attr_#{self.read_only ? 'reader' : 'accessor'} :#{self.name.downcase}"
         | 
| 55 | 
            +
                  return parts.compact.join("\n").gsub(/\s+$/, "")
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                # Used for documenting attributes that are "declared dynamically via meta-programming".
         | 
| 59 | 
            +
                # See the documentation on [YARD directives][1] for more info.
         | 
| 60 | 
            +
                #
         | 
| 61 | 
            +
                # [1]: https://www.rubydoc.info/gems/yard/file/docs/Tags.md#attribute
         | 
| 62 | 
            +
                #
         | 
| 63 | 
            +
                # @return [String]
         | 
| 64 | 
            +
                def render_dynamic()
         | 
| 65 | 
            +
                  parts = []
         | 
| 66 | 
            +
                  parts << "@!attribute #{self.name.downcase} [#{self.read_only ? 'r' : 'rw'}]".comment
         | 
| 67 | 
            +
                  parts << (@description&.length&.positive? ? @description.indent(2).comment : nil)
         | 
| 68 | 
            +
                  parts << "@return [#{self.type}]".indent(2).comment
         | 
| 69 | 
            +
                  return parts.compact.join("\n").gsub(/\s+$/, "")
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
            end
         | 
| @@ -0,0 +1,94 @@ | |
| 1 | 
            +
            require "dry/inflector"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Ginny
         | 
| 4 | 
            +
              # Used to generate a [class](https://ruby-doc.org/core-2.6.5/Class.html)
         | 
| 5 | 
            +
              class Class
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                # Name of the class.
         | 
| 8 | 
            +
                # @return [String]
         | 
| 9 | 
            +
                attr_accessor :name
         | 
| 10 | 
            +
                # Description of the class. [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) is supported.
         | 
| 11 | 
            +
                # @return [String]
         | 
| 12 | 
            +
                attr_accessor :description
         | 
| 13 | 
            +
                # Name of a class to inherit from. (Ex: `YourNewClass < Parent`)
         | 
| 14 | 
            +
                # @return [String]
         | 
| 15 | 
            +
                attr_accessor :parent
         | 
| 16 | 
            +
                # List of modules to declare the class inside.
         | 
| 17 | 
            +
                # @return [String]
         | 
| 18 | 
            +
                attr_accessor :modules
         | 
| 19 | 
            +
                # An array of {Ginny::Attr}s.
         | 
| 20 | 
            +
                # @return [Array<Ginny::Attr>]
         | 
| 21 | 
            +
                attr_accessor :attrs
         | 
| 22 | 
            +
                # String to write into the body of the class.
         | 
| 23 | 
            +
                # @return [String]
         | 
| 24 | 
            +
                attr_accessor :body
         | 
| 25 | 
            +
                # String to prepend to the name of the generated file.
         | 
| 26 | 
            +
                # @return [String]
         | 
| 27 | 
            +
                attr_accessor :file_prefix
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                # @return [void]
         | 
| 30 | 
            +
                def initialize()
         | 
| 31 | 
            +
                  self.attrs = []
         | 
| 32 | 
            +
                  self.modules = []
         | 
| 33 | 
            +
                  self.file_prefix = ""
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                # Constructor for a Class. Use `create`, not `new`.
         | 
| 37 | 
            +
                #
         | 
| 38 | 
            +
                # @param args [Hash<Symbol>]
         | 
| 39 | 
            +
                # @return [Class]
         | 
| 40 | 
            +
                def self.create(args = {})
         | 
| 41 | 
            +
                  c = Ginny::Class.new()
         | 
| 42 | 
            +
                  c.name        = args[:name]
         | 
| 43 | 
            +
                  c.description = args[:description]
         | 
| 44 | 
            +
                  c.parent      = args[:parent]
         | 
| 45 | 
            +
                  c.modules     = args[:modules] unless args[:modules].nil?
         | 
| 46 | 
            +
                  c.attrs       = Ginny::Attr.from_array(args[:attrs]) if args[:attrs]&.is_a?(Array)
         | 
| 47 | 
            +
                  c.body        = args[:body] unless args[:body].nil?
         | 
| 48 | 
            +
                  c.file_prefix = args[:file_prefix] || ""
         | 
| 49 | 
            +
                  return c
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                # @param folder [String]
         | 
| 53 | 
            +
                # @return [String]
         | 
| 54 | 
            +
                def generate(folder = ".")
         | 
| 55 | 
            +
                  path = File.join(File.expand_path(folder), self.file_name())
         | 
| 56 | 
            +
                  File.open(path, "a") { |f| f.write(self.render() + "\n") }
         | 
| 57 | 
            +
                  return path
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                # @return [String]
         | 
| 61 | 
            +
                def render()
         | 
| 62 | 
            +
                  parts = []
         | 
| 63 | 
            +
                  parts << (self.description&.length&.positive? ? self.description.comment.strip : nil)
         | 
| 64 | 
            +
                  parts << (self.parent.nil? ? "class #{self.class_name()}" : "class #{self.class_name()} < #{self.parent}")
         | 
| 65 | 
            +
                  parts << self.render_attributes()
         | 
| 66 | 
            +
                  parts << (self.body&.length&.positive? ? self.body.indent(2) : nil)
         | 
| 67 | 
            +
                  parts << "end"
         | 
| 68 | 
            +
                  if self.modules.length > 0
         | 
| 69 | 
            +
                    body = parts.compact.join("\n").gsub(/\s+$/, "")
         | 
| 70 | 
            +
                    return Ginny.mod(body, self.modules)
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                  return parts.compact.join("\n").gsub(/\s+$/, "")
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                # @return [String]
         | 
| 76 | 
            +
                def render_attributes()
         | 
| 77 | 
            +
                  return nil unless self.attrs.length > 0
         | 
| 78 | 
            +
                  return self.attrs.map(&:render).join("\n").indent(2)
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                # @return [String]
         | 
| 82 | 
            +
                def class_name()
         | 
| 83 | 
            +
                  inflector = Dry::Inflector.new
         | 
| 84 | 
            +
                  return inflector.classify(inflector.underscore(self.name))
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                # @return [String]
         | 
| 88 | 
            +
                def file_name()
         | 
| 89 | 
            +
                  inflector = Dry::Inflector.new
         | 
| 90 | 
            +
                  return self.file_prefix + inflector.underscore(self.name) + ".rb"
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              end
         | 
| 94 | 
            +
            end
         | 
| @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            module Ginny
         | 
| 2 | 
            +
              # Used to generate a [method][2].
         | 
| 3 | 
            +
              #
         | 
| 4 | 
            +
              # [1]: https://ruby-doc.org/core-2.6.5/Method.html
         | 
| 5 | 
            +
              # [2]: https://ruby-doc.org/core-2.6.5/doc/syntax/methods_rdoc.html
         | 
| 6 | 
            +
              class Func
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                # Name of the function.
         | 
| 9 | 
            +
                # @return [String]
         | 
| 10 | 
            +
                attr_accessor :name
         | 
| 11 | 
            +
                # Description of the function. [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) is supported.
         | 
| 12 | 
            +
                # @return [String]
         | 
| 13 | 
            +
                attr_accessor :description
         | 
| 14 | 
            +
                # Return [type](https://rubydoc.info/gems/yard/file/docs/GettingStarted.md#Declaring_Types) of the function.
         | 
| 15 | 
            +
                # @return [String]
         | 
| 16 | 
            +
                attr_accessor :return_type
         | 
| 17 | 
            +
                # String to write into the body of the function.
         | 
| 18 | 
            +
                # @return [String]
         | 
| 19 | 
            +
                attr_accessor :body
         | 
| 20 | 
            +
                # List of modules to declare the function inside of.
         | 
| 21 | 
            +
                # @return [String]
         | 
| 22 | 
            +
                attr_accessor :modules
         | 
| 23 | 
            +
                # An array of {Ginny::Param}s.
         | 
| 24 | 
            +
                # @return [Array<Param>]
         | 
| 25 | 
            +
                attr_accessor :params
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                # @return [void]
         | 
| 28 | 
            +
                def initialize()
         | 
| 29 | 
            +
                  self.params = []
         | 
| 30 | 
            +
                  self.modules = []
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                # Constructor for a Func. Use `create`, not `new`.
         | 
| 34 | 
            +
                #
         | 
| 35 | 
            +
                # @param args [Hash<Symbol>]
         | 
| 36 | 
            +
                # @return [Ginny::Func]
         | 
| 37 | 
            +
                def self.create(args = {})
         | 
| 38 | 
            +
                  f = Ginny::Func.new()
         | 
| 39 | 
            +
                  f.name        = args[:name]
         | 
| 40 | 
            +
                  f.description = args[:description]
         | 
| 41 | 
            +
                  f.return_type = args[:return_type]
         | 
| 42 | 
            +
                  f.body        = args[:body]
         | 
| 43 | 
            +
                  f.modules     = args[:modules] unless args[:modules].nil?
         | 
| 44 | 
            +
                  f.params      = Ginny::Param.from_array(args[:params]) if args[:params]&.is_a?(Array)
         | 
| 45 | 
            +
                  return f
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                # @return [String]
         | 
| 49 | 
            +
                def render()
         | 
| 50 | 
            +
                  # return self.render_compact() if self.body.nil? && self.params.length == 0
         | 
| 51 | 
            +
                  parts = []
         | 
| 52 | 
            +
                  parts << self.render_description()
         | 
| 53 | 
            +
                  parts << self.params.map(&:render_doc).join("\n") unless self.params.length == 0
         | 
| 54 | 
            +
                  parts << self.render_return_type()
         | 
| 55 | 
            +
                  parts << "def " + self.name + self.render_params()
         | 
| 56 | 
            +
                  parts << self.body.indent(2) unless self.body.nil?
         | 
| 57 | 
            +
                  parts << "end"
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  body = parts.compact.join("\n").gsub(/\s+$/, "")
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  return Ginny.mod(body, self.modules) if self.modules.length > 0
         | 
| 62 | 
            +
                  return body
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                # @return [String]
         | 
| 66 | 
            +
                def render_params()
         | 
| 67 | 
            +
                  return "()" if self.params.length == 0
         | 
| 68 | 
            +
                  # if self.params.length >= 5
         | 
| 69 | 
            +
                  #   return "(\n" + self.params.map(&:render).join(",\n").indent(2) + "\n)"
         | 
| 70 | 
            +
                  # end
         | 
| 71 | 
            +
                  return "(" + self.params.map(&:render).join(", ") + ")"
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                # @return [String]
         | 
| 75 | 
            +
                def render_return_type
         | 
| 76 | 
            +
                  type = self.return_type.nil? ? "void" : self.return_type
         | 
| 77 | 
            +
                  return "# @return [#{type}]"
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                # @return [String]
         | 
| 81 | 
            +
                def render_description()
         | 
| 82 | 
            +
                  return (self.description&.length&.positive? ? self.description.comment.strip : nil)
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
            end
         |