diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..97bd577 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,79 @@ +name: CI + +on: + push: + branches: [master] + tags: + - "v*" + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: [2.7, 3.0, 3.1, 3.2, 3.3, 3.4] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby ${{ matrix.ruby }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: RuboCop (self‑check) + run: | + bundle exec rubocop + + - name: RSpec + run: bundle exec rspec + + release: + needs: test + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + + - name: Get gem name + id: gem_name + run: | + GEM_NAME=$(ruby -e "require 'rubygems'; spec = Gem::Specification.load(Dir.glob('*.gemspec').first); puts spec.name") + echo "gem_name=$GEM_NAME" >> $GITHUB_OUTPUT + + - name: Build gem + run: | + gem build *.gemspec + if [ ! -f ${{ steps.gem_name.outputs.gem_name }}-*.gem ]; then + echo "Gem build failed!" + exit 1 + fi + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: ${{ steps.gem_name.outputs.gem_name }}-*.gem + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Push gem to RubyGems.org + env: + RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} + run: gem push ${{ steps.gem_name.outputs.gem_name }}-*.gem + + - name: Deploy Documentation + if: success() + run: | + git clone git@github.com:unurgunite/${{ steps.gem_name.outputs.gem_name }}_docs.git ../${{ steps.gem_name.outputs.gem_name }}_docs + bundle exec rake docs:deploy + env: + SSH_AUTH_SOCK: /tmp/ssh_agent.sock diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 92e7b48..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Ruby - -on: - push: - branches: - - master - - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - name: Ruby ${{ matrix.ruby }} - strategy: - matrix: - ruby: - - '2.7.6' - - steps: - - uses: actions/checkout@v4 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - name: Run the default task - run: bundle exec rake diff --git a/.gitignore b/.gitignore index a362df0..b4179fe 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ # rspec failure tracking .rspec_status .idea +*.gem diff --git a/.rspec b/.rspec index 34c5164..41ef3e6 100644 --- a/.rspec +++ b/.rspec @@ -1,3 +1,4 @@ +--require rspec --format documentation --color --require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml index e3462a7..62e39f2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,13 +1,17 @@ -AllCops: - TargetRubyVersion: 2.6 +inherit_from: .rubocop_todo.yml -Style/StringLiterals: - Enabled: true - EnforcedStyle: double_quotes +plugins: + - rubocop-sorted_methods_by_call -Style/StringLiteralsInInterpolation: - Enabled: true - EnforcedStyle: double_quotes +AllCops: + NewCops: enable + TargetRubyVersion: 2.7 Layout/LineLength: Max: 120 + +Gemspec/DevelopmentDependencies: + Enabled: false + +SortedMethodsByCall/Waterfall: + Enabled: true diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..fb7bf9b --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,23 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2025-11-06 17:49:32 UTC using RuboCop version 1.81.7. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. +Metrics/AbcSize: + Max: 18 + +# Offense count: 4 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +# AllowedMethods: refine +Metrics/BlockLength: + Max: 179 + +# Offense count: 3 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +Metrics/MethodLength: + Max: 18 diff --git a/Gemfile b/Gemfile index c7527d6..7f4f5e9 100644 --- a/Gemfile +++ b/Gemfile @@ -1,12 +1,5 @@ # frozen_string_literal: true -source "https://rubygems.org" +source 'https://rubygems.org' -# Specify your gem's dependencies in code_generator.gemspec gemspec - -gem "rake", "~> 13.0" - -gem "rspec", "~> 3.0" - -gem "rubocop", "~> 1.21" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..788b19c --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,86 @@ +PATH + remote: . + specs: + advanced_code_generator (1.0.0) + +GEM + remote: https://rubygems.org/ + specs: + ast (2.4.3) + diff-lcs (1.6.2) + json (2.15.2) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + parallel (1.27.0) + parser (3.3.10.0) + ast (~> 2.4.1) + racc + prism (1.6.0) + racc (1.8.1) + rainbow (3.1.1) + rake (13.3.1) + regexp_parser (2.11.3) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.7) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.6) + rubocop (1.81.7) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.47.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.47.1) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-capybara (2.22.1) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-factory_bot (2.27.1) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-rspec (2.31.0) + rubocop (~> 1.40) + rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) + rubocop-rspec_rails (~> 2.28) + rubocop-rspec_rails (2.29.1) + rubocop (~> 1.61) + rubocop-sorted_methods_by_call (1.1.1) + lint_roller + rubocop (>= 1.72.0) + ruby-progressbar (1.13.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.1.0) + yard (0.9.37) + +PLATFORMS + arm64-darwin-24 + ruby + +DEPENDENCIES + advanced_code_generator! + rake (~> 13.0) + rspec (~> 3.0) + rubocop + rubocop-rspec (~> 2.24) + rubocop-sorted_methods_by_call + yard + +BUNDLED WITH + 2.3.25 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..ebfde50 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 unurgunite + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index d527f5d..b9c0d03 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,221 @@ -# CodeGenerator - -TODO: Delete this and the text below, and describe your gem - -Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/code_generator`. To experiment with that code, run `bin/console` for an interactive prompt. +Here's a **modern, professional README.md** that reflects your DSL-based code generator: + +# AdvancedCodeGenerator (ACG) + +![Repobeats](https://repobeats.axiom.co/api/embed/cf02cc6438367e8127e0aae8fc871c935844f4e8.svg "Project stats") +[![Gem Version](https://badge.fury.io/rb/code_generator.svg)](https://rubygems.org/gems/code_generator) +[![CI](https://github.com/unurgunite/code_generator/actions/workflows/ci.yml/badge.svg)](https://github.com/unurgunite/code_generator/actions) + +**A fluent DSL for generating Ruby classes with stubbed methods for testing and prototyping.** + +* [AdvancedCodeGenerator (ACG)](#advancedcodegenerator-acg) + * [Features](#features) + * [Installation](#installation) + * [Usage Examples](#usage-examples) + * [Basic Public Method](#basic-public-method) + * [Method with Parameters](#method-with-parameters) + * [Private and Protected Methods](#private-and-protected-methods) + * [Class Methods](#class-methods) + * [Random Value Generation](#random-value-generation) + * [Testing](#testing) + * [Development](#development) + * [Available Commands](#available-commands) + * [Release Process](#release-process) + * [Requirements](#requirements) + * [Contributing](#contributing) + * [License](#license) + * [Code of Conduct](#code-of-conduct) + +## Features + +- **Fluent DSL**: Clean, readable syntax for defining methods +- **Full visibility support**: Public, private, and protected instance methods +- **Class method support**: Public and private class methods +- **Parameter configuration**: Define required, optional, and keyword parameters +- **Smart return values**: Return specific objects or generate random values +- **Zero dependencies**: Pure Ruby, no external requirements ## Installation -TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org. +Add this line to your application's Gemfile: + +```ruby +gem 'advanced_code_generator' +``` + +And then execute: + +```bash +bundle install +``` + +Or install it yourself: + +```bash +gem install advanced_code_generator +``` + +## Usage Examples + +### Basic Public Method + +```ruby +generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :hello do |m| + m.returns "world" + end +end + +Klass = generator.build +obj = Klass.new +obj.hello # => "world" +``` + +### Method with Parameters + +```ruby +generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :greet do |m| + m.required :name + m.optional :greeting, default: "Hello" + m.keyword_required :format + m.returns true + end +end + +Klass = generator.build +obj = Klass.new +obj.greet("Alice", format: :json) # => true +obj.greet("Bob", "Hi", format: :xml) # => true +``` + +### Private and Protected Methods + +```ruby +generator = AdvancedCodeGenerator::Generator.new do |g| + g.private_method :secret_calculation do |m| + m.returns 42 + end + + g.protected_method :internal_logic do |m| + m.returns "protected result" + end +end + +Klass = generator.build +obj = Klass.new +obj.send(:secret_calculation) # => 42 +# obj.secret_calculation # => NoMethodError + +# Protected method access through subclass +Subclass = Class.new(Klass) do + def access_protected + internal_logic + end +end +Subclass.new.access_protected # => "protected result" +``` + +### Class Methods + +```ruby +generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_class_method :factory do |m| + m.returns "class helper" + end + + g.private_class_method :setup do |m| + m.returns "private setup" + end +end + +Klass = generator.build +Klass.factory # => "class helper" +Klass.send(:setup) # => "private setup" +``` + +### Random Value Generation + +```ruby +generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :random_int do |m| + m.returns Integer + m.generate true + end + + g.public_method :random_string do |m| + m.returns String + m.generate true + end +end + +Klass = generator.build +obj = Klass.new +obj.random_int # => 42891 (random integer) +obj.random_string # => "aB3xY9zK2m" (random string) +``` + +## Testing + +Run the test suite: + +```bash +bundle exec rspec +``` + +## Development -Install the gem and add to the application's Gemfile by executing: +After checking out the repo, run: - $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG +```bash +bin/setup +``` -If bundler is not being used to manage dependencies, install the gem by executing: +This will install dependencies and start an interactive console. - $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG +### Available Commands -## Usage +- `bin/console` - Interactive development console +- `bin/setup` - Install dependencies and build gem +- `bundle exec rake` - Run tests and linting -TODO: Write usage instructions here +### Release Process -## Development +1. Update version in `lib/code_generator/version.rb` +2. Create and push a git tag: `git tag v0.1.0 && git push origin v0.1.0` +3. GitHub Actions will automatically: + - Build the gem + - Publish to RubyGems.org + - Create a GitHub release -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. +## Requirements -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). +- **Ruby**: >= 2.7.0 +- **No external dependencies** ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/code_generator. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/code_generator/blob/master/CODE_OF_CONDUCT.md). +Bug reports and pull requests are welcome! Please follow these guidelines: + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -am 'Add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a pull request + +Please ensure your code passes all tests and follows the existing style. + +## License + +The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). ## Code of Conduct -Everyone interacting in the CodeGenerator project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/code_generator/blob/master/CODE_OF_CONDUCT.md). +Everyone interacting with this project is expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md). + +--- + +> **Note**: This gem is designed for **testing and prototyping**. Generated methods accept any parameters and return +> configured values, making it perfect for creating test doubles and stubs. + +``` diff --git a/Rakefile b/Rakefile index cca7175..4964751 100644 --- a/Rakefile +++ b/Rakefile @@ -1,11 +1,11 @@ # frozen_string_literal: true -require "bundler/gem_tasks" -require "rspec/core/rake_task" +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) -require "rubocop/rake_task" +require 'rubocop/rake_task' RuboCop::RakeTask.new diff --git a/bin/console b/bin/console index 4fb2129..48a26e2 100755 --- a/bin/console +++ b/bin/console @@ -1,11 +1,11 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require "bundler/setup" -require "code_generator" +require 'bundler/setup' +require 'advanced_code_generator' # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. -require "irb" +require 'irb' IRB.start(__FILE__) diff --git a/bin/release b/bin/release new file mode 100755 index 0000000..2e1da4b --- /dev/null +++ b/bin/release @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +bash <(curl -sSL https://raw.githubusercontent.com/unurgunite/release_gem/refs/heads/master/release) diff --git a/bin/setup b/bin/setup index dce67d8..4482f7b 100755 --- a/bin/setup +++ b/bin/setup @@ -3,6 +3,19 @@ set -euo pipefail IFS=$'\n\t' set -vx +PROJECT_NAME='advanced_code_generator' +if pwd | grep $PROJECT_NAME; then + cd "${PWD%$PROJECT_NAME*}$PROJECT_NAME" +else + echo "Change your working directory to the $PROJECT_NAME dir or its relative directories. Exiting with error code 1" + exit 1 +fi + bundle install -# Do any other automated setup that you need to do here +gem build "$PROJECT_NAME".gemspec +GEM_VERSION=$(bundle info $PROJECT_NAME | head -n 1 | sed 's/.*(\(.*\))/\1/') +GEM_ARCHIVE="$PROJECT_NAME-$GEM_VERSION.gem" +gem install "$GEM_ARCHIVE" + +# bin/console diff --git a/code_generator.gemspec b/code_generator.gemspec index 7a23a85..cda8a60 100644 --- a/code_generator.gemspec +++ b/code_generator.gemspec @@ -1,23 +1,26 @@ # frozen_string_literal: true -require_relative "lib/code_generator/version" +require_relative 'lib/advanced_code_generator/version' Gem::Specification.new do |spec| - spec.name = "code_generator" - spec.version = CodeGenerator::VERSION - spec.authors = ["unurgunite"] - spec.email = [""] + spec.name = 'advanced_code_generator' + spec.version = AdvancedCodeGenerator::VERSION + spec.authors = ['unurgunite'] + spec.email = ['senpaiguru1488@gmail.com'] - spec.summary = "Code generation tool based on preferences." - spec.description = "This gem gives an ability to generate code based on preferences. You can use it to skip a boring routine with writing tests, some classes for other purposes or just for fun." - spec.homepage = "https://github.com/unurgunite/code_generator." - spec.required_ruby_version = ">= 2.6.0" + spec.summary = 'Code generation tool based on preferences.' + spec.description = <<~TEXT + This gem makes it possible to generate code based on preferences. You can use it to avoid the boring routine of writing tests, use some classes for other purposes, or just for fun. + TEXT + spec.homepage = 'https://github.com/unurgunite/advanced_code_generator.' + spec.required_ruby_version = '>= 2.7.0' - spec.metadata["allowed_push_host"] = "https://rubygems.org" + spec.metadata['allowed_push_host'] = 'https://rubygems.org' - spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = "https://github.com/unurgunite/code_generator" - spec.metadata["changelog_uri"] = "https://github.com/unurgunite/code_generator/CHANGELOG.md" + spec.metadata['homepage_uri'] = spec.homepage + spec.metadata['source_code_uri'] = 'https://github.com/unurgunite/advanced_code_generator' + spec.metadata['changelog_uri'] = 'https://github.com/unurgunite/advanced_code_generator/CHANGELOG.md' + spec.metadata['rubygems_mfa_required'] = 'true' # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. @@ -27,12 +30,14 @@ Gem::Specification.new do |spec| f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile]) end end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] - - # Uncomment to register a new dependency of your gem - # spec.add_dependency "example-gem", "~> 1.0" + spec.require_paths = ['lib'] + + spec.add_development_dependency 'rake', '~> 13.0' + spec.add_development_dependency 'rspec', '~> 3.0' + spec.add_development_dependency 'rubocop' + spec.add_development_dependency 'rubocop-rspec', '~> 2.24' + spec.add_development_dependency 'rubocop-sorted_methods_by_call' + spec.add_development_dependency 'yard' # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html diff --git a/lib/advanced_code_generator.rb b/lib/advanced_code_generator.rb new file mode 100644 index 0000000..accc994 --- /dev/null +++ b/lib/advanced_code_generator.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'securerandom' +require_relative 'advanced_code_generator/version' +require_relative 'advanced_code_generator/parameter' +require_relative 'advanced_code_generator/method_config' +require_relative 'advanced_code_generator/generator' + +module AdvancedCodeGenerator + class Error < StandardError; end +end diff --git a/lib/advanced_code_generator/generator.rb b/lib/advanced_code_generator/generator.rb new file mode 100644 index 0000000..8b10f05 --- /dev/null +++ b/lib/advanced_code_generator/generator.rb @@ -0,0 +1,298 @@ +# frozen_string_literal: true + +module AdvancedCodeGenerator + # Generates Ruby classes with stubbed methods using a fluent DSL. + # + # This class provides a domain-specific language (DSL) for defining + # methods with various visibility levels, parameters, and return values. + # It's primarily designed for testing and prototyping scenarios where + # you need to create mock objects or stub classes quickly. + # + # @example Basic usage + # generator = CodeGenerator::Generator.new do |g| + # g.public_method :hello do |m| + # m.returns "world" + # end + # end + # Klass = generator.build + # obj = Klass.new + # obj.hello # => "world" + # + # @example Method with parameters + # generator = CodeGenerator::Generator.new do |g| + # g.public_method :greet do |m| + # m.required :name + # m.optional :greeting, default: "Hello" + # m.returns true + # end + # end + class Generator + # @return [Array] List of instance method configurations + attr_reader :methods + + # @return [Array] List of class method configurations + attr_reader :class_methods + + # Initializes a new Generator instance with empty method collections. + # + # This constructor is typically called internally by {Generator.new} + # and should not be called directly in most cases. + # + # @return [void] + def initialize + @methods = [] + @class_methods = [] + end + + # Creates a new Generator instance and evaluates the given block in its context. + # + # This is the primary entry point for using the DSL. The block parameter + # provides access to the generator's DSL methods for defining methods. + # + # @yield [generator] The generator instance for DSL configuration + # @yieldparam generator [CodeGenerator::Generator] The current generator instance + # @return [AdvancedCodeGenerator::Generator] A configured generator instance + # + # @example + # generator = CodeGenerator::Generator.new do |g| + # g.public_method :test_method do |m| + # m.returns "test" + # end + # end + def self.new(&block) + generator = allocate + generator.__send__(:initialize) + generator.instance_eval(&block) if block + generator + end + + # Builds and returns a new Ruby class with all configured methods defined. + # + # This method creates an anonymous class and defines all the methods + # that were configured through the DSL. The returned class can be + # instantiated and used like any other Ruby class. + # + # @return [Class] A new class with all configured methods + # + # @example + # generator = CodeGenerator::Generator.new do |g| + # g.public_method :hello do |m| + # m.returns "world" + # end + # end + # Klass = generator.build + # obj = Klass.new + # puts obj.hello # => "world" + def build + klass = Class.new + define_instance_methods(klass) + define_class_methods(klass) + klass + end + + # Defines a public instance method on the generated class. + # + # @param name [Symbol, String] The name of the method to define + # @yield [method_config] Configuration block for the method + # @yieldparam method_config [CodeGenerator::MethodConfig] Method configuration object + # @return [void] + # + # @example + # g.public_method :calculate do |m| + # m.required :x + # m.optional :y, default: 10 + # m.returns 42 + # end + def public_method(name, &block) + method_config = MethodConfig.new(name, :public, &block) + @methods << method_config + end + + # Defines a private instance method on the generated class. + # + # Private methods can only be called within the class or its instances + # using {Object#send} or from within other instance methods. + # + # @param name [Symbol, String] The name of the method to define + # @yield [method_config] Configuration block for the method + # @yieldparam method_config [CodeGenerator::MethodConfig] Method configuration object + # @return [void] + # + # @example + # g.private_method :internal_calculation do |m| + # m.returns "private result" + # end + def private_method(name, &block) + method_config = MethodConfig.new(name, :private, &block) + @methods << method_config + end + + # Defines a protected instance method on the generated class. + # + # Protected methods can be called by instances of the same class + # or its subclasses, but not from outside the inheritance hierarchy. + # + # @param name [Symbol, String] The name of the method to define + # @yield [method_config] Configuration block for the method + # @yieldparam method_config [CodeGenerator::MethodConfig] Method configuration object + # @return [void] + # + # @example + # g.protected_method :shared_logic do |m| + # m.returns "protected result" + # end + def protected_method(name, &block) + method_config = MethodConfig.new(name, :protected, &block) + @methods << method_config + end + + # Defines a public class method on the generated class. + # + # Class methods are called on the class itself rather than instances. + # + # @param name [Symbol, String] The name of the method to define + # @yield [method_config] Configuration block for the method + # @yieldparam method_config [CodeGenerator::MethodConfig] Method configuration object + # @return [void] + # + # @example + # g.public_class_method :factory do |m| + # m.returns "class helper" + # end + def public_class_method(name, &block) + method_config = MethodConfig.new(name, :public_class, &block) + @class_methods << method_config + end + + # Defines a private class method on the generated class. + # + # Private class methods can only be called within the class context + # using {Object#send} on the class itself. + # + # @param name [Symbol, String] The name of the method to define + # @yield [method_config] Configuration block for the method + # @yieldparam method_config [CodeGenerator::MethodConfig] Method configuration object + # @return [void] + # + # @example + # g.private_class_method :internal_setup do |m| + # m.returns "private setup" + # end + def private_class_method(name, &block) + method_config = MethodConfig.new(name, :private_class, &block) + @class_methods << method_config + end + + private + + # Defines all configured instance methods on the target class. + # + # @param klass [Class] The class to define methods on + # @return [void] + def define_instance_methods(klass) + return unless @methods + + @methods.each do |method_config| + case method_config.visibility + when :public + define_method_with_params(klass, method_config, :define_method) + when :private + define_method_with_params(klass, method_config, :define_method) + klass.send(:private, method_config.name) + when :protected + define_method_with_params(klass, method_config, :define_method) + klass.send(:protected, method_config.name) + end + end + end + + # Defines all configured class methods on the target class. + # + # @param klass [Class] The class to define class methods on + # @return [void] + def define_class_methods(klass) + return unless class_methods + + class_methods.each do |method_config| + case method_config.visibility + when :public_class + define_method_with_params(klass.singleton_class, method_config, :define_method) + when :private_class + define_method_with_params(klass.singleton_class, method_config, :define_method) + klass.singleton_class.send(:private, method_config.name) + end + end + end + + # Defines a single method on the target class with proper parameter handling. + # + # This method handles both simple methods (no parameters) and complex + # methods (with parameter definitions) by either using define_method + # with a proc or generating a method definition string with class_eval. + # + # @param target_class [Class, #singleton_class] The class to define the method on + # @param method_config [AdvancedCodeGenerator::MethodConfig] The method configuration + # @param define_method_name [Symbol] The method used to define methods (:define_method) + # @return [void] + def define_method_with_params(target_class, method_config, define_method_name) + if method_config.parameters.empty? + return_value = calculate_return_value(method_config) + target_class.send(define_method_name, method_config.name) do |*_args, **_kwargs| + return_value + end + else + # Generate actual method with proper parameter validation + param_string = method_config.parameters.map(&:to_ruby_param).join(', ') + return_value = calculate_return_value(method_config) + + # Create the method definition as a string + method_code = "def #{method_config.name}(#{param_string}); #{return_value.inspect}; end" + + # Evaluate it in the target class context + target_class.class_eval(method_code) + end + end + + # Calculates the actual return value for a method configuration. + # + # Handles both static return values and random object generation + # based on the method configuration settings. + # + # @param method_config [AdvancedCodeGenerator::MethodConfig] The method configuration + # @return [Object, nil] The calculated return value + def calculate_return_value(method_config) + if method_config.generate_random && method_config.return_value.is_a?(Class) + generate_random_object(method_config.return_value) + else + method_config.return_value + end + end + + # Generates a random object of the specified class type. + # + # Supports Integer, String, and Symbol types with appropriate + # random generation logic. For unsupported classes, returns + # the class itself. + # + # @param klass [Class] The class to generate a random instance of + # @return [Object] A random object of the specified type, or the class itself + # + # @example + # generate_random_object(Integer) # => 42891 + # generate_random_object(String) # => "aB3xY9zK2m" + # generate_random_object(Symbol) # => :random_symbol + # generate_random_object(Object) # => Object + def generate_random_object(klass) + case klass.name + when 'Integer' + rand(1..1_000_000) + when 'String' + SecureRandom.alphanumeric(10) + when 'Symbol' + SecureRandom.alphanumeric(10).to_sym + else + klass + end + end + end +end diff --git a/lib/advanced_code_generator/method_config.rb b/lib/advanced_code_generator/method_config.rb new file mode 100644 index 0000000..b52f758 --- /dev/null +++ b/lib/advanced_code_generator/method_config.rb @@ -0,0 +1,187 @@ +# frozen_string_literal: true + +module AdvancedCodeGenerator + # Represents the configuration for a single method definition. + # + # This class encapsulates all the metadata needed to define a method, + # including its name, visibility, parameters, return value, and generation + # settings. It's used internally by {CodeGenerator::Generator} to store + # method configuration data collected through the DSL. + # + # @example Method configuration usage + # config = CodeGenerator::MethodConfig.new(:calculate, :public) do |m| + # m.required :x + # m.optional :y, default: 10 + # m.returns 42 + # end + # + # @see CodeGenerator::Generator + # @see CodeGenerator::Parameter + class MethodConfig + # @return [Symbol] The name of the method to be defined + attr_reader :name + + # @return [Symbol] The visibility level (:public, :private, :protected, :public_class, :private_class) + attr_reader :visibility + + # @return [Array] List of method parameters + attr_reader :parameters + + # @return [Object, nil] The value that the method should return + attr_reader :return_value + + # @return [Boolean] Whether to generate random values for class return types + attr_reader :generate_random + + # Valid visibility options for method definitions. + # + # @return [Array] + VALID_VISIBILITIES = %i[public private protected public_class private_class].freeze + + # Initializes a new MethodConfig instance. + # + # @param name [Symbol, String] The name of the method to configure + # @param visibility [Symbol] The visibility level (must be one of {VALID_VISIBILITIES}) + # @yield [method_config] Configuration block for method parameters and settings + # @yieldparam method_config [CodeGenerator::MethodConfig] The current method configuration instance + # @raise [ArgumentError] If name is not a Symbol/String or visibility is invalid + # + # @example + # config = CodeGenerator::MethodConfig.new(:my_method, :public) do |m| + # m.required :param1 + # m.returns "result" + # end + def initialize(name, visibility) + raise ArgumentError, 'Method name must be a Symbol or String' unless name.is_a?(Symbol) || name.is_a?(String) + raise ArgumentError, "Invalid visibility: #{visibility}" unless VALID_VISIBILITIES.include?(visibility) + + @name = name.to_sym + @visibility = visibility + @parameters = [] + @return_value = nil + @generate_random = false + + yield self if block_given? + end + + # Adds a required positional parameter to the method. + # + # Required parameters must be provided when calling the method. + # + # @param param_name [Symbol] The name of the required parameter + # @return [void] + # @raise [ArgumentError] If param_name is not a Symbol + # + # @example + # m.required :user_id + # # Generates: def method_name(user_id) + def required(param_name) + validate_param_name(param_name) + parameters << Parameter.new(:required, param_name) + end + + # Adds an optional positional parameter to the method. + # + # Optional parameters have a default value and can be omitted when calling the method. + # + # @param param_name [Symbol] The name of the optional parameter + # @param default [Object] The default value for the parameter (defaults to nil) + # @return [void] + # @raise [ArgumentError] If param_name is not a Symbol + # + # @example + # m.optional :options, default: {} + # # Generates: def method_name(options = {}) + def optional(param_name, default: nil) + validate_param_name(param_name) + parameters << Parameter.new(:optional, param_name, default: default) + end + + # Adds a required keyword parameter to the method. + # + # Required keyword parameters must be provided as named arguments when calling the method. + # + # @param param_name [Symbol] The name of the required keyword parameter + # @return [void] + # @raise [ArgumentError] If param_name is not a Symbol + # + # @example + # m.keyword_required :format + # # Generates: def method_name(format:) + def keyword_required(param_name) + validate_param_name(param_name) + parameters << Parameter.new(:keyword_required, param_name) + end + + # Adds an optional keyword parameter to the method. + # + # Optional keyword parameters have a default value and can be omitted when calling the method. + # + # @param param_name [Symbol] The name of the optional keyword parameter + # @param default [Object] The default value for the parameter (defaults to nil) + # @return [void] + # @raise [ArgumentError] If param_name is not a Symbol + # + # @example + # m.keyword :timeout, default: 30 + # # Generates: def method_name(timeout: 30) + def keyword(param_name, default: nil) + validate_param_name(param_name) + parameters << Parameter.new(:keyword, param_name, default: default) + end + + # Sets the return value for the method. + # + # The method will return this value when called. If combined with {#generate}, + # and the return value is a Class, it will generate random instances of that class. + # + # @param value [Object] The value to return from the method + # @return [Object] The provided value + # + # @example + # m.returns "success" + # m.returns Integer # Will generate random integers if #generate is true + def returns(value) + self.return_value = value + end + + # Enables random value generation for class return types. + # + # When enabled and the return value is a Class (like Integer, String, Symbol), + # the method will return random instances of that class instead of the class itself. + # + # @param value [Boolean] Whether to enable random generation (defaults to true) + # @return [Boolean] The provided value + # + # @example + # m.returns Integer + # m.generate true + # # Method will return random integers like 42891, not the Integer class + def generate(value: true) + self.generate_random = value + end + + private + + # @!attribute [rw] parameters + # @return [Array] + attr_writer :parameters + + # @!attribute [rw] return_value + # @return [Object, nil] + attr_writer :return_value + + # @!attribute [rw] generate_random + # @return [Boolean] + attr_writer :generate_random + + # Validates that a parameter name is a Symbol. + # + # @param name [Object] The parameter name to validate + # @raise [ArgumentError] If name is not a Symbol + # @return [void] + def validate_param_name(name) + raise ArgumentError, 'Parameter name must be a Symbol' unless name.is_a?(Symbol) + end + end +end diff --git a/lib/advanced_code_generator/parameter.rb b/lib/advanced_code_generator/parameter.rb new file mode 100644 index 0000000..67cb489 --- /dev/null +++ b/lib/advanced_code_generator/parameter.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +module AdvancedCodeGenerator + # Represents a single method parameter configuration. + # + # This class encapsulates the metadata for a method parameter, including + # its type (required, optional, keyword, etc.), name, and default value. + # It's used internally by {CodeGenerator::MethodConfig} to store parameter + # definitions that are later converted to Ruby method signatures. + # + # @example Parameter usage + # param = CodeGenerator::Parameter.new(:required, :user_id) + # param.to_ruby_param # => "user_id" + # + # @see CodeGenerator::MethodConfig + class Parameter + # Valid parameter types for method definitions. + # + # @return [Array] + VALID_TYPES = %i[required optional keyword_required keyword].freeze + + # @return [Symbol] The type of parameter (:required, :optional, :keyword_required, :keyword) + attr_reader :type + + # @return [Symbol] The name of the parameter + attr_reader :name + + # @return [Object, nil] The default value for optional parameters (nil for required parameters) + attr_reader :default + + # Initializes a new Parameter instance. + # + # @param type [Symbol] The parameter type (must be one of {VALID_TYPES}) + # @param name [Symbol] The parameter name + # @param default [Object, nil] The default value for optional parameters (defaults to nil) + # @raise [ArgumentError] If type is invalid or name is not a Symbol + # + # @example + # # Required parameter + # param = CodeGenerator::Parameter.new(:required, :id) + # + # # Optional parameter with default + # param = CodeGenerator::Parameter.new(:optional, :options, default: {}) + # + # # Required keyword parameter + # param = CodeGenerator::Parameter.new(:keyword_required, :format) + # + # # Optional keyword parameter + # param = CodeGenerator::Parameter.new(:keyword, :timeout, default: 30) + def initialize(type, name, default: nil) + raise ArgumentError, "Invalid parameter type: #{type}" unless VALID_TYPES.include?(type) + raise ArgumentError, 'Parameter name must be a Symbol' unless name.is_a?(Symbol) + + @type = type + @name = name + @default = default + end + + # Converts the parameter configuration to a Ruby method parameter string. + # + # This method generates the appropriate Ruby syntax for the parameter + # based on its type and configuration, which is used when defining + # methods with {CodeGenerator::Generator}. + # + # @return [String] The Ruby parameter string + # + # @example + # CodeGenerator::Parameter.new(:required, :name).to_ruby_param + # # => "name" + # + # CodeGenerator::Parameter.new(:optional, :options, default: {}).to_ruby_param + # # => "options = {}" + # + # CodeGenerator::Parameter.new(:keyword_required, :format).to_ruby_param + # # => "format:" + # + # CodeGenerator::Parameter.new(:keyword, :timeout, default: 30).to_ruby_param + # # => "timeout: 30" + def to_ruby_param + case type + when :required + name.to_s + when :optional + if default.nil? + "#{name} = nil" + else + "#{name} = #{default.inspect}" + end + when :keyword_required + "#{name}:" + when :keyword + if default.nil? + "#{name}: nil" + else + "#{name}: #{default.inspect}" + end + end + end + + # Returns the parameter name as a Symbol. + # + # This is a convenience method that simply returns the {#name} attribute. + # + # @return [Symbol] The parameter name + # + # @example + # param = CodeGenerator::Parameter.new(:required, :user_id) + # param.ruby_param_name # => :user_id + def ruby_param_name + name + end + end +end diff --git a/lib/advanced_code_generator/version.rb b/lib/advanced_code_generator/version.rb new file mode 100644 index 0000000..6ec0105 --- /dev/null +++ b/lib/advanced_code_generator/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module AdvancedCodeGenerator + VERSION = '1.0.0' +end diff --git a/lib/code_generator.rb b/lib/code_generator.rb deleted file mode 100644 index 1e4ef86..0000000 --- a/lib/code_generator.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -require_relative "code_generator/version" - -module CodeGenerator - class Error < StandardError; end - # Your code goes here... -end diff --git a/lib/code_generator/version.rb b/lib/code_generator/version.rb deleted file mode 100644 index 211aae1..0000000 --- a/lib/code_generator/version.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -module CodeGenerator - VERSION = "0.1.0" -end diff --git a/rakelib/docs.rake b/rakelib/docs.rake new file mode 100644 index 0000000..466ae2a --- /dev/null +++ b/rakelib/docs.rake @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'English' +require 'yard' +require 'fileutils' + +GEM_NAME = Bundler.load_gemspec(Dir.glob('*.gemspec').first).name +DOCS_REPO_NAME = "#{GEM_NAME}_docs" +DOCS_REPO_PATH = "../#{DOCS_REPO_NAME}" + +namespace :docs do + desc 'Generate new docs and push them to repo' + task generate: :clean do + puts 'Generating docs...' + YARD::CLI::Yardoc.run + puts 'OK!' + end + + desc 'Clean existing docs' + task :clean do + if File.directory?('doc') + FileUtils.rm_rf('doc') + puts 'Cleaned existing docs directory' + end + end + + desc 'Pushes docs to github' + task push: :generate do + unless File.directory?(DOCS_REPO_PATH) + puts "Error: Docs repo not found at #{DOCS_REPO_PATH}" + puts 'Please clone the docs repo first:' + puts " git clone git@github.com:unurgunite/#{DOCS_REPO_NAME}.git #{DOCS_REPO_PATH}" + exit 1 + end + + puts "Copying docs to #{DOCS_REPO_PATH}..." + FileUtils.mkdir_p('doc') unless File.directory?('doc') + FileUtils.cp_r('doc/.', DOCS_REPO_PATH) + + puts 'Changing dir...' + Dir.chdir(DOCS_REPO_PATH) do + puts 'Checking git status...' + status_output = `git status --porcelain` + + if status_output.strip.empty? + puts 'No changes to commit' + else + puts 'Committing git changes...' + puts `git add .` + commit_result = `git commit -m "Update docs for #{GEM_NAME} #{Time.now.utc.strftime('%Y-%m-%d %H:%M:%S UTC')}"` + puts commit_result + + if $CHILD_STATUS.success? + puts 'Pushing to GitHub...' + push_result = `git push origin master 2>&1` + puts push_result + if $CHILD_STATUS.success? + puts 'Docs successfully pushed!' + else + puts 'Push failed!' + exit 1 + end + else + puts 'Commit failed!' + exit 1 + end + end + end + end + + desc 'Generate and push docs in one command' + task deploy: :push +end diff --git a/sig/code_generator.rbs b/sig/code_generator.rbs deleted file mode 100644 index ae63f2f..0000000 --- a/sig/code_generator.rbs +++ /dev/null @@ -1,4 +0,0 @@ -module CodeGenerator - VERSION: String - # See the writing guide of rbs: https://github.com/ruby/rbs#guides -end diff --git a/spec/advanced_code_generator/generator_spec.rb b/spec/advanced_code_generator/generator_spec.rb new file mode 100644 index 0000000..aeaed7b --- /dev/null +++ b/spec/advanced_code_generator/generator_spec.rb @@ -0,0 +1,226 @@ +# frozen_string_literal: true + +RSpec.describe AdvancedCodeGenerator::Generator do + describe 'public methods' do + it 'creates a simple public method' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :hello do |m| + m.returns 'world' + end + end + + klass = generator.build + obj = klass.new + expect(obj.hello).to eq 'world' + end + + it 'creates public method with required parameters' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :greet do |m| + m.required :name + m.returns 'Hello' + end + end + + klass = generator.build + obj = klass.new + + # Should accept the parameter + expect(obj.greet('Alice')).to eq 'Hello' + + # Should raise ArgumentError if parameter missing + expect { obj.greet }.to raise_error(ArgumentError) + end + + it 'creates public method with optional parameters' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :greet do |m| + m.required :name + m.optional :greeting, default: 'Hello' + m.returns 'done' + end + end + + klass = generator.build + obj = klass.new + + expect(obj.greet('Alice')).to eq 'done' + expect(obj.greet('Alice', 'Hi')).to eq 'done' + end + + it 'creates public method with keyword parameters' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :process do |m| + m.required :id + m.keyword :format, default: 'json' + m.keyword_required :action + m.returns true + end + end + + klass = generator.build + obj = klass.new + + expect(obj.process(1, action: :create)).to eq true + expect(obj.process(1, format: 'xml', action: :update)).to eq true + + # Should raise error if required keyword missing + expect { obj.process(1) }.to raise_error(ArgumentError) + end + end + + describe 'private methods' do + it 'creates a private method' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.private_method :secret do |m| + m.returns 42 + end + end + + klass = generator.build + obj = klass.new + + expect(obj.send(:secret)).to eq 42 + expect { obj.secret }.to raise_error(NoMethodError) + end + + it 'creates private method with parameters' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.private_method :calculate do |m| + m.required :x + m.required :y + m.returns 100 + end + end + + klass = generator.build + obj = klass.new + + expect(obj.send(:calculate, 1, 2)).to eq 100 + expect { obj.send(:calculate, 1) }.to raise_error(ArgumentError) + end + end + + describe 'protected methods' do + it 'creates a protected method' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.protected_method :internal do |m| + m.returns 'protected' + end + end + + klass = generator.build + + # Create a subclass to test protected method access + subclass = Class.new(klass) do + def access_protected + internal + end + end + + obj = subclass.new + expect(obj.access_protected).to eq 'protected' + end + end + + describe 'class methods' do + it 'creates public class method' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_class_method :helper do |m| + m.returns 'class helper' + end + end + + klass = generator.build + expect(klass.helper).to eq 'class helper' + end + + it 'creates private class method' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.private_class_method :internal_class_method do |m| + m.returns 'private class' + end + end + + klass = generator.build + + expect(klass.send(:internal_class_method)).to eq 'private class' + expect { klass.internal_class_method }.to raise_error(NoMethodError) + end + end + + describe 'random generation' do + it 'generates random integers' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :random_int do |m| + m.returns Integer + m.generate value: true + end + end + + klass = generator.build + obj = klass.new + result = obj.random_int + + expect(result).to be_an(Integer) + expect(result).to be > 0 + end + + it 'generates random strings' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :random_string do |m| + m.returns String + m.generate value: true + end + end + + klass = generator.build + obj = klass.new + result = obj.random_string + + expect(result).to be_a(String) + expect(result.length).to eq 10 + end + + it 'generates random symbols' do + generator = AdvancedCodeGenerator::Generator.new do |g| + g.public_method :random_symbol do |m| + m.returns Symbol + m.generate value: true + end + end + + klass = generator.build + obj = klass.new + result = obj.random_symbol + + expect(result).to be_a(Symbol) + end + end + + describe 'parameter validation' do + it 'raises error for invalid parameter name' do + expect do + AdvancedCodeGenerator::Generator.new do |g| + g.public_method :test do |m| + m.required 'string_name' # Should be symbol + end + end + end.to raise_error(ArgumentError, 'Parameter name must be a Symbol') + end + + it 'raises error for invalid parameter type' do + expect do + AdvancedCodeGenerator::Parameter.new(:invalid_type, :name) + end.to raise_error(ArgumentError, 'Invalid parameter type: invalid_type') + end + end + + describe 'visibility validation' do + it 'raises error for invalid visibility' do + expect do + AdvancedCodeGenerator::MethodConfig.new(:test, :invalid_visibility) + end.to raise_error(ArgumentError, 'Invalid visibility: invalid_visibility') + end + end +end diff --git a/spec/advanced_code_generator_spec.rb b/spec/advanced_code_generator_spec.rb new file mode 100644 index 0000000..2f52141 --- /dev/null +++ b/spec/advanced_code_generator_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +RSpec.describe AdvancedCodeGenerator do + it 'has a version number' do + expect(AdvancedCodeGenerator::VERSION).not_to be nil + end +end diff --git a/spec/code_generator_spec.rb b/spec/code_generator_spec.rb deleted file mode 100644 index 6e5f53e..0000000 --- a/spec/code_generator_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe CodeGenerator do - it "has a version number" do - expect(CodeGenerator::VERSION).not_to be nil - end - - it "does something useful" do - expect(false).to eq(true) - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2a86a22..1699d44 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require "code_generator" +require 'advanced_code_generator' RSpec.configure do |config| # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = ".rspec_status" + config.example_status_persistence_file_path = '.rspec_status' # Disable RSpec exposing methods globally on `Module` and `main` config.disable_monkey_patching!