From c0b8ab9705617bfc01ff6779d06a009a77cc2f4a Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:26:00 +0300 Subject: [PATCH 01/39] Added setup binary --- bin/setup | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/bin/setup b/bin/setup index dce67d8..88a0436 100755 --- a/bin/setup +++ b/bin/setup @@ -3,6 +3,19 @@ set -euo pipefail IFS=$'\n\t' set -vx +PROJECT_NAME='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 From 8681ab74bad04d206933757621f89535c209906b Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:26:22 +0300 Subject: [PATCH 02/39] Added `rubocop_rspec` --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index c7527d6..4d187c3 100644 --- a/Gemfile +++ b/Gemfile @@ -10,3 +10,5 @@ gem "rake", "~> 13.0" gem "rspec", "~> 3.0" gem "rubocop", "~> 1.21" + +gem "rubocop-rspec", "~> 2.24" From 3f746589dbdc90ff3d2fbab8e6b7e5da2ffff871 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:26:46 +0300 Subject: [PATCH 03/39] Added `extensions` folder --- lib/extensions/extensions.rb | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 lib/extensions/extensions.rb diff --git a/lib/extensions/extensions.rb b/lib/extensions/extensions.rb new file mode 100644 index 0000000..87a066c --- /dev/null +++ b/lib/extensions/extensions.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative "validator" From b1773daa7241e618185077a9195932109f660a7c Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:27:31 +0300 Subject: [PATCH 04/39] Added code generator --- lib/code_generator.rb | 6 +- lib/code_generator/generator.rb | 139 +++++++++++++++++++ sig/code_generator/generator.rbs | 14 ++ spec/code_generator/generator_spec.rb | 192 ++++++++++++++++++++++++++ 4 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 lib/code_generator/generator.rb create mode 100644 sig/code_generator/generator.rbs create mode 100644 spec/code_generator/generator_spec.rb diff --git a/lib/code_generator.rb b/lib/code_generator.rb index 1e4ef86..31a8068 100644 --- a/lib/code_generator.rb +++ b/lib/code_generator.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true -require_relative "code_generator/version" - module CodeGenerator class Error < StandardError; end # Your code goes here... end + +require_relative 'extensions/extensions' +require_relative "code_generator/version" +require_relative 'code_generator/generator' diff --git a/lib/code_generator/generator.rb b/lib/code_generator/generator.rb new file mode 100644 index 0000000..457c631 --- /dev/null +++ b/lib/code_generator/generator.rb @@ -0,0 +1,139 @@ +# frozen_string_literal: true + +require "securerandom" + +module CodeGenerator # :nodoc: + class Generator # :nodoc: + PARAMS_TYPES = %i[req opt keyreq key block].freeze + + # +CodeGenerator::Generator#new+ -> CodeGenerator::Generator + # + # This object constructor helps to easily create class with stubbed methods (with support of public, public singleton, + # private and private singleton methods and their params). For e.g., we want to create a class which have several + # public methods: + # @example + # # By passing array of Symbols or Strings + # code = CodeGenerator.new(public_methods: %i[method1 method2]) + # code.instance_methods(false) #=> [:method1, :method2] + # # By passing number of methods + # code = CodeGenerator.new(public_methods: 3) + # code.instance_methods(false) #=> [:method1, :method2, :method3] + # If you want to pass arguments, you can pass it according the following signature. Note that only +:opt+ and +:key+ + # variables can be predefined (by default they would be nil), also +:block+ variable could be presented only once for + # each method: + # @example + # code = CodeGenerator.new(public_methods: [:method1, [:method2, { args: [[:req, :foo], + # [:req, :bar], + # [:opt, :opts, {}], + # [:keyreq, :some_keyword], + # [:key, :some_key, 123], + # [:block, :some_block]], + # should_return: 123 }], :method3]) + # If you want to specify returnable object, you can pass it inside +:should_return+ key. Also you can pass class + # names, but method would return class itself. Note that +shout+return+ and +generate+ args passed as options for + # specific method would override global values of this two args: + # @example + # # By passing object + # code = CodeGenerator.new(public_methods: [[:method1, { should_return: 123 }]) + # code.method1 #=> 123 + # # By passing class (not all classes are supported) + # code = CodeGenerator.new(public_methods: [[:method1, { should_return: Integer, generate: true }]) + # code.method1 #=> some random Integer + def initialize(public_methods: nil, public_class_methods: nil, private_methods: nil, private_class_methods: nil, should_return: nil, generate: false) + @public_methods = public_methods + @public_class_methods = public_class_methods + @private_methods = private_methods + @private_class_methods = private_class_methods + @should_return = should_return + @generate = generate + end + + def generate_code + case @public_methods + when String, Symbol + # code = CodeGenerator.new(public_methods: :method1, should_return: 123, generate: true) + return define_singleton_method(@public_methods) {} if !@should_return || !@generate + + object_to_return = operate_on_value(@should_return, @generate) + define_singleton_method(@public_methods) { object_to_return } + when Integer + # code = CodeGenerator.new(public_methods: 1, should_return: 123, generate: true) + return if @public_methods.negative? || @public_methods.zero? + + object_to_return = operate_on_value(@should_return, @generate) + + 1.upto(@public_methods) do |time| + define_singleton_method("method#{time}") do + object_to_return + end + end + when Array + return if @public_methods.empty? + + @public_methods.each do |m| + if m.instance_of?(Symbol) || m.instance_of?(String) + if !@should_return && !@generate + define_singleton_method(m) {} + elsif @should_return || @generate + object_to_return = operate_on_value(@should_return, @generate) + define_singleton_method(m) do + object_to_return + end + end + elsif m.instance_of?(Array) + unless (m_name = m.first).instance_of?(Symbol) || m_name.instance_of?(String) + raise ArgumentError, "Method name should be Symbol or String, but #{m_name.class} was passed." + end + unless (opts = m.last).is_a?(Hash) #&& (arguments = opts[:args]).is_a?(Array) + raise ArgumentError, "Method arguments should behave as Hash, but #{m_name.class} was passed." + end + + arguments = opts[:args] ? arguments_parser(opts[:args]) : nil + object_to_return = operate_on_value(opts[:should_return], opts[:generate]).inspect + instance_eval <<-METHOD, __FILE__, __LINE__ + 1 + def #{m_name}(#{arguments}) + #{object_to_return} + end + METHOD + end + end + when NilClass + nil + else + raise ArgumentError, "public_methods is #{@public_methods.class} but expected Array[Symbol|String] or Integer." + end + end + + private + + def operate_on_value(should_return, generate) + random_object = if generate && should_return.instance_of?(Class) + generate_random_object(should_return) + elsif should_return + should_return + end + + return random_object if random_object && should_return + + random_object || should_return + end + + def generate_random_object(klass) + case klass.name + when "Integer" + rand.to_s.sub(/.*?\./, "").to_i + when "String" + SecureRandom.alphanumeric(10) + when "Symbol" + SecureRandom.alphanumeric(10).to_sym + else + "Unsupported class #{klass}" + end + end + + def arguments_parser(arguments) + validator = Validator.new(arguments) + validator.validate! + end + end +end diff --git a/sig/code_generator/generator.rbs b/sig/code_generator/generator.rbs new file mode 100644 index 0000000..39f60a1 --- /dev/null +++ b/sig/code_generator/generator.rbs @@ -0,0 +1,14 @@ +module CodeGenerator + module Generator + type paramType = Integer | Symbol | String | Array[String | Symbol | paramType] + + PARAMS_TYPES: Array[Symbol] + + def initialize: (?public_methods: paramType?, public_class_methods: paramType?, private_methods: paramType?, private_class_methods: paramType?) -> void + + @public_methods: Integer | Array[Symbol | String | Array[untyped]] | nil + @public_class_methods: Integer | Array[Symbol | String] | nil + @private_methods: Integer | Array[Symbol | String] | nil + @private_class_methods: Integer | Array[Symbol | String] | nil + end +end diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb new file mode 100644 index 0000000..5679cff --- /dev/null +++ b/spec/code_generator/generator_spec.rb @@ -0,0 +1,192 @@ +# frozen_string_literal: true + +require "rspec" +require_relative "../../lib/code_generator/generator" + +RSpec.describe CodeGenerator::Generator do + describe "#generate_code" do + context "public_methods" do + before do |test| + code.generate_code unless test.metadata[:skip_generation] + end + + context "when Symbol is passed" do + before { code.generate_code } + + let(:code) { described_class.new(public_methods: :method1) } + + it "returns method" do + expect(code.public_methods(false) - [:generate_code]).to contain_exactly(:method1) + end + + context "when options for method were passed" do + let(:code) { described_class.new(public_methods: :method1, should_return: Integer, generate: true) } + + it "returns some value" do + expect(code.method1).to be_a Integer + end + end + end + + context "when String is passed" do + before { code.generate_code } + + let(:code) { described_class.new(public_methods: "method1") } + + it "returns a method" do + expect(code.public_methods(false) - [:generate_code]).to eq [:method1] + end + + context "when options for method were passed" do + let(:code) { described_class.new(public_methods: :method1, should_return: Integer, generate: true) } + + it "returns some value" do + expect(code.method1).to be_a Integer + end + end + end + + context "when Integer is passed" do + before { code.generate_code } + + let(:code) { described_class.new(public_methods: 2) } + + it "returns two methods" do + expect(code.public_methods(false) - [:generate_code]).to contain_exactly(:method1, :method2) + end + + context "when there is any params" do + let(:code) { described_class.new(public_methods: 2, should_return: Integer, generate: true) } + + it "returns some objects" do + expect(code.method1).to be_an Integer + expect(code.method2).to be_an Integer + end + end + end + + context "when Array is passed" do + context "when first value is any other object than String or Symbol" do + let(:some_value) { 1 } + let(:code) { described_class.new(public_methods: [[11, { should_return: 123 }]]) } + + it "raise error", :skip_generation do + # .with("Method name should be Symbol or String, but #{some_value.class} was passed.") + expect do + code.generate_code + end.to raise_error(ArgumentError) + end + end + + context "when values are Symbol or String" do + let(:code) { described_class.new(public_methods: [:method1, "method2"]) } + + it "returns two methods" do + expect(code.public_methods(false) - [:generate_code]).to contain_exactly(:method1, :method2) + end + + context "when additional params were passed" do + let(:code) do + described_class.new(public_methods: [:method1, "method2"], should_return: Integer, generate: true) + end + + it "returns some value" do + expect(code.method1).to be_an Integer + expect(code.method2).to be_an Integer + end + + context "when params are passed for specific method" do + before do + code.generate_code + end + let(:code) do + described_class.new(public_methods: [:method1, ["method2", { should_return: Integer, generate: true }]]) + end + + it "returns nil and random Integer", :skip_generation do + expect(code.method1).to eq nil + expect(code.method2).to be_an Integer + end + end + + context "when params are passed in global scope" do + before { code.generate_code } + + let(:code) do + described_class.new(public_methods: [:method1, ["method2", { should_return: Integer, generate: true }]], + should_return: String, generate: true) + end + + it "ignores global params" do + expect(code.method1).to be_a String + expect(code.method2).to be_an Integer + end + end + end + end + + # context "when `should_return` passed" do + # context "when instance is passed" do + # let(:m1_value) { 123 } + # let(:m2_value) { Integer } + # let(:code) do + # described_class.new(public_methods: [[:method1, { should_return: m1_value }], + # [:method2, { should_return: m2_value }]]) + # end + # + # it "returns passed object" do + # expect(code.method1).to eq(m1_value) + # expect(code.method2).to eq(m2_value) + # end + # end + # end + # + # context "when `generate` passed" do + # context "when it was passed in correct way" do + # subject(:code) do + # described_class.new(public_methods: [[:method1, { should_return: klass, generate: true }], + # [:method2, { should_return: klass, generate: true }]]) + # end + # + # context "when integer passed" do + # let(:klass) { Integer } + # + # it "returns random integer" do + # expect(code.method1).to be_an Integer + # expect(code.method2).to be_an Integer + # end + # end + # + # context "when string passed" do + # let(:klass) { String } + # + # it "returns random string" do + # expect(code.method1).to be_a String + # expect(code.method2).to be_a String + # end + # end + # + # context "when symbol is passed" do + # let(:klass) { Symbol } + # + # it "returns random symbol" do + # expect(code.method1).to be_a Symbol + # expect(code.method2).to be_a Symbol + # end + # end + # end + # end + end + + context "when passed anything else" do + let(:code) do + described_class.new(public_methods: Object.new) + end + + it "returns error", :skip_generation do + expect { code.generate_code }.to raise_error(ArgumentError) + end + end + end + end +end From 496eed4bdbef5ee3cd265bb3ecb742ae518a6cfc Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:27:42 +0300 Subject: [PATCH 05/39] Updated default specs --- spec/code_generator_spec.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/code_generator_spec.rb b/spec/code_generator_spec.rb index 6e5f53e..60c6609 100644 --- a/spec/code_generator_spec.rb +++ b/spec/code_generator_spec.rb @@ -4,8 +4,4 @@ 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 From 9f8fa2d2564d15fdbf0023991c19c22f71b88f3b Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:27:56 +0300 Subject: [PATCH 06/39] Added validator --- lib/extensions/validator.rb | 91 +++++++++++++++++++++++++++++++++++++ sig/validator.rbs | 9 ++++ 2 files changed, 100 insertions(+) create mode 100644 lib/extensions/validator.rb create mode 100644 sig/validator.rbs diff --git a/lib/extensions/validator.rb b/lib/extensions/validator.rb new file mode 100644 index 0000000..53d0173 --- /dev/null +++ b/lib/extensions/validator.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: false + +class Validator + PARAMS_TYPES = %i[req opt keyreq key block].freeze + + def initialize(arguments) + @arguments = arguments + end + + def validate! + validate(arguments_table).join(", ") + end + + private + + def arguments_table + counts = Hash.new(0) + @arguments.each do |arg| + type = arg.first + raise ArgumentError, "@arguments has illegal type: #{type.inspect}." unless PARAMS_TYPES.include?(type) + + counts[type] += 1 + end + counts + end + + def validate(counts) + arr = [] + arr << validate_req if counts[:req].positive? + arr << validate_opt if counts[:opt].positive? + arr << validate_keyreq if counts[:keyreq].positive? + arr << validate_key if counts[:key].positive? + arr << validate_block if counts[:block].positive? + end + + def validate_req + if (reqs = @arguments.select { _1.first == :req }).any? { _1.size > 2 } + raise ArgumentError, messages[:req] + end + + reqs.map(&:last).join(", ") + end + + def validate_opt + if (opts = @arguments.select { _1.first == :opt }).any? { _1.size != 3 } + raise ArgumentError, messages[:opt] + end + + str = opts.each_with_object("") do |opt, obj| + obj << "#{opt[1]}=#{opt.last}#{',' if @o_size ||= opts.size == 1}" + end + @o_size ? str.chop : str + end + + def validate_keyreq + if (keyreqs = @arguments.select { _1.first == :keyreq }).any? { _1.size > 2 } + raise ArgumentError, messages[:keyreq] + end + + keyreqs.map(&:last).map { "#{_1}:" }.join(", ") + end + + def validate_key + if (keys = @arguments.select { _1.first == :key }).any? { _1.size != 3 } + raise ArgumentError, messages[:key] + end + + str = keys.each_with_object("") do |key, obj| + obj << "#{key[1]}: #{key.last}#{',' if @k_size ||= keys.size == 1 }" + end + @k_size ? str.chop : str + end + + def validate_block + if (block = @arguments.select { _1.first == :block }).any? { _1.size > 2 } + raise ArgumentError, messages[:block] + end + + "&#{block[0].last}" + end + + def messages + { + req: "Required params should have no params.", + opt: "Optional params should have only one value.", + keyreq: "Required keyword params should be empty.", + key: "Keyword params should have only one value.", + block: "Block param should have no values." + } + end +end diff --git a/sig/validator.rbs b/sig/validator.rbs new file mode 100644 index 0000000..595a16c --- /dev/null +++ b/sig/validator.rbs @@ -0,0 +1,9 @@ +class Validator + def validate!: -> untyped + + private + + def arguments_table: -> untyped + + def validate: -> untyped +end From b5841c4a7da4c24a6a4ca5a4c68cd9016219fb1a Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:35:34 +0300 Subject: [PATCH 07/39] Removed useless `before` hooks --- spec/code_generator/generator_spec.rb | 110 ++++++++++++-------------- 1 file changed, 51 insertions(+), 59 deletions(-) diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index 5679cff..66d85f6 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -11,8 +11,6 @@ end context "when Symbol is passed" do - before { code.generate_code } - let(:code) { described_class.new(public_methods: :method1) } it "returns method" do @@ -29,8 +27,6 @@ end context "when String is passed" do - before { code.generate_code } - let(:code) { described_class.new(public_methods: "method1") } it "returns a method" do @@ -47,8 +43,6 @@ end context "when Integer is passed" do - before { code.generate_code } - let(:code) { described_class.new(public_methods: 2) } it "returns two methods" do @@ -110,8 +104,6 @@ end context "when params are passed in global scope" do - before { code.generate_code } - let(:code) do described_class.new(public_methods: [:method1, ["method2", { should_return: Integer, generate: true }]], should_return: String, generate: true) @@ -125,57 +117,57 @@ end end - # context "when `should_return` passed" do - # context "when instance is passed" do - # let(:m1_value) { 123 } - # let(:m2_value) { Integer } - # let(:code) do - # described_class.new(public_methods: [[:method1, { should_return: m1_value }], - # [:method2, { should_return: m2_value }]]) - # end - # - # it "returns passed object" do - # expect(code.method1).to eq(m1_value) - # expect(code.method2).to eq(m2_value) - # end - # end - # end - # - # context "when `generate` passed" do - # context "when it was passed in correct way" do - # subject(:code) do - # described_class.new(public_methods: [[:method1, { should_return: klass, generate: true }], - # [:method2, { should_return: klass, generate: true }]]) - # end - # - # context "when integer passed" do - # let(:klass) { Integer } - # - # it "returns random integer" do - # expect(code.method1).to be_an Integer - # expect(code.method2).to be_an Integer - # end - # end - # - # context "when string passed" do - # let(:klass) { String } - # - # it "returns random string" do - # expect(code.method1).to be_a String - # expect(code.method2).to be_a String - # end - # end - # - # context "when symbol is passed" do - # let(:klass) { Symbol } - # - # it "returns random symbol" do - # expect(code.method1).to be_a Symbol - # expect(code.method2).to be_a Symbol - # end - # end - # end - # end + context "when `should_return` passed" do + context "when instance is passed" do + let(:m1_value) { 123 } + let(:m2_value) { Integer } + let(:code) do + described_class.new(public_methods: [[:method1, { should_return: m1_value }], + [:method2, { should_return: m2_value }]]) + end + + it "returns passed object" do + expect(code.method1).to eq(m1_value) + expect(code.method2).to eq(m2_value) + end + end + end + + context "when `generate` passed" do + context "when it was passed in correct way" do + subject(:code) do + described_class.new(public_methods: [[:method1, { should_return: klass, generate: true }], + [:method2, { should_return: klass, generate: true }]]) + end + + context "when integer passed" do + let(:klass) { Integer } + + it "returns random integer" do + expect(code.method1).to be_an Integer + expect(code.method2).to be_an Integer + end + end + + context "when string passed" do + let(:klass) { String } + + it "returns random string" do + expect(code.method1).to be_a String + expect(code.method2).to be_a String + end + end + + context "when symbol is passed" do + let(:klass) { Symbol } + + it "returns random symbol" do + expect(code.method1).to be_a Symbol + expect(code.method2).to be_a Symbol + end + end + end + end end context "when passed anything else" do From c6452f45a63d7a24a846b6789a1c59fecf146ea5 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:37:00 +0300 Subject: [PATCH 08/39] Removed unused `with` --- spec/code_generator/generator_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index 66d85f6..fc3f394 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -65,7 +65,6 @@ let(:code) { described_class.new(public_methods: [[11, { should_return: 123 }]]) } it "raise error", :skip_generation do - # .with("Method name should be Symbol or String, but #{some_value.class} was passed.") expect do code.generate_code end.to raise_error(ArgumentError) From d351e5c2f9893be1a147a20bfd58f4d008e8b4d5 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:38:16 +0300 Subject: [PATCH 09/39] Fixed conflicts between `before` hooks --- spec/code_generator/generator_spec.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index fc3f394..1d9d4b2 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -89,14 +89,11 @@ end context "when params are passed for specific method" do - before do - code.generate_code - end let(:code) do described_class.new(public_methods: [:method1, ["method2", { should_return: Integer, generate: true }]]) end - it "returns nil and random Integer", :skip_generation do + it "returns nil and random Integer" do expect(code.method1).to eq nil expect(code.method2).to be_an Integer end From eceb64923328eeb2a44afb60a86c35268ba442b5 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 22:39:42 +0300 Subject: [PATCH 10/39] Removed useless `require` --- .rspec | 1 + spec/code_generator/generator_spec.rb | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) 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/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index 1d9d4b2..18e91cb 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -require "rspec" -require_relative "../../lib/code_generator/generator" - RSpec.describe CodeGenerator::Generator do describe "#generate_code" do context "public_methods" do From bb7ed641dc176224fd808bedd65c95f6a502f506 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 23:13:33 +0300 Subject: [PATCH 11/39] Updated `README.md` --- README.md | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 194 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d527f5d..ce7c159 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,216 @@ # 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. +![Alt](https://repobeats.axiom.co/api/embed/cf02cc6438367e8127e0aae8fc871c935844f4e8.svg "Project stats") + +Welcome to the Ruby library which could generate code in any directions you want. It supports flexible settings for your +purposes and does not need any requirements. + +## Documentation content + +1. [Overview][1] +2. [Installation][2] + 1. [Build from source][2.1] + 1. [Manual installation][2.1.1] + 2. [Automatic installation][2.1.2] + 2. [Build via bundler][2.2] +3. [Usage][3] +4. [Todo][4] +5. [Development][5] +6. [Requirements][6] + 1. [Common usage][6.1] + 2. [Development purposes][6.2] +7. [Project style guide][7] +8. [Contributing][8] +9. [License][9] +10. [Code of Conduct][10] + +## Overview + +As noted above, this gem . The projects source tree is pretty simple. All of the +resources are stored in theirs separate module, so it does code readability much cleaner. ## 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. +Genius API gem is quite simple to use and install. There are two options to install it — for those who is going to +contribute into the project and for those who is going to embed gem to theirs project. See below for each step. + +### Build from source + +#### Manual installation + +The manual installation includes installation via command line interface. it is practically no different from what +happens during the automatic build of the project: + +```shell +git clone https://github.com/unurgunite/code_generator.git && \ +cd code_generator && \ +bundle install && \ +gem build code_generator.gemspec && \ +gem install code-generator-0.1.0.gem +``` + +Now everything should work fine. Just type `irb` and `require "code_generator"` to start working with the library + +#### Automatic installation + +The automatic installation is simpler but it has at least same steps as manual installation: + +```shell +git clone https://github.com/unurgunite/code_generator.git && \ +cd code_generator && \ +bin/setup +``` + +If you see `irb` interface, then everything works fine. The main goal of automatic installation is that you do not need +to create your own script to simplify project build and clean up the shell history. Note: you do not need to require +projects file after the automatic installation. See `bin/setup` file for clarity of the statement + +### Build via bundler + +This documentation point is close to those who need to embed the library in their project. Just place this gem to your +Gemfile or create it manually via `bundle init`: + +```ruby +# Your Gemfile +gem 'code_generator' +``` -Install the gem and add to the application's Gemfile by executing: +And then execute: - $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG +```shell +bundle install +``` -If bundler is not being used to manage dependencies, install the gem by executing: +Or install it yourself for non bundled projects as: - $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG +```shell +gem install code_generator +``` ## Usage -TODO: Write usage instructions here +All docs are available at the separate page: https://unurgunite.github.io/code_generator_docs/ + +## TODO + +- [x] Add support for public methods +- [ ] Add support for public public class methods +- [ ] Add support for public private methods +- [ ] Add support for public private class methods ## Development -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. +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can +also run `bin/console` for an interactive prompt that will allow you to experiment. + +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). + +## Requirements + +This section will show dependencies which are used in the project. This section splits in two other sections — +requirements for common use and requirements for the development purposes. + +### Common use + +The `code_generator` gem does not have any 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). +### Development purposes + +For the development purposes `code_generator` gem uses: + +| Dependencies | Description | +|----------------------|------------------------------------------------------------------------------------------| +| [RSpec][201] | The RSpec gem is used for test which are located in a separate folder under `spec` name. | +| [RuboCop][202] | The RuboCop gem is used for code formatting. | +| [Rake][203] | The Rake gem is used for building tasks as generating documentation. | +| [rubocop-rspec][205] | The rubocop-rspec gem is used for formatting specs. | +| [YARD][206] | The YARD gem is used for the documentation. | + +## Project style guide + +To make the code base much cleaner gem has its own style guides. They are defined in a root folder of the gem in +a [CONTRIBUTING.md](https://github.com/unurgunite/code_generator/blob/master/CONTRIBUTING.md) file. Check it for more +details. ## 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 on GitHub at https://github.com/unurgunite/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/unurgunite/code_generator/blob/master/CODE_OF_CONDUCT.md). To contribute you +should +fork this project and create there new branch: + +```shell +git clone https://github.com/your-beautiful-username/code_generator.git && \ +git checkout -b refactor && \ +git commit -m "Affected new changes" && \ +git push origin refactor +``` + +And then make new pull request with additional notes of what you have done. The better the changes are scheduled, the +faster the PR will be checked. ## 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 in the `CodeGenerator` project's codebases, issue trackers, chat rooms and mailing lists is expected +to follow the [code of conduct](https://github.com/unurgunite/code_generator/blob/master/CODE_OF_CONDUCT.md). + +## License + +The gem is available as open source under the terms of the [GPLv3 License](https://opensource.org/licenses/GPL-3.0). The +copy of the license is stored in project under the `LICENSE.txt` file +name: [copy of the License](https://github.com/unurgunite/code_generator/blob/master/LICENSE.txt) + +The documentation is available as open source under the terms of +the [CC BY-SA 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/) + +The other libs are available as open source under the terms of +the [New BSD License](https://opensource.org/licenses/BSD-3-Clause) + +![GPLv3 logo](https://www.gnu.org/graphics/gplv3-or-later.png) +![CC BY-SA 4.0](https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/by-nc.svg) +![BSD license logo](https://upload.wikimedia.org/wikipedia/commons/4/42/License_icon-bsd-88x31.png) + +[1]:https://github.com/unurgunite/code_generator#overview + +[2]:https://github.com/unurgunite/code_generator#installation + +[2.1]:https://github.com/unurgunite/code_generator#build-from-source + +[2.1.1]:https://github.com/unurgunite/code_generator#manual-installation + +[2.1.2]:https://github.com/unurgunite/code_generator#automatic-installation + +[2.2]:https://github.com/unurgunite/code_generator#build-via-bundler + +[3]:https://github.com/unurgunite/code_generator#usage + +[4]:https://github.com/unurgunite/code_generator#todo + +[5]:https://github.com/unurgunite/code_generator#development + +[6]:https://github.com/unurgunite/code_generator#requirements + +[6.1]:https://github.com/unurgunite/code_generator#common-usage + +[6.2]:https://github.com/unurgunite/code_generator#development-purposes + +[7]:https://github.com/unurgunite/code_generator#project-style-guide + +[8]:https://github.com/unurgunite/code_generator#contributing + +[9]:https://github.com/unurgunite/code_generator#license + +[10]:https://github.com/unurgunite/code_generator#code-of-conduct + +[101]:https://rubygems.org/gems/httparty + +[102]:https://rubygems.org/gems/nokogiri + +[201]:https://rubygems.org/gems/rspec + +[202]:https://rubygems.org/gems/rubocop + +[203]:https://rubygems.org/gems/rake From 0878bcf6f83f671a0f786f12794182ed0110ff56 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 23:14:57 +0300 Subject: [PATCH 12/39] Updated `README.md` --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ce7c159..225b38c 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,8 @@ For the development purposes `code_generator` gem uses: | [RSpec][201] | The RSpec gem is used for test which are located in a separate folder under `spec` name. | | [RuboCop][202] | The RuboCop gem is used for code formatting. | | [Rake][203] | The Rake gem is used for building tasks as generating documentation. | -| [rubocop-rspec][205] | The rubocop-rspec gem is used for formatting specs. | -| [YARD][206] | The YARD gem is used for the documentation. | +| [rubocop-rspec][204] | The rubocop-rspec gem is used for formatting specs. | +| [YARD][205] | The YARD gem is used for the documentation. | ## Project style guide @@ -154,7 +154,8 @@ faster the PR will be checked. ## Code of Conduct -Everyone interacting in the `CodeGenerator` project's codebases, issue trackers, chat rooms and mailing lists is expected +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/unurgunite/code_generator/blob/master/CODE_OF_CONDUCT.md). ## License @@ -214,3 +215,7 @@ the [New BSD License](https://opensource.org/licenses/BSD-3-Clause) [202]:https://rubygems.org/gems/rubocop [203]:https://rubygems.org/gems/rake + +[204]:https://rubygems.org/gems/rubocop-rspec + +[205]:https://rubygems.org/gems/yard From fd79b61b43b6c1a3622e996c1a6314b42bedfc99 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 23:15:21 +0300 Subject: [PATCH 13/39] Updated YARD documentation --- lib/code_generator/generator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/code_generator/generator.rb b/lib/code_generator/generator.rb index 457c631..8f869c7 100644 --- a/lib/code_generator/generator.rb +++ b/lib/code_generator/generator.rb @@ -14,10 +14,10 @@ class Generator # :nodoc: # @example # # By passing array of Symbols or Strings # code = CodeGenerator.new(public_methods: %i[method1 method2]) - # code.instance_methods(false) #=> [:method1, :method2] + # code.methods(false) #=> [:method1, :method2] # # By passing number of methods # code = CodeGenerator.new(public_methods: 3) - # code.instance_methods(false) #=> [:method1, :method2, :method3] + # code.methods(false) #=> [:method1, :method2, :method3] # If you want to pass arguments, you can pass it according the following signature. Note that only +:opt+ and +:key+ # variables can be predefined (by default they would be nil), also +:block+ variable could be presented only once for # each method: From edbab5ed692001331fe6c1b99191596cfc89c68d Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sat, 14 Oct 2023 23:35:22 +0300 Subject: [PATCH 14/39] Updated `README.md` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 225b38c..17ee8e8 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ resources are stored in theirs separate module, so it does code readability much ## Installation -Genius API gem is quite simple to use and install. There are two options to install it — for those who is going to +Code Generator gem is quite simple to use and install. There are two options to install it — for those who is going to contribute into the project and for those who is going to embed gem to theirs project. See below for each step. ### Build from source From 0656c10e7bfa07b1904e33520e28290df37b69f0 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sun, 15 Oct 2023 00:53:10 +0300 Subject: [PATCH 15/39] Updated `README.md` --- README.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 17ee8e8..9a90f45 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,34 @@ gem install code_generator ## Usage -All docs are available at the separate page: https://unurgunite.github.io/code_generator_docs/ +All internal docs are available at the separate page: https://unurgunite.github.io/code_generator_docs/ + +This gem is really simple to use. It generates methods according to your requests. It uses standard Ruby parameters +names for representing them as separate objects: + +1. `req` is a required param +2. `opt` is an optional param +3. `keyreq` is a required keyword param +4. `key` is a keyword param with default value +5. `block` is block + +```ruby +code = CodeGenerator::Generator.new(public_methods: [:method1, [:method2, { args: [[:req, :foo], + [:req, :bar], + [:opt, :opts, {}], + [:keyreq, :some_keyword], + [:key, :some_key, 123], + [:block, :some_block]], + should_return: 123 }], :method3], should_return: Integer, generate: true) +code.generate_code #=> [:method1, [:method2, {:args=>[[:req, :foo], [:req, :bar], [:opt, :opts, {}], [:keyreq, :some_keyword], [:key, :some_key, 123], [:block, :some_block]], :should_return=>123}], :method3] +code.methods(false) #=> [:method1, :method2, :method3] +code.method1 #=> 724835356767704 +code.method2(1, { foo: 555 }, some_keyword: 345) #=> 123 +code.method3 #=> 724835356767704 # random objects will be fixed in next release + +code.method(:method2) #=> #[[:req, :foo], [:req, :bar], [:opt, :opts, {}], [:keyreq, :some_keyword], [:key, :some_key, 123], [:block, :some_block]], :should_return=>123}], :method3], @public_class_methods=nil, @private_methods=nil, @private_class_methods=nil, @should_return=Integer, @generate=true>.method2(foo, bar, opts=..., some_keyword:, some_key: ..., &some_block) /home/ruby/code_generator/lib/code_generator/generator.rb:99> +code.method(:method2).parameters # => [[:req, :foo], [:req, :bar], [:opt, :opts], [:keyreq, :some_keyword], [:key, :some_key], [:block, :some_block]] +``` ## TODO From 01e78fc8c94f42eb85329f876fdb24c594edd392 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sun, 15 Oct 2023 13:10:24 +0300 Subject: [PATCH 16/39] Added specs for dynamic arguments --- spec/code_generator/generator_spec.rb | 103 +++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index 18e91cb..bb1dd11 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative "../support/shared_examples/acts_as_public_methods_spec" + RSpec.describe CodeGenerator::Generator do describe "#generate_code" do context "public_methods" do @@ -133,7 +135,7 @@ [:method2, { should_return: klass, generate: true }]]) end - context "when integer passed" do + context "when Integer passed" do let(:klass) { Integer } it "returns random integer" do @@ -142,7 +144,7 @@ end end - context "when string passed" do + context "when String passed" do let(:klass) { String } it "returns random string" do @@ -151,7 +153,7 @@ end end - context "when symbol is passed" do + context "when Symbol passed" do let(:klass) { Symbol } it "returns random symbol" do @@ -159,6 +161,15 @@ expect(code.method2).to be_a Symbol end end + + context "when unsupported class passed" do + let(:klass) { Object } + + it "returns passed class" do + expect(code.method1).to eq klass + expect(code.method2).to eq klass + end + end end end end @@ -172,6 +183,92 @@ expect { code.generate_code }.to raise_error(ArgumentError) end end + + context "when passed arguments to methods" do + subject(:code) { described_class.new(public_methods: [[:method1, { args: args }]]) } + + context "when required arguments passed" do + let(:args) { [[:req, :foo]] } + + it do + expect(code.method(:method1).parameters).to eq args + end + + context 'when args were passed incorrectly' do + let(:args) { [[:req, :foo, 123]] } + + it '', :skip_generation do + expect { code.generate_code }.to raise_error ArgumentError + end + end + end + + context "when optional arguments passed" do + let(:args) { [[:opt, :opts, {}]] } + + it do + expect(code.method(:method1).parameters).to eq [[:opt, :opts]] + end + + context 'when args were passed incorrectly' do + let(:args) { [[:opt, :opts]] } + + it '', :skip_generation do + expect { code.generate_code }.to raise_error ArgumentError + end + end + end + + context "when required keyword arguments passed" do + let(:args) { [[:keyreq, :some_keyword]] } + + it { expect(code.method(:method1).parameters).to eq args } + + context 'when args were passed incorrectly' do + let(:args) { [[:keyreq, :some_keyword, 123]] } + + it '', :skip_generation do + expect { code.generate_code }.to raise_error ArgumentError + end + end + end + + context "when keyword arguments passed" do + let(:args) { [[:key, :some_key, 123]] } + + it { expect(code.method(:method1).parameters).to eq [[:key, :some_key]] } + + context 'when args were passed incorrectly' do + let(:args) { [[:key, :some_key]] } + + it '', :skip_generation do + expect { code.generate_code }.to raise_error ArgumentError + end + end + end + + context "when block argument passed" do + let(:args) { [[:block, :some_block]] } + + it { expect(code.method(:method1).parameters).to eq args } + + context 'when args were passed incorrectly' do + let(:args) { [[:block, :some_block, 123]] } + + it '', :skip_generation do + expect { code.generate_code }.to raise_error ArgumentError + end + end + end + end end end end + +# code = CodeGenerator.new(public_methods: [:method1, [:method2, { args: [[:req, :foo], +# [:req, :bar], +# [:opt, :opts, {}], +# [:keyreq, :some_keyword], +# [:key, :some_key, 123], +# [:block, :some_block]], +# should_return: 123 }], :method3]) \ No newline at end of file From 990e697db617ff4bbe30340faf28d6600eda73ed Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sun, 15 Oct 2023 13:11:17 +0300 Subject: [PATCH 17/39] Fixed some typos --- lib/code_generator.rb | 4 ++-- lib/extensions/validator.rb | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/code_generator.rb b/lib/code_generator.rb index 31a8068..feea9c6 100644 --- a/lib/code_generator.rb +++ b/lib/code_generator.rb @@ -5,6 +5,6 @@ class Error < StandardError; end # Your code goes here... end -require_relative 'extensions/extensions' +require_relative "extensions/extensions" require_relative "code_generator/version" -require_relative 'code_generator/generator' +require_relative "code_generator/generator" diff --git a/lib/extensions/validator.rb b/lib/extensions/validator.rb index 53d0173..a236155 100644 --- a/lib/extensions/validator.rb +++ b/lib/extensions/validator.rb @@ -31,6 +31,7 @@ def validate(counts) arr << validate_keyreq if counts[:keyreq].positive? arr << validate_key if counts[:key].positive? arr << validate_block if counts[:block].positive? + arr end def validate_req @@ -47,7 +48,7 @@ def validate_opt end str = opts.each_with_object("") do |opt, obj| - obj << "#{opt[1]}=#{opt.last}#{',' if @o_size ||= opts.size == 1}" + obj << "#{opt[1]}=#{opt.last}#{"," if @o_size ||= opts.size == 1}" end @o_size ? str.chop : str end @@ -66,7 +67,7 @@ def validate_key end str = keys.each_with_object("") do |key, obj| - obj << "#{key[1]}: #{key.last}#{',' if @k_size ||= keys.size == 1 }" + obj << "#{key[1]}: #{key.last}#{"," if @k_size ||= keys.size == 1}" end @k_size ? str.chop : str end From 9441383a4fa84363246e186e4394ab1539f7a652 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sun, 15 Oct 2023 15:33:35 +0300 Subject: [PATCH 18/39] Updated `README.md` --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a90f45..dd57e87 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ purposes and does not need any requirements. ## Overview -As noted above, this gem . The projects source tree is pretty simple. All of the -resources are stored in theirs separate module, so it does code readability much cleaner. +As noted above, this gem helps to create large domains of data (objects and their interfaces). The projects source tree +is pretty simple. All of the resources are stored in theirs separate module, so it does code readability much cleaner. ## Installation From 8df7e618865ad92c23d38132ae858e8a3989273f Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sun, 15 Oct 2023 16:36:08 +0300 Subject: [PATCH 19/39] Added specs for arguments --- lib/extensions/validator.rb | 2 +- spec/code_generator/generator_spec.rb | 39 +++++++++++++-------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/lib/extensions/validator.rb b/lib/extensions/validator.rb index a236155..aed0532 100644 --- a/lib/extensions/validator.rb +++ b/lib/extensions/validator.rb @@ -73,7 +73,7 @@ def validate_key end def validate_block - if (block = @arguments.select { _1.first == :block }).any? { _1.size > 2 } + if (block = @arguments.select { _1.first == :block }).any? { _1.size > 2 } || block.size > 1 raise ArgumentError, messages[:block] end diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index bb1dd11..de34679 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -194,10 +194,10 @@ expect(code.method(:method1).parameters).to eq args end - context 'when args were passed incorrectly' do + context "when args were passed incorrectly" do let(:args) { [[:req, :foo, 123]] } - it '', :skip_generation do + it "", :skip_generation do expect { code.generate_code }.to raise_error ArgumentError end end @@ -207,13 +207,13 @@ let(:args) { [[:opt, :opts, {}]] } it do - expect(code.method(:method1).parameters).to eq [[:opt, :opts]] + expect(code.method(:method1).parameters).to eq [%i[opt opts]] end - context 'when args were passed incorrectly' do + context "when args were passed incorrectly" do let(:args) { [[:opt, :opts]] } - it '', :skip_generation do + it "", :skip_generation do expect { code.generate_code }.to raise_error ArgumentError end end @@ -224,10 +224,10 @@ it { expect(code.method(:method1).parameters).to eq args } - context 'when args were passed incorrectly' do + context "when args were passed incorrectly" do let(:args) { [[:keyreq, :some_keyword, 123]] } - it '', :skip_generation do + it "", :skip_generation do expect { code.generate_code }.to raise_error ArgumentError end end @@ -236,12 +236,12 @@ context "when keyword arguments passed" do let(:args) { [[:key, :some_key, 123]] } - it { expect(code.method(:method1).parameters).to eq [[:key, :some_key]] } + it { expect(code.method(:method1).parameters).to eq [%i[key some_key]] } - context 'when args were passed incorrectly' do + context "when args were passed incorrectly" do let(:args) { [[:key, :some_key]] } - it '', :skip_generation do + it "", :skip_generation do expect { code.generate_code }.to raise_error ArgumentError end end @@ -252,10 +252,17 @@ it { expect(code.method(:method1).parameters).to eq args } - context 'when args were passed incorrectly' do + context "when args were passed incorrectly" do let(:args) { [[:block, :some_block, 123]] } - it '', :skip_generation do + it "", :skip_generation do + expect { code.generate_code }.to raise_error ArgumentError + end + end + + context "when there is more than one block" do + let(:args) { [[:block, :foo_block], [[:block, :bar_block]]] } + it "", :skip_generation do expect { code.generate_code }.to raise_error ArgumentError end end @@ -264,11 +271,3 @@ end end end - -# code = CodeGenerator.new(public_methods: [:method1, [:method2, { args: [[:req, :foo], -# [:req, :bar], -# [:opt, :opts, {}], -# [:keyreq, :some_keyword], -# [:key, :some_key, 123], -# [:block, :some_block]], -# should_return: 123 }], :method3]) \ No newline at end of file From 771c8890ac94bfe4f0761cd8927e6392292a8598 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sun, 15 Oct 2023 19:57:08 +0300 Subject: [PATCH 20/39] Added support for public class methods --- README.md | 2 +- lib/code_generator/generator.rb | 88 ++++++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index dd57e87..f9528c9 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ code.method(:method2).parameters # => [[:req, :foo], [:req, :bar], [:opt, :opts] ## TODO - [x] Add support for public methods -- [ ] Add support for public public class methods +- [x] Add support for public public class methods - [ ] Add support for public private methods - [ ] Add support for public private class methods diff --git a/lib/code_generator/generator.rb b/lib/code_generator/generator.rb index 8f869c7..dae721a 100644 --- a/lib/code_generator/generator.rb +++ b/lib/code_generator/generator.rb @@ -39,7 +39,10 @@ class Generator # :nodoc: # # By passing class (not all classes are supported) # code = CodeGenerator.new(public_methods: [[:method1, { should_return: Integer, generate: true }]) # code.method1 #=> some random Integer - def initialize(public_methods: nil, public_class_methods: nil, private_methods: nil, private_class_methods: nil, should_return: nil, generate: false) + # + # @param [Integer|String|Symbol|Array>] public_methods + def initialize(public_methods: nil, public_class_methods: nil, private_methods: nil, private_class_methods: nil, + should_return: nil, generate: false) @public_methods = public_methods @public_class_methods = public_class_methods @private_methods = private_methods @@ -48,11 +51,21 @@ def initialize(public_methods: nil, public_class_methods: nil, private_methods: @generate = generate end + # +CodeGenerator::Generator#generate_code+ -> value + # + # def generate_code + generate_public_methods + generate_public_class_methods + end + + private + + def generate_public_methods case @public_methods when String, Symbol # code = CodeGenerator.new(public_methods: :method1, should_return: 123, generate: true) - return define_singleton_method(@public_methods) {} if !@should_return || !@generate + return define_singleton_method(@public_methods) {} unless any_generation_rules object_to_return = operate_on_value(@should_return, @generate) define_singleton_method(@public_methods) { object_to_return } @@ -72,9 +85,9 @@ def generate_code @public_methods.each do |m| if m.instance_of?(Symbol) || m.instance_of?(String) - if !@should_return && !@generate + if !both_generation_rules define_singleton_method(m) {} - elsif @should_return || @generate + elsif any_generation_rules object_to_return = operate_on_value(@should_return, @generate) define_singleton_method(m) do object_to_return @@ -84,7 +97,7 @@ def generate_code unless (m_name = m.first).instance_of?(Symbol) || m_name.instance_of?(String) raise ArgumentError, "Method name should be Symbol or String, but #{m_name.class} was passed." end - unless (opts = m.last).is_a?(Hash) #&& (arguments = opts[:args]).is_a?(Array) + unless (opts = m.last).is_a?(Hash) # && (arguments = opts[:args]).is_a?(Array) raise ArgumentError, "Method arguments should behave as Hash, but #{m_name.class} was passed." end @@ -104,7 +117,68 @@ def #{m_name}(#{arguments}) end end - private + def generate_public_class_methods + case @public_class_methods + when String, Symbol + return self.class.define_singleton_method(@public_class_methods) {} unless any_generation_rules + + object_to_return = operate_on_value(@should_return, @generate) + self.class.define_singleton_method(@public_class_methods) { object_to_return } + when Integer + # code = CodeGenerator.new(public_class_methods: 1, should_return: 123, generate: true) + return if @public_class_methods.negative? || @public_class_methods.zero? + + object_to_return = operate_on_value(@should_return, @generate) + + 1.upto(@public_class_methods) do |time| + self.class.define_singleton_method("method#{time}") do + object_to_return + end + end + when Array + return if @public_class_methods.empty? + + @public_class_methods.each do |m| + if m.instance_of?(Symbol) || m.instance_of?(String) + if !both_generation_rules + self.class.define_singleton_method(m) {} + elsif any_generation_rules + object_to_return = operate_on_value(@should_return, @generate) + self.class.define_singleton_method(m) do + object_to_return + end + end + elsif m.instance_of?(Array) + unless (m_name = m.first).instance_of?(Symbol) || m_name.instance_of?(String) + raise ArgumentError, "Method name should be Symbol or String, but #{m_name.class} was passed." + end + unless (opts = m.last).is_a?(Hash) # && (arguments = opts[:args]).is_a?(Array) + raise ArgumentError, "Method arguments should behave as Hash, but #{m_name.class} was passed." + end + + arguments = opts[:args] ? arguments_parser(opts[:args]) : nil + object_to_return = operate_on_value(opts[:should_return], opts[:generate]).inspect + class_eval <<-METHOD, __FILE__, __LINE__ + 1 + def #{m_name}(#{arguments}) + #{object_to_return} + end + METHOD + end + end + when NilClass + nil + else + raise ArgumentError, "public_methods is #{@public_class_methods.class} but expected Array[Symbol|String] or Integer." + end + end + + def any_generation_rules + @should_return || @generate + end + + def both_generation_rules + @should_return && @generate + end def operate_on_value(should_return, generate) random_object = if generate && should_return.instance_of?(Class) @@ -126,8 +200,6 @@ def generate_random_object(klass) SecureRandom.alphanumeric(10) when "Symbol" SecureRandom.alphanumeric(10).to_sym - else - "Unsupported class #{klass}" end end From dc696c816053656d73d8e15e074a45608238eff5 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Sun, 15 Oct 2023 19:57:28 +0300 Subject: [PATCH 21/39] Added `yard` gem for documentation --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 4d187c3..e9e7bef 100644 --- a/Gemfile +++ b/Gemfile @@ -12,3 +12,5 @@ gem "rspec", "~> 3.0" gem "rubocop", "~> 1.21" gem "rubocop-rspec", "~> 2.24" + +gem "yard" From 82f071dabc24d19e3976dd4676fbc23bc21c6515 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Fri, 27 Oct 2023 01:33:42 +0300 Subject: [PATCH 22/39] Edited type signature for generator --- lib/code_generator/generator.rb | 8 +++---- lib/extensions/extensions.rb | 1 + sig/code_generator/generator.rbs | 38 +++++++++++++++++++++++++------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/lib/code_generator/generator.rb b/lib/code_generator/generator.rb index dae721a..e1ffbc8 100644 --- a/lib/code_generator/generator.rb +++ b/lib/code_generator/generator.rb @@ -1,16 +1,14 @@ # frozen_string_literal: true -require "securerandom" - module CodeGenerator # :nodoc: class Generator # :nodoc: PARAMS_TYPES = %i[req opt keyreq key block].freeze # +CodeGenerator::Generator#new+ -> CodeGenerator::Generator # - # This object constructor helps to easily create class with stubbed methods (with support of public, public singleton, - # private and private singleton methods and their params). For e.g., we want to create a class which have several - # public methods: + # This object constructor helps to easily create class with stubbed methods (with support of public, public + # singleton, private and private singleton methods and their params). For e.g., we want to create a class which + # has several public methods: # @example # # By passing array of Symbols or Strings # code = CodeGenerator.new(public_methods: %i[method1 method2]) diff --git a/lib/extensions/extensions.rb b/lib/extensions/extensions.rb index 87a066c..307c008 100644 --- a/lib/extensions/extensions.rb +++ b/lib/extensions/extensions.rb @@ -1,3 +1,4 @@ # frozen_string_literal: true +require "securerandom" require_relative "validator" diff --git a/sig/code_generator/generator.rbs b/sig/code_generator/generator.rbs index 39f60a1..c909433 100644 --- a/sig/code_generator/generator.rbs +++ b/sig/code_generator/generator.rbs @@ -1,14 +1,36 @@ module CodeGenerator - module Generator - type paramType = Integer | Symbol | String | Array[String | Symbol | paramType] + class Generator + type req = [Symbol, Symbol] + type opt = [Symbol, Symbol, untyped] + type keyreq = [Symbol, Symbol] + type key = [Symbol, Symbol, untyped] + type block = [Symbol, Symbol] + type methods_options = { + args: [req | opt | keyreq | key | block], + should_return: Integer, + generate: boolish + } + type primitives = Integer | Symbol | String + type paramType = primitives | [primitives, methods_options] - PARAMS_TYPES: Array[Symbol] + PARAMS_TYPES: [Symbol] - def initialize: (?public_methods: paramType?, public_class_methods: paramType?, private_methods: paramType?, private_class_methods: paramType?) -> void + def initialize: ( + ?public_methods: paramType?, + ?public_class_methods: paramType?, + ?private_methods: paramType?, + ?private_class_methods: paramType?, + ?should_return: primitives | nil, + ?generate: boolish + ) -> untyped - @public_methods: Integer | Array[Symbol | String | Array[untyped]] | nil - @public_class_methods: Integer | Array[Symbol | String] | nil - @private_methods: Integer | Array[Symbol | String] | nil - @private_class_methods: Integer | Array[Symbol | String] | nil + @public_methods: paramType? + @public_class_methods: paramType? + @private_methods: paramType? + @private_class_methods: paramType? + @should_return: untyped + @generate: boolish + + def generate_code: -> untyped end end From b5d17d28c5cf3e0cafd3e80a9023e109d5cd7769 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Fri, 27 Oct 2023 02:05:04 +0300 Subject: [PATCH 23/39] Added skeleton for private methods type signature --- sig/code_generator/generator.rbs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sig/code_generator/generator.rbs b/sig/code_generator/generator.rbs index c909433..9aa1da2 100644 --- a/sig/code_generator/generator.rbs +++ b/sig/code_generator/generator.rbs @@ -32,5 +32,19 @@ module CodeGenerator @generate: boolish def generate_code: -> untyped + + private + + def generate_public_methods: -> untyped + + def generate_public_class_methods: -> untyped + + def any_generation_rules: -> boolish + + def both_generation_rules: -> boolish + + def operate_on_value: -> untyped + + def generate_random_object: -> untyped end end From f86ed82be2eeb4e4aa12757af21f15bbde4da70d Mon Sep 17 00:00:00 2001 From: unurgunite Date: Fri, 10 May 2024 13:40:42 +0300 Subject: [PATCH 24/39] Fixed typo in `README.md` --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f9528c9..ce73f0a 100644 --- a/README.md +++ b/README.md @@ -121,9 +121,9 @@ code.method(:method2).parameters # => [[:req, :foo], [:req, :bar], [:opt, :opts] ## TODO - [x] Add support for public methods -- [x] Add support for public public class methods -- [ ] Add support for public private methods -- [ ] Add support for public private class methods +- [x] Add support for public class methods +- [ ] Add support for private methods +- [ ] Add support for private class methods ## Development From 40f5fe9f135836d4309fbf190912d21ed0e8e21e Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 10:59:32 +0300 Subject: [PATCH 25/39] Added lock file --- Gemfile.lock | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 Gemfile.lock diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..3b53ee0 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,66 @@ +PATH + remote: . + specs: + code_generator (0.1.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) + ruby-progressbar (1.13.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.1.0) + +PLATFORMS + arm64-darwin-24 + ruby + +DEPENDENCIES + code_generator! + rake (~> 13.0) + rspec (~> 3.0) + rubocop (~> 1.21) + +BUNDLED WITH + 2.7.2 From 5108b05c425d9f0a3e3afdddce11f49b3915e3c3 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 11:51:13 +0300 Subject: [PATCH 26/39] Updated code --- .github/workflows/ci.yml | 80 +++++++++++++++++++++++++++ .github/workflows/main.yml | 27 --------- .rubocop.yml | 7 +++ Gemfile | 7 +-- Gemfile.lock | 24 +++++++- code_generator.gemspec | 3 +- spec/code_generator/generator_spec.rb | 2 - 7 files changed, 113 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0ac3372 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,80 @@ +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 + bundle exec rubocop --config .rubocop.test.yml lib/ -A + + - 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/.rubocop.yml b/.rubocop.yml index e3462a7..26d23dc 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,8 @@ +plugins: + - rubocop-sorted_methods_by_call + AllCops: + NewCops: enable TargetRubyVersion: 2.6 Style/StringLiterals: @@ -11,3 +15,6 @@ Style/StringLiteralsInInterpolation: Layout/LineLength: Max: 120 + +SortedMethodsByCall/Waterfall: + Enabled: true diff --git a/Gemfile b/Gemfile index e9e7bef..1015c3d 100644 --- a/Gemfile +++ b/Gemfile @@ -6,11 +6,8 @@ source "https://rubygems.org" gemspec gem "rake", "~> 13.0" - gem "rspec", "~> 3.0" - -gem "rubocop", "~> 1.21" - +gem "rubocop" gem "rubocop-rspec", "~> 2.24" - +gem "rubocop-sorted_methods_by_call", "~> 1.0", ">= 1.0.1" gem "yard" diff --git a/Gemfile.lock b/Gemfile.lock index 3b53ee0..4e2c6dc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -47,10 +47,27 @@ GEM 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.0.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 @@ -60,7 +77,10 @@ DEPENDENCIES code_generator! rake (~> 13.0) rspec (~> 3.0) - rubocop (~> 1.21) + rubocop + rubocop-rspec (~> 2.24) + rubocop-sorted_methods_by_call (~> 1.0, >= 1.0.1) + yard BUNDLED WITH - 2.7.2 + 2.3.25 diff --git a/code_generator.gemspec b/code_generator.gemspec index 7a23a85..63e62b5 100644 --- a/code_generator.gemspec +++ b/code_generator.gemspec @@ -6,7 +6,7 @@ Gem::Specification.new do |spec| spec.name = "code_generator" spec.version = CodeGenerator::VERSION spec.authors = ["unurgunite"] - spec.email = [""] + 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." @@ -18,6 +18,7 @@ Gem::Specification.new do |spec| 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["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. diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index de34679..5de19cf 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "../support/shared_examples/acts_as_public_methods_spec" - RSpec.describe CodeGenerator::Generator do describe "#generate_code" do context "public_methods" do From 988a82c46e087618c804755e3227abcfe9b312c3 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 12:36:12 +0300 Subject: [PATCH 27/39] Added DSL support --- .gitignore | 1 + lib/code_generator.rb | 11 +- lib/code_generator/generator.rb | 239 +++++++--------------- lib/code_generator/method_config.rb | 57 ++++++ lib/code_generator/parameter.rb | 36 ++++ lib/extensions/extensions.rb | 4 - lib/extensions/validator.rb | 92 --------- spec/code_generator/generator_spec.rb | 282 ++------------------------ 8 files changed, 191 insertions(+), 531 deletions(-) create mode 100644 lib/code_generator/method_config.rb create mode 100644 lib/code_generator/parameter.rb delete mode 100644 lib/extensions/extensions.rb delete mode 100644 lib/extensions/validator.rb 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/lib/code_generator.rb b/lib/code_generator.rb index feea9c6..50075bb 100644 --- a/lib/code_generator.rb +++ b/lib/code_generator.rb @@ -1,10 +1,11 @@ # frozen_string_literal: true +require "securerandom" +require_relative "code_generator/version" +require_relative "code_generator/parameter" +require_relative "code_generator/method_config" +require_relative "code_generator/generator" + module CodeGenerator class Error < StandardError; end - # Your code goes here... end - -require_relative "extensions/extensions" -require_relative "code_generator/version" -require_relative "code_generator/generator" diff --git a/lib/code_generator/generator.rb b/lib/code_generator/generator.rb index e1ffbc8..3b04655 100644 --- a/lib/code_generator/generator.rb +++ b/lib/code_generator/generator.rb @@ -1,209 +1,110 @@ # frozen_string_literal: true -module CodeGenerator # :nodoc: - class Generator # :nodoc: - PARAMS_TYPES = %i[req opt keyreq key block].freeze - - # +CodeGenerator::Generator#new+ -> CodeGenerator::Generator - # - # This object constructor helps to easily create class with stubbed methods (with support of public, public - # singleton, private and private singleton methods and their params). For e.g., we want to create a class which - # has several public methods: - # @example - # # By passing array of Symbols or Strings - # code = CodeGenerator.new(public_methods: %i[method1 method2]) - # code.methods(false) #=> [:method1, :method2] - # # By passing number of methods - # code = CodeGenerator.new(public_methods: 3) - # code.methods(false) #=> [:method1, :method2, :method3] - # If you want to pass arguments, you can pass it according the following signature. Note that only +:opt+ and +:key+ - # variables can be predefined (by default they would be nil), also +:block+ variable could be presented only once for - # each method: - # @example - # code = CodeGenerator.new(public_methods: [:method1, [:method2, { args: [[:req, :foo], - # [:req, :bar], - # [:opt, :opts, {}], - # [:keyreq, :some_keyword], - # [:key, :some_key, 123], - # [:block, :some_block]], - # should_return: 123 }], :method3]) - # If you want to specify returnable object, you can pass it inside +:should_return+ key. Also you can pass class - # names, but method would return class itself. Note that +shout+return+ and +generate+ args passed as options for - # specific method would override global values of this two args: - # @example - # # By passing object - # code = CodeGenerator.new(public_methods: [[:method1, { should_return: 123 }]) - # code.method1 #=> 123 - # # By passing class (not all classes are supported) - # code = CodeGenerator.new(public_methods: [[:method1, { should_return: Integer, generate: true }]) - # code.method1 #=> some random Integer - # - # @param [Integer|String|Symbol|Array>] public_methods - def initialize(public_methods: nil, public_class_methods: nil, private_methods: nil, private_class_methods: nil, - should_return: nil, generate: false) - @public_methods = public_methods - @public_class_methods = public_class_methods - @private_methods = private_methods - @private_class_methods = private_class_methods - @should_return = should_return - @generate = generate +module CodeGenerator + class Generator + def initialize + @methods = [] + @class_methods = [] end - # +CodeGenerator::Generator#generate_code+ -> value - # - # - def generate_code - generate_public_methods - generate_public_class_methods + def self.new(&block) + generator = allocate + generator.__send__(:initialize) + generator.instance_eval(&block) if block + generator + end + + def build + klass = Class.new + define_instance_methods(klass) + define_class_methods(klass) + klass end private - def generate_public_methods - case @public_methods - when String, Symbol - # code = CodeGenerator.new(public_methods: :method1, should_return: 123, generate: true) - return define_singleton_method(@public_methods) {} unless any_generation_rules + attr_reader :methods, :class_methods - object_to_return = operate_on_value(@should_return, @generate) - define_singleton_method(@public_methods) { object_to_return } - when Integer - # code = CodeGenerator.new(public_methods: 1, should_return: 123, generate: true) - return if @public_methods.negative? || @public_methods.zero? + def define_instance_methods(klass) + return unless methods - object_to_return = operate_on_value(@should_return, @generate) + methods.each do |method_config| + return_value = calculate_return_value(method_config) - 1.upto(@public_methods) do |time| - define_singleton_method("method#{time}") do - object_to_return + case method_config.visibility + when :public + klass.define_method(method_config.name) do |*_args, **_kwargs| + return_value end - end - when Array - return if @public_methods.empty? - - @public_methods.each do |m| - if m.instance_of?(Symbol) || m.instance_of?(String) - if !both_generation_rules - define_singleton_method(m) {} - elsif any_generation_rules - object_to_return = operate_on_value(@should_return, @generate) - define_singleton_method(m) do - object_to_return - end - end - elsif m.instance_of?(Array) - unless (m_name = m.first).instance_of?(Symbol) || m_name.instance_of?(String) - raise ArgumentError, "Method name should be Symbol or String, but #{m_name.class} was passed." - end - unless (opts = m.last).is_a?(Hash) # && (arguments = opts[:args]).is_a?(Array) - raise ArgumentError, "Method arguments should behave as Hash, but #{m_name.class} was passed." - end - - arguments = opts[:args] ? arguments_parser(opts[:args]) : nil - object_to_return = operate_on_value(opts[:should_return], opts[:generate]).inspect - instance_eval <<-METHOD, __FILE__, __LINE__ + 1 - def #{m_name}(#{arguments}) - #{object_to_return} - end - METHOD + when :private + klass.send(:define_method, method_config.name) do |*_args, **_kwargs| + return_value end + klass.send(:private, method_config.name) end - when NilClass - nil - else - raise ArgumentError, "public_methods is #{@public_methods.class} but expected Array[Symbol|String] or Integer." end end - def generate_public_class_methods - case @public_class_methods - when String, Symbol - return self.class.define_singleton_method(@public_class_methods) {} unless any_generation_rules - - object_to_return = operate_on_value(@should_return, @generate) - self.class.define_singleton_method(@public_class_methods) { object_to_return } - when Integer - # code = CodeGenerator.new(public_class_methods: 1, should_return: 123, generate: true) - return if @public_class_methods.negative? || @public_class_methods.zero? + def define_class_methods(klass) + return unless class_methods - object_to_return = operate_on_value(@should_return, @generate) + class_methods.each do |method_config| + return_value = calculate_return_value(method_config) - 1.upto(@public_class_methods) do |time| - self.class.define_singleton_method("method#{time}") do - object_to_return + case method_config.visibility + when :public_class + klass.define_singleton_method(method_config.name) do |*_args, **_kwargs| + return_value end - end - when Array - return if @public_class_methods.empty? - - @public_class_methods.each do |m| - if m.instance_of?(Symbol) || m.instance_of?(String) - if !both_generation_rules - self.class.define_singleton_method(m) {} - elsif any_generation_rules - object_to_return = operate_on_value(@should_return, @generate) - self.class.define_singleton_method(m) do - object_to_return - end - end - elsif m.instance_of?(Array) - unless (m_name = m.first).instance_of?(Symbol) || m_name.instance_of?(String) - raise ArgumentError, "Method name should be Symbol or String, but #{m_name.class} was passed." - end - unless (opts = m.last).is_a?(Hash) # && (arguments = opts[:args]).is_a?(Array) - raise ArgumentError, "Method arguments should behave as Hash, but #{m_name.class} was passed." - end - - arguments = opts[:args] ? arguments_parser(opts[:args]) : nil - object_to_return = operate_on_value(opts[:should_return], opts[:generate]).inspect - class_eval <<-METHOD, __FILE__, __LINE__ + 1 - def #{m_name}(#{arguments}) - #{object_to_return} - end - METHOD + when :private_class + klass.singleton_class.send(:define_method, method_config.name) do |*_args, **_kwargs| + return_value end + klass.singleton_class.send(:private, method_config.name) end - when NilClass - nil - else - raise ArgumentError, "public_methods is #{@public_class_methods.class} but expected Array[Symbol|String] or Integer." end end - def any_generation_rules - @should_return || @generate - end - - def both_generation_rules - @should_return && @generate - end - - def operate_on_value(should_return, generate) - random_object = if generate && should_return.instance_of?(Class) - generate_random_object(should_return) - elsif should_return - should_return - end - - return random_object if random_object && should_return - - random_object || should_return + 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 def generate_random_object(klass) case klass.name when "Integer" - rand.to_s.sub(/.*?\./, "").to_i + rand(1..1_000_000) when "String" SecureRandom.alphanumeric(10) when "Symbol" SecureRandom.alphanumeric(10).to_sym + else + klass end end - def arguments_parser(arguments) - validator = Validator.new(arguments) - validator.validate! + # DSL methods + def public_method(name, &block) + method_config = MethodConfig.new(name, :public, &block) + @methods << method_config + end + + def private_method(name, &block) + method_config = MethodConfig.new(name, :private, &block) + @methods << method_config + end + + def public_class_method(name, &block) + method_config = MethodConfig.new(name, :public_class, &block) + @class_methods << method_config + end + + def private_class_method(name, &block) + method_config = MethodConfig.new(name, :private_class, &block) + @class_methods << method_config end end end diff --git a/lib/code_generator/method_config.rb b/lib/code_generator/method_config.rb new file mode 100644 index 0000000..b4201fd --- /dev/null +++ b/lib/code_generator/method_config.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module CodeGenerator + class MethodConfig + attr_reader :name, :visibility, :parameters, :return_value, :generate_random + + 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 %i[public private public_class + private_class].include?(visibility) + + @name = name.to_sym + @visibility = visibility + @parameters = [] + @return_value = nil + @generate_random = false + + yield self if block_given? + end + + def required(param_name) + validate_param_name(param_name) + parameters << Parameter.new(:required, param_name) + end + + def optional(param_name, default: nil) + validate_param_name(param_name) + parameters << Parameter.new(:optional, param_name, default: default) + end + + def keyword_required(param_name) + validate_param_name(param_name) + parameters << Parameter.new(:keyword_required, param_name) + end + + def keyword(param_name, default: nil) + validate_param_name(param_name) + parameters << Parameter.new(:keyword, param_name, default: default) + end + + def returns(value) + self.return_value = value + end + + def generate(value: true) + self.generate_random = value + end + + private + + attr_writer :parameters, :return_value, :generate_random + + 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/code_generator/parameter.rb b/lib/code_generator/parameter.rb new file mode 100644 index 0000000..556427d --- /dev/null +++ b/lib/code_generator/parameter.rb @@ -0,0 +1,36 @@ +# lib/code_generator/parameter.rb +# frozen_string_literal: true + +module CodeGenerator + class Parameter + VALID_TYPES = %i[required optional keyword_required keyword].freeze + + attr_reader :type, :name, :default + + 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 + + def to_ruby_param + case type + when :required + name.to_s + when :optional + "#{name} = #{default.inspect}" + when :keyword_required + "#{name}:" + when :keyword + "#{name}: #{default.inspect}" + end + end + + def ruby_param_name + name + end + end +end diff --git a/lib/extensions/extensions.rb b/lib/extensions/extensions.rb deleted file mode 100644 index 307c008..0000000 --- a/lib/extensions/extensions.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -require "securerandom" -require_relative "validator" diff --git a/lib/extensions/validator.rb b/lib/extensions/validator.rb deleted file mode 100644 index aed0532..0000000 --- a/lib/extensions/validator.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: false - -class Validator - PARAMS_TYPES = %i[req opt keyreq key block].freeze - - def initialize(arguments) - @arguments = arguments - end - - def validate! - validate(arguments_table).join(", ") - end - - private - - def arguments_table - counts = Hash.new(0) - @arguments.each do |arg| - type = arg.first - raise ArgumentError, "@arguments has illegal type: #{type.inspect}." unless PARAMS_TYPES.include?(type) - - counts[type] += 1 - end - counts - end - - def validate(counts) - arr = [] - arr << validate_req if counts[:req].positive? - arr << validate_opt if counts[:opt].positive? - arr << validate_keyreq if counts[:keyreq].positive? - arr << validate_key if counts[:key].positive? - arr << validate_block if counts[:block].positive? - arr - end - - def validate_req - if (reqs = @arguments.select { _1.first == :req }).any? { _1.size > 2 } - raise ArgumentError, messages[:req] - end - - reqs.map(&:last).join(", ") - end - - def validate_opt - if (opts = @arguments.select { _1.first == :opt }).any? { _1.size != 3 } - raise ArgumentError, messages[:opt] - end - - str = opts.each_with_object("") do |opt, obj| - obj << "#{opt[1]}=#{opt.last}#{"," if @o_size ||= opts.size == 1}" - end - @o_size ? str.chop : str - end - - def validate_keyreq - if (keyreqs = @arguments.select { _1.first == :keyreq }).any? { _1.size > 2 } - raise ArgumentError, messages[:keyreq] - end - - keyreqs.map(&:last).map { "#{_1}:" }.join(", ") - end - - def validate_key - if (keys = @arguments.select { _1.first == :key }).any? { _1.size != 3 } - raise ArgumentError, messages[:key] - end - - str = keys.each_with_object("") do |key, obj| - obj << "#{key[1]}: #{key.last}#{"," if @k_size ||= keys.size == 1}" - end - @k_size ? str.chop : str - end - - def validate_block - if (block = @arguments.select { _1.first == :block }).any? { _1.size > 2 } || block.size > 1 - raise ArgumentError, messages[:block] - end - - "&#{block[0].last}" - end - - def messages - { - req: "Required params should have no params.", - opt: "Optional params should have only one value.", - keyreq: "Required keyword params should be empty.", - key: "Keyword params should have only one value.", - block: "Block param should have no values." - } - end -end diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index 5de19cf..1f6625e 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -1,271 +1,31 @@ # frozen_string_literal: true RSpec.describe CodeGenerator::Generator do - describe "#generate_code" do - context "public_methods" do - before do |test| - code.generate_code unless test.metadata[:skip_generation] + it "creates public methods with DSL" do + generator = CodeGenerator::Generator.new do |g| + g.public_method :hello do |m| + m.returns "world" end + end - context "when Symbol is passed" do - let(:code) { described_class.new(public_methods: :method1) } - - it "returns method" do - expect(code.public_methods(false) - [:generate_code]).to contain_exactly(:method1) - end - - context "when options for method were passed" do - let(:code) { described_class.new(public_methods: :method1, should_return: Integer, generate: true) } - - it "returns some value" do - expect(code.method1).to be_a Integer - end - end - end - - context "when String is passed" do - let(:code) { described_class.new(public_methods: "method1") } - - it "returns a method" do - expect(code.public_methods(false) - [:generate_code]).to eq [:method1] - end - - context "when options for method were passed" do - let(:code) { described_class.new(public_methods: :method1, should_return: Integer, generate: true) } - - it "returns some value" do - expect(code.method1).to be_a Integer - end - end - end - - context "when Integer is passed" do - let(:code) { described_class.new(public_methods: 2) } - - it "returns two methods" do - expect(code.public_methods(false) - [:generate_code]).to contain_exactly(:method1, :method2) - end - - context "when there is any params" do - let(:code) { described_class.new(public_methods: 2, should_return: Integer, generate: true) } - - it "returns some objects" do - expect(code.method1).to be_an Integer - expect(code.method2).to be_an Integer - end - end - end - - context "when Array is passed" do - context "when first value is any other object than String or Symbol" do - let(:some_value) { 1 } - let(:code) { described_class.new(public_methods: [[11, { should_return: 123 }]]) } - - it "raise error", :skip_generation do - expect do - code.generate_code - end.to raise_error(ArgumentError) - end - end - - context "when values are Symbol or String" do - let(:code) { described_class.new(public_methods: [:method1, "method2"]) } - - it "returns two methods" do - expect(code.public_methods(false) - [:generate_code]).to contain_exactly(:method1, :method2) - end - - context "when additional params were passed" do - let(:code) do - described_class.new(public_methods: [:method1, "method2"], should_return: Integer, generate: true) - end - - it "returns some value" do - expect(code.method1).to be_an Integer - expect(code.method2).to be_an Integer - end - - context "when params are passed for specific method" do - let(:code) do - described_class.new(public_methods: [:method1, ["method2", { should_return: Integer, generate: true }]]) - end - - it "returns nil and random Integer" do - expect(code.method1).to eq nil - expect(code.method2).to be_an Integer - end - end - - context "when params are passed in global scope" do - let(:code) do - described_class.new(public_methods: [:method1, ["method2", { should_return: Integer, generate: true }]], - should_return: String, generate: true) - end - - it "ignores global params" do - expect(code.method1).to be_a String - expect(code.method2).to be_an Integer - end - end - end - end - - context "when `should_return` passed" do - context "when instance is passed" do - let(:m1_value) { 123 } - let(:m2_value) { Integer } - let(:code) do - described_class.new(public_methods: [[:method1, { should_return: m1_value }], - [:method2, { should_return: m2_value }]]) - end - - it "returns passed object" do - expect(code.method1).to eq(m1_value) - expect(code.method2).to eq(m2_value) - end - end - end - - context "when `generate` passed" do - context "when it was passed in correct way" do - subject(:code) do - described_class.new(public_methods: [[:method1, { should_return: klass, generate: true }], - [:method2, { should_return: klass, generate: true }]]) - end - - context "when Integer passed" do - let(:klass) { Integer } - - it "returns random integer" do - expect(code.method1).to be_an Integer - expect(code.method2).to be_an Integer - end - end - - context "when String passed" do - let(:klass) { String } - - it "returns random string" do - expect(code.method1).to be_a String - expect(code.method2).to be_a String - end - end - - context "when Symbol passed" do - let(:klass) { Symbol } - - it "returns random symbol" do - expect(code.method1).to be_a Symbol - expect(code.method2).to be_a Symbol - end - end - - context "when unsupported class passed" do - let(:klass) { Object } - - it "returns passed class" do - expect(code.method1).to eq klass - expect(code.method2).to eq klass - end - end - end - end - end - - context "when passed anything else" do - let(:code) do - described_class.new(public_methods: Object.new) - end - - it "returns error", :skip_generation do - expect { code.generate_code }.to raise_error(ArgumentError) - end - end - - context "when passed arguments to methods" do - subject(:code) { described_class.new(public_methods: [[:method1, { args: args }]]) } - - context "when required arguments passed" do - let(:args) { [[:req, :foo]] } - - it do - expect(code.method(:method1).parameters).to eq args - end - - context "when args were passed incorrectly" do - let(:args) { [[:req, :foo, 123]] } - - it "", :skip_generation do - expect { code.generate_code }.to raise_error ArgumentError - end - end - end - - context "when optional arguments passed" do - let(:args) { [[:opt, :opts, {}]] } - - it do - expect(code.method(:method1).parameters).to eq [%i[opt opts]] - end - - context "when args were passed incorrectly" do - let(:args) { [[:opt, :opts]] } - - it "", :skip_generation do - expect { code.generate_code }.to raise_error ArgumentError - end - end - end - - context "when required keyword arguments passed" do - let(:args) { [[:keyreq, :some_keyword]] } - - it { expect(code.method(:method1).parameters).to eq args } - - context "when args were passed incorrectly" do - let(:args) { [[:keyreq, :some_keyword, 123]] } - - it "", :skip_generation do - expect { code.generate_code }.to raise_error ArgumentError - end - end - end - - context "when keyword arguments passed" do - let(:args) { [[:key, :some_key, 123]] } - - it { expect(code.method(:method1).parameters).to eq [%i[key some_key]] } - - context "when args were passed incorrectly" do - let(:args) { [[:key, :some_key]] } - - it "", :skip_generation do - expect { code.generate_code }.to raise_error ArgumentError - end - end - end - - context "when block argument passed" do - let(:args) { [[:block, :some_block]] } - - it { expect(code.method(:method1).parameters).to eq args } - - context "when args were passed incorrectly" do - let(:args) { [[:block, :some_block, 123]] } - - it "", :skip_generation do - expect { code.generate_code }.to raise_error ArgumentError - end - end + klass = generator.build + obj = klass.new + expect(obj.hello).to eq "world" + end - context "when there is more than one block" do - let(:args) { [[:block, :foo_block], [[:block, :bar_block]]] } - it "", :skip_generation do - expect { code.generate_code }.to raise_error ArgumentError - end - end - end + it "creates private methods with parameters" do + generator = CodeGenerator::Generator.new do |g| + g.private_method :calculate do |m| + m.required :x + m.optional :y, default: 10 + m.returns Integer + m.generate value: true end end + + klass = generator.build + obj = klass.new + result = obj.send(:calculate, 5) + expect(result).to be_an(Integer) end end From 2f4a276ce015c45eede896ca64b275ef34ebc2b9 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 13:07:35 +0300 Subject: [PATCH 28/39] Added params support --- lib/code_generator/generator.rb | 94 ++++++----- lib/code_generator/method_config.rb | 5 +- lib/code_generator/parameter.rb | 12 +- spec/code_generator/generator_spec.rb | 232 ++++++++++++++++++++++++-- 4 files changed, 282 insertions(+), 61 deletions(-) diff --git a/lib/code_generator/generator.rb b/lib/code_generator/generator.rb index 3b04655..5842f58 100644 --- a/lib/code_generator/generator.rb +++ b/lib/code_generator/generator.rb @@ -1,3 +1,4 @@ +# lib/code_generator/generator.rb # frozen_string_literal: true module CodeGenerator @@ -21,26 +22,48 @@ def build klass end + def public_method(name, &block) + method_config = MethodConfig.new(name, :public, &block) + @methods << method_config + end + + def private_method(name, &block) + method_config = MethodConfig.new(name, :private, &block) + @methods << method_config + end + + def protected_method(name, &block) + method_config = MethodConfig.new(name, :protected, &block) + @methods << method_config + end + + def public_class_method(name, &block) + method_config = MethodConfig.new(name, :public_class, &block) + @class_methods << method_config + end + + def private_class_method(name, &block) + method_config = MethodConfig.new(name, :private_class, &block) + @class_methods << method_config + end + private attr_reader :methods, :class_methods def define_instance_methods(klass) - return unless methods - - methods.each do |method_config| - return_value = calculate_return_value(method_config) + return unless @methods + @methods.each do |method_config| case method_config.visibility when :public - klass.define_method(method_config.name) do |*_args, **_kwargs| - return_value - end + define_method_with_params(klass, method_config, :define_method) when :private - klass.send(:define_method, method_config.name) do |*_args, **_kwargs| - return_value - end + 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 @@ -49,22 +72,36 @@ def define_class_methods(klass) return unless class_methods class_methods.each do |method_config| - return_value = calculate_return_value(method_config) - case method_config.visibility when :public_class - klass.define_singleton_method(method_config.name) do |*_args, **_kwargs| - return_value - end + define_method_with_params(klass.singleton_class, method_config, :define_method) when :private_class - klass.singleton_class.send(:define_method, method_config.name) do |*_args, **_kwargs| - return_value - end + define_method_with_params(klass.singleton_class, method_config, :define_method) klass.singleton_class.send(:private, method_config.name) end end end + # lib/code_generator/generator.rb + 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, &block| + 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 + 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) @@ -85,26 +122,5 @@ def generate_random_object(klass) klass end end - - # DSL methods - def public_method(name, &block) - method_config = MethodConfig.new(name, :public, &block) - @methods << method_config - end - - def private_method(name, &block) - method_config = MethodConfig.new(name, :private, &block) - @methods << method_config - end - - def public_class_method(name, &block) - method_config = MethodConfig.new(name, :public_class, &block) - @class_methods << method_config - end - - def private_class_method(name, &block) - method_config = MethodConfig.new(name, :private_class, &block) - @class_methods << method_config - end end end diff --git a/lib/code_generator/method_config.rb b/lib/code_generator/method_config.rb index b4201fd..4c8f86b 100644 --- a/lib/code_generator/method_config.rb +++ b/lib/code_generator/method_config.rb @@ -4,10 +4,11 @@ module CodeGenerator class MethodConfig attr_reader :name, :visibility, :parameters, :return_value, :generate_random + VALID_VISIBILITIES = %i[public private protected public_class private_class].freeze + 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 %i[public private public_class - private_class].include?(visibility) + raise ArgumentError, "Invalid visibility: #{visibility}" unless VALID_VISIBILITIES.include?(visibility) @name = name.to_sym @visibility = visibility diff --git a/lib/code_generator/parameter.rb b/lib/code_generator/parameter.rb index 556427d..c2e49c8 100644 --- a/lib/code_generator/parameter.rb +++ b/lib/code_generator/parameter.rb @@ -21,11 +21,19 @@ def to_ruby_param when :required name.to_s when :optional - "#{name} = #{default.inspect}" + if default.nil? + "#{name} = nil" + else + "#{name} = #{default.inspect}" + end when :keyword_required "#{name}:" when :keyword - "#{name}: #{default.inspect}" + if default.nil? + "#{name}: nil" + else + "#{name}: #{default.inspect}" + end end end diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index 1f6625e..11de57e 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -1,31 +1,227 @@ +# spec/code_generator/generator_spec.rb # frozen_string_literal: true RSpec.describe CodeGenerator::Generator do - it "creates public methods with DSL" do - generator = CodeGenerator::Generator.new do |g| - g.public_method :hello do |m| - m.returns "world" + describe "public methods" do + it "creates a simple public method" do + generator = CodeGenerator::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 - klass = generator.build - obj = klass.new - expect(obj.hello).to eq "world" + it "creates public method with required parameters" do + generator = CodeGenerator::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 = CodeGenerator::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 = CodeGenerator::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 - it "creates private methods with parameters" do - generator = CodeGenerator::Generator.new do |g| - g.private_method :calculate do |m| - m.required :x - m.optional :y, default: 10 - m.returns Integer - m.generate value: true + describe "private methods" do + it "creates a private method" do + generator = CodeGenerator::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 = CodeGenerator::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 = CodeGenerator::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 - klass = generator.build - obj = klass.new - result = obj.send(:calculate, 5) - expect(result).to be_an(Integer) + describe "class methods" do + it "creates public class method" do + generator = CodeGenerator::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 = CodeGenerator::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 = CodeGenerator::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 = CodeGenerator::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 = CodeGenerator::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 + CodeGenerator::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 + CodeGenerator::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 + CodeGenerator::MethodConfig.new(:test, :invalid_visibility) + end.to raise_error(ArgumentError, "Invalid visibility: invalid_visibility") + end end end From 8a54c33538019dbfab5cd1b85e40020559487206 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 13:57:56 +0300 Subject: [PATCH 29/39] Updated version --- Gemfile.lock | 2 +- lib/code_generator/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4e2c6dc..35243b7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - code_generator (0.1.0) + code_generator (1.0.0) GEM remote: https://rubygems.org/ diff --git a/lib/code_generator/version.rb b/lib/code_generator/version.rb index 211aae1..b0fdf22 100644 --- a/lib/code_generator/version.rb +++ b/lib/code_generator/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module CodeGenerator - VERSION = "0.1.0" + VERSION = "1.0.0" end From b92f3363a00e2335400cf6854e3edac5c2c2147e Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 14:05:28 +0300 Subject: [PATCH 30/39] Updated `README.md` --- README.md | 357 +++++++++++++++++++++++++----------------------------- 1 file changed, 165 insertions(+), 192 deletions(-) diff --git a/README.md b/README.md index ce73f0a..26013ba 100644 --- a/README.md +++ b/README.md @@ -1,248 +1,221 @@ -# CodeGenerator - -![Alt](https://repobeats.axiom.co/api/embed/cf02cc6438367e8127e0aae8fc871c935844f4e8.svg "Project stats") - -Welcome to the Ruby library which could generate code in any directions you want. It supports flexible settings for your -purposes and does not need any requirements. - -## Documentation content +Here's a **modern, professional README.md** that reflects your DSL-based code generator: -1. [Overview][1] -2. [Installation][2] - 1. [Build from source][2.1] - 1. [Manual installation][2.1.1] - 2. [Automatic installation][2.1.2] - 2. [Build via bundler][2.2] -3. [Usage][3] -4. [Todo][4] -5. [Development][5] -6. [Requirements][6] - 1. [Common usage][6.1] - 2. [Development purposes][6.2] -7. [Project style guide][7] -8. [Contributing][8] -9. [License][9] -10. [Code of Conduct][10] - -## Overview +# CodeGenerator -As noted above, this gem helps to create large domains of data (objects and their interfaces). The projects source tree -is pretty simple. All of the resources are stored in theirs separate module, so it does code readability much cleaner. +![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.** + +* [CodeGenerator](#codegenerator) + * [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 -Code Generator gem is quite simple to use and install. There are two options to install it — for those who is going to -contribute into the project and for those who is going to embed gem to theirs project. See below for each step. - -### Build from source - -#### Manual installation - -The manual installation includes installation via command line interface. it is practically no different from what -happens during the automatic build of the project: - -```shell -git clone https://github.com/unurgunite/code_generator.git && \ -cd code_generator && \ -bundle install && \ -gem build code_generator.gemspec && \ -gem install code-generator-0.1.0.gem -``` - -Now everything should work fine. Just type `irb` and `require "code_generator"` to start working with the library - -#### Automatic installation - -The automatic installation is simpler but it has at least same steps as manual installation: - -```shell -git clone https://github.com/unurgunite/code_generator.git && \ -cd code_generator && \ -bin/setup -``` - -If you see `irb` interface, then everything works fine. The main goal of automatic installation is that you do not need -to create your own script to simplify project build and clean up the shell history. Note: you do not need to require -projects file after the automatic installation. See `bin/setup` file for clarity of the statement - -### Build via bundler - -This documentation point is close to those who need to embed the library in their project. Just place this gem to your -Gemfile or create it manually via `bundle init`: +Add this line to your application's Gemfile: ```ruby -# Your Gemfile gem 'code_generator' ``` And then execute: -```shell +```bash bundle install ``` -Or install it yourself for non bundled projects as: +Or install it yourself: -```shell +```bash gem install code_generator ``` -## Usage - -All internal docs are available at the separate page: https://unurgunite.github.io/code_generator_docs/ - -This gem is really simple to use. It generates methods according to your requests. It uses standard Ruby parameters -names for representing them as separate objects: +## Usage Examples -1. `req` is a required param -2. `opt` is an optional param -3. `keyreq` is a required keyword param -4. `key` is a keyword param with default value -5. `block` is block +### Basic Public Method ```ruby -code = CodeGenerator::Generator.new(public_methods: [:method1, [:method2, { args: [[:req, :foo], - [:req, :bar], - [:opt, :opts, {}], - [:keyreq, :some_keyword], - [:key, :some_key, 123], - [:block, :some_block]], - should_return: 123 }], :method3], should_return: Integer, generate: true) -code.generate_code #=> [:method1, [:method2, {:args=>[[:req, :foo], [:req, :bar], [:opt, :opts, {}], [:keyreq, :some_keyword], [:key, :some_key, 123], [:block, :some_block]], :should_return=>123}], :method3] -code.methods(false) #=> [:method1, :method2, :method3] -code.method1 #=> 724835356767704 -code.method2(1, { foo: 555 }, some_keyword: 345) #=> 123 -code.method3 #=> 724835356767704 # random objects will be fixed in next release - -code.method(:method2) #=> #[[:req, :foo], [:req, :bar], [:opt, :opts, {}], [:keyreq, :some_keyword], [:key, :some_key, 123], [:block, :some_block]], :should_return=>123}], :method3], @public_class_methods=nil, @private_methods=nil, @private_class_methods=nil, @should_return=Integer, @generate=true>.method2(foo, bar, opts=..., some_keyword:, some_key: ..., &some_block) /home/ruby/code_generator/lib/code_generator/generator.rb:99> -code.method(:method2).parameters # => [[:req, :foo], [:req, :bar], [:opt, :opts], [:keyreq, :some_keyword], [:key, :some_key], [:block, :some_block]] +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" ``` -## TODO - -- [x] Add support for public methods -- [x] Add support for public class methods -- [ ] Add support for private methods -- [ ] Add support for private class methods - -## Development - -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can -also run `bin/console` for an interactive prompt that will allow you to experiment. - -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). - -## Requirements - -This section will show dependencies which are used in the project. This section splits in two other sections — -requirements for common use and requirements for the development purposes. - -### Common use - -The `code_generator` gem does not have any requirements - -### Development purposes - -For the development purposes `code_generator` gem uses: - -| Dependencies | Description | -|----------------------|------------------------------------------------------------------------------------------| -| [RSpec][201] | The RSpec gem is used for test which are located in a separate folder under `spec` name. | -| [RuboCop][202] | The RuboCop gem is used for code formatting. | -| [Rake][203] | The Rake gem is used for building tasks as generating documentation. | -| [rubocop-rspec][204] | The rubocop-rspec gem is used for formatting specs. | -| [YARD][205] | The YARD gem is used for the documentation. | - -## Project style guide - -To make the code base much cleaner gem has its own style guides. They are defined in a root folder of the gem in -a [CONTRIBUTING.md](https://github.com/unurgunite/code_generator/blob/master/CONTRIBUTING.md) file. Check it for more -details. +### Method with Parameters -## Contributing - -Bug reports and pull requests are welcome on GitHub at https://github.com/unurgunite/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/unurgunite/code_generator/blob/master/CODE_OF_CONDUCT.md). To contribute you -should -fork this project and create there new branch: - -```shell -git clone https://github.com/your-beautiful-username/code_generator.git && \ -git checkout -b refactor && \ -git commit -m "Affected new changes" && \ -git push origin refactor +```ruby +generator = CodeGenerator::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 ``` -And then make new pull request with additional notes of what you have done. The better the changes are scheduled, the -faster the PR will be checked. +### Private and Protected Methods -## Code of Conduct +```ruby +generator = CodeGenerator::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" +``` -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/unurgunite/code_generator/blob/master/CODE_OF_CONDUCT.md). +### Class Methods -## License - -The gem is available as open source under the terms of the [GPLv3 License](https://opensource.org/licenses/GPL-3.0). The -copy of the license is stored in project under the `LICENSE.txt` file -name: [copy of the License](https://github.com/unurgunite/code_generator/blob/master/LICENSE.txt) +```ruby +generator = CodeGenerator::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" +``` -The documentation is available as open source under the terms of -the [CC BY-SA 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/) +### Random Value Generation -The other libs are available as open source under the terms of -the [New BSD License](https://opensource.org/licenses/BSD-3-Clause) +```ruby +generator = CodeGenerator::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) +``` -![GPLv3 logo](https://www.gnu.org/graphics/gplv3-or-later.png) -![CC BY-SA 4.0](https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/by-nc.svg) -![BSD license logo](https://upload.wikimedia.org/wikipedia/commons/4/42/License_icon-bsd-88x31.png) +## Testing -[1]:https://github.com/unurgunite/code_generator#overview +Run the test suite: -[2]:https://github.com/unurgunite/code_generator#installation +```bash +bundle exec rspec +``` -[2.1]:https://github.com/unurgunite/code_generator#build-from-source +## Development -[2.1.1]:https://github.com/unurgunite/code_generator#manual-installation +After checking out the repo, run: -[2.1.2]:https://github.com/unurgunite/code_generator#automatic-installation +```bash +bin/setup +``` -[2.2]:https://github.com/unurgunite/code_generator#build-via-bundler +This will install dependencies and start an interactive console. -[3]:https://github.com/unurgunite/code_generator#usage +### Available Commands -[4]:https://github.com/unurgunite/code_generator#todo +- `bin/console` - Interactive development console +- `bin/setup` - Install dependencies and build gem +- `bundle exec rake` - Run tests and linting -[5]:https://github.com/unurgunite/code_generator#development +### Release Process -[6]:https://github.com/unurgunite/code_generator#requirements +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 -[6.1]:https://github.com/unurgunite/code_generator#common-usage +## Requirements -[6.2]:https://github.com/unurgunite/code_generator#development-purposes +- **Ruby**: >= 2.6.0 +- **No external dependencies** -[7]:https://github.com/unurgunite/code_generator#project-style-guide +## Contributing -[8]:https://github.com/unurgunite/code_generator#contributing +Bug reports and pull requests are welcome! Please follow these guidelines: -[9]:https://github.com/unurgunite/code_generator#license +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 -[10]:https://github.com/unurgunite/code_generator#code-of-conduct +Please ensure your code passes all tests and follows the existing style. -[101]:https://rubygems.org/gems/httparty +## License -[102]:https://rubygems.org/gems/nokogiri +The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). -[201]:https://rubygems.org/gems/rspec +## Code of Conduct -[202]:https://rubygems.org/gems/rubocop +Everyone interacting with this project is expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md). -[203]:https://rubygems.org/gems/rake +--- -[204]:https://rubygems.org/gems/rubocop-rspec +> **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. -[205]:https://rubygems.org/gems/yard +``` From 62ec086d259b08ba9f0c4f21bc590ac689557b78 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 14:12:20 +0300 Subject: [PATCH 31/39] Added `LICENSE.txt` --- LICENSE.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.txt 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. From c1fa9b06d92ad2e65be4b2ccf1c27b630c890f0d Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 14:14:15 +0300 Subject: [PATCH 32/39] Added `bin/release` --- bin/release | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 bin/release diff --git a/bin/release b/bin/release new file mode 100755 index 0000000..c9b3e2e --- /dev/null +++ b/bin/release @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log_error() { echo -e "${RED}ERROR: $1${NC}" >&2; } +log_warn() { echo -e "${YELLOW}WARNING: $1${NC}" >&2; } +log_success() { echo -e "${GREEN}$1${NC}"; } + +check_clean_working_directory() { + if ! git diff-index --quiet HEAD --; then + log_error "Working directory is not clean. Please commit or stash your changes." + exit 1 + fi +} + +check_main_branch() { + local current_branch + current_branch=$(git rev-parse --abbrev-ref HEAD) + if [[ "$current_branch" != "main" && "$current_branch" != "master" ]]; then + log_error "Must be on 'main' or 'master' branch to release. Current branch: $current_branch" + exit 1 + fi +} + +detect_gemspec() { + GEMSPEC=$(ls *.gemspec 2>/dev/null | head -n 1) + if [[ -z "${GEMSPEC}" ]]; then + log_error "No .gemspec file found in this directory." + exit 1 + fi + + GEM_NAME=$(ruby -r rubygems -e "spec = Gem::Specification.load('./${GEMSPEC}'); puts spec.name") + VERSION=$(ruby -r rubygems -e "spec = Gem::Specification.load('./${GEMSPEC}'); puts spec.version") +} + +detect_version() { + if git rev-parse "v${VERSION}" >/dev/null 2>&1; then + log_error "Git tag v${VERSION} already exists. Please update the version in your gemspec or version file first." + exit 1 + fi + + echo "Preparing to release version ${VERSION} for ${GEM_NAME}" +} + +confirm_release() { + echo "" + log_warn "This will:" + echo " • Build gem ${GEM_NAME} version ${VERSION}" + echo " • Create git tag v${VERSION}" + echo " • Push tag to origin" + echo " • Publish to RubyGems" + echo "" + read -p "Are you sure you want to proceed? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_warn "Release cancelled." + exit 1 + fi +} + +build_gem() { + log_success "Building gem..." + gem build "${GEMSPEC}" + + if [[ ! -f "${GEM_NAME}-${VERSION}.gem" ]]; then + log_error "Gem build failed or gem file not found: ${GEM_NAME}-${VERSION}.gem" + exit 1 + fi +} + +create_and_push_tag() { + log_success "Creating and pushing git tag..." + git tag "v${VERSION}" -m "Release v${VERSION}" + git push origin "v${VERSION}" +} + +publish_to_rubygems() { + log_success "Publishing to RubyGems..." + gem push "${GEM_NAME}-${VERSION}.gem" +} + +main() { + log_success "Starting release process..." + + check_clean_working_directory + check_main_branch + detect_gemspec + detect_version + confirm_release + + build_gem + create_and_push_tag + publish_to_rubygems + + log_success "✅ Release v${VERSION} completed successfully!" + echo "" + echo "Next steps:" + echo " • Create a GitHub release: https://github.com/$(git remote get-url origin | sed -E 's|.*github.com[:/](.*/.*)\.git|\1|; s|.*github.com[:/](.*)|\1|')/releases/new?tag=v${VERSION}" + echo " • Update CHANGELOG.md for the next release" +} + +main "$@" From 56936202b27e0af09cfcf2385f146a65b4546ca4 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 15:16:40 +0300 Subject: [PATCH 33/39] Updated YARD docs --- .github/workflows/ci.yml | 2 +- .rubocop.yml | 8 -- lib/code_generator/generator.rb | 180 ++++++++++++++++++++++++++- lib/code_generator/method_config.rb | 133 +++++++++++++++++++- lib/code_generator/parameter.rb | 73 ++++++++++- rakelib/docs.rake | 73 +++++++++++ sig/code_generator/generator.rbs | 50 -------- sig/lib/code_generator/generator.rbs | 40 ++++++ 8 files changed, 492 insertions(+), 67 deletions(-) create mode 100644 rakelib/docs.rake delete mode 100644 sig/code_generator/generator.rbs create mode 100644 sig/lib/code_generator/generator.rbs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ac3372..e91f0de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.7, 3.0, 3.1, 3.2, 3.3, 3.4] + ruby: [2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4] steps: - uses: actions/checkout@v4 diff --git a/.rubocop.yml b/.rubocop.yml index 26d23dc..cc2061d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,14 +5,6 @@ AllCops: NewCops: enable TargetRubyVersion: 2.6 -Style/StringLiterals: - Enabled: true - EnforcedStyle: double_quotes - -Style/StringLiteralsInInterpolation: - Enabled: true - EnforcedStyle: double_quotes - Layout/LineLength: Max: 120 diff --git a/lib/code_generator/generator.rb b/lib/code_generator/generator.rb index 5842f58..66d2fa5 100644 --- a/lib/code_generator/generator.rb +++ b/lib/code_generator/generator.rb @@ -1,13 +1,64 @@ -# lib/code_generator/generator.rb # frozen_string_literal: true module CodeGenerator + # 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 [CodeGenerator::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) @@ -15,6 +66,23 @@ def self.new(&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) @@ -22,26 +90,94 @@ def build 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 @@ -49,8 +185,10 @@ def private_class_method(name, &block) private - attr_reader :methods, :class_methods - + # 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 @@ -68,6 +206,10 @@ def define_instance_methods(klass) 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 @@ -82,7 +224,16 @@ def define_class_methods(klass) end end - # lib/code_generator/generator.rb + # 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 [CodeGenerator::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) @@ -102,6 +253,13 @@ def define_method_with_params(target_class, method_config, define_method_name) 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 [CodeGenerator::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) @@ -110,6 +268,20 @@ def calculate_return_value(method_config) 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" diff --git a/lib/code_generator/method_config.rb b/lib/code_generator/method_config.rb index 4c8f86b..e1d19a6 100644 --- a/lib/code_generator/method_config.rb +++ b/lib/code_generator/method_config.rb @@ -1,11 +1,56 @@ # frozen_string_literal: true module CodeGenerator + # 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 - attr_reader :name, :visibility, :parameters, :return_value, :generate_random + # @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) @@ -19,38 +64,122 @@ def initialize(name, visibility) 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 - attr_writer :parameters, :return_value, :generate_random + # @!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 diff --git a/lib/code_generator/parameter.rb b/lib/code_generator/parameter.rb index c2e49c8..e07e5a8 100644 --- a/lib/code_generator/parameter.rb +++ b/lib/code_generator/parameter.rb @@ -1,12 +1,52 @@ -# lib/code_generator/parameter.rb # frozen_string_literal: true module CodeGenerator + # 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 - attr_reader :type, :name, :default + # @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) @@ -16,6 +56,26 @@ def initialize(type, name, default: nil) @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 @@ -37,6 +97,15 @@ def to_ruby_param 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 diff --git a/rakelib/docs.rake b/rakelib/docs.rake new file mode 100644 index 0000000..3609912 --- /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".freeze +DOCS_REPO_PATH = "../#{DOCS_REPO_NAME}".freeze + +namespace :docs do # rubocop:disable Metrics/BlockLength + 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/generator.rbs b/sig/code_generator/generator.rbs deleted file mode 100644 index 9aa1da2..0000000 --- a/sig/code_generator/generator.rbs +++ /dev/null @@ -1,50 +0,0 @@ -module CodeGenerator - class Generator - type req = [Symbol, Symbol] - type opt = [Symbol, Symbol, untyped] - type keyreq = [Symbol, Symbol] - type key = [Symbol, Symbol, untyped] - type block = [Symbol, Symbol] - type methods_options = { - args: [req | opt | keyreq | key | block], - should_return: Integer, - generate: boolish - } - type primitives = Integer | Symbol | String - type paramType = primitives | [primitives, methods_options] - - PARAMS_TYPES: [Symbol] - - def initialize: ( - ?public_methods: paramType?, - ?public_class_methods: paramType?, - ?private_methods: paramType?, - ?private_class_methods: paramType?, - ?should_return: primitives | nil, - ?generate: boolish - ) -> untyped - - @public_methods: paramType? - @public_class_methods: paramType? - @private_methods: paramType? - @private_class_methods: paramType? - @should_return: untyped - @generate: boolish - - def generate_code: -> untyped - - private - - def generate_public_methods: -> untyped - - def generate_public_class_methods: -> untyped - - def any_generation_rules: -> boolish - - def both_generation_rules: -> boolish - - def operate_on_value: -> untyped - - def generate_random_object: -> untyped - end -end diff --git a/sig/lib/code_generator/generator.rbs b/sig/lib/code_generator/generator.rbs new file mode 100644 index 0000000..9ba66d5 --- /dev/null +++ b/sig/lib/code_generator/generator.rbs @@ -0,0 +1,40 @@ +module CodeGenerator + class Generator + @methods: untyped + + @class_methods: untyped + + def initialize: () -> void + + def self.new: () ?{ (?) -> untyped } -> untyped + + def build: () -> untyped + + def public_method: (untyped name) { (?) -> untyped } -> untyped + + def private_method: (untyped name) { (?) -> untyped } -> untyped + + def protected_method: (untyped name) { (?) -> untyped } -> untyped + + def public_class_method: (untyped name) { (?) -> untyped } -> untyped + + def private_class_method: (untyped name) { (?) -> untyped } -> untyped + + private + + attr_reader methods: untyped + + attr_reader class_methods: untyped + + def define_instance_methods: (untyped klass) -> (nil | untyped) + + def define_class_methods: (untyped klass) -> (nil | untyped) + + # lib/code_generator/generator.rb + def define_method_with_params: (untyped target_class, untyped method_config, untyped define_method_name) -> untyped + + def calculate_return_value: (untyped method_config) -> untyped + + def generate_random_object: (untyped klass) -> untyped + end +end From 1ce062ac5834d68a9ebbb9b591d57aa9eb6c2fb8 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 15:29:56 +0300 Subject: [PATCH 34/39] RuboCop --- Gemfile | 14 ++--- Rakefile | 6 +- bin/console | 6 +- code_generator.gemspec | 30 +++++----- lib/code_generator.rb | 10 ++-- lib/code_generator/generator.rb | 8 +-- lib/code_generator/method_config.rb | 4 +- lib/code_generator/parameter.rb | 2 +- lib/code_generator/version.rb | 2 +- rakelib/docs.rake | 4 +- spec/code_generator/generator_spec.rb | 84 +++++++++++++-------------- spec/code_generator_spec.rb | 2 +- spec/spec_helper.rb | 4 +- 13 files changed, 88 insertions(+), 88 deletions(-) diff --git a/Gemfile b/Gemfile index 1015c3d..ad82082 100644 --- a/Gemfile +++ b/Gemfile @@ -1,13 +1,13 @@ # 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" -gem "rubocop-rspec", "~> 2.24" -gem "rubocop-sorted_methods_by_call", "~> 1.0", ">= 1.0.1" -gem "yard" +gem 'rake', '~> 13.0' +gem 'rspec', '~> 3.0' +gem 'rubocop' +gem 'rubocop-rspec', '~> 2.24' +gem 'rubocop-sorted_methods_by_call', '~> 1.0', '>= 1.0.1' +gem 'yard' 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..d68f5b4 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 '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/code_generator.gemspec b/code_generator.gemspec index 63e62b5..fca5014 100644 --- a/code_generator.gemspec +++ b/code_generator.gemspec @@ -1,24 +1,24 @@ # frozen_string_literal: true -require_relative "lib/code_generator/version" +require_relative 'lib/code_generator/version' Gem::Specification.new do |spec| - spec.name = "code_generator" + spec.name = 'code_generator' spec.version = CodeGenerator::VERSION - spec.authors = ["unurgunite"] - spec.email = ["senpaiguru1488@gmail.com"] + 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 = '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.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["rubygems_mfa_required"] = "true" + 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['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. @@ -28,9 +28,9 @@ Gem::Specification.new do |spec| f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile]) end end - spec.bindir = "exe" + spec.bindir = 'exe' spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] + spec.require_paths = ['lib'] # Uncomment to register a new dependency of your gem # spec.add_dependency "example-gem", "~> 1.0" diff --git a/lib/code_generator.rb b/lib/code_generator.rb index 50075bb..4117adc 100644 --- a/lib/code_generator.rb +++ b/lib/code_generator.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require "securerandom" -require_relative "code_generator/version" -require_relative "code_generator/parameter" -require_relative "code_generator/method_config" -require_relative "code_generator/generator" +require 'securerandom' +require_relative 'code_generator/version' +require_relative 'code_generator/parameter' +require_relative 'code_generator/method_config' +require_relative 'code_generator/generator' module CodeGenerator class Error < StandardError; end diff --git a/lib/code_generator/generator.rb b/lib/code_generator/generator.rb index 66d2fa5..18d0d42 100644 --- a/lib/code_generator/generator.rb +++ b/lib/code_generator/generator.rb @@ -237,7 +237,7 @@ def define_class_methods(klass) 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, &block| + target_class.send(define_method_name, method_config.name) do |*_args, **_kwargs| return_value end else @@ -284,11 +284,11 @@ def calculate_return_value(method_config) # generate_random_object(Object) # => Object def generate_random_object(klass) case klass.name - when "Integer" + when 'Integer' rand(1..1_000_000) - when "String" + when 'String' SecureRandom.alphanumeric(10) - when "Symbol" + when 'Symbol' SecureRandom.alphanumeric(10).to_sym else klass diff --git a/lib/code_generator/method_config.rb b/lib/code_generator/method_config.rb index e1d19a6..32c2487 100644 --- a/lib/code_generator/method_config.rb +++ b/lib/code_generator/method_config.rb @@ -52,7 +52,7 @@ class MethodConfig # 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, '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 @@ -181,7 +181,7 @@ def generate(value: true) # @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) + raise ArgumentError, 'Parameter name must be a Symbol' unless name.is_a?(Symbol) end end end diff --git a/lib/code_generator/parameter.rb b/lib/code_generator/parameter.rb index e07e5a8..0ffa4a0 100644 --- a/lib/code_generator/parameter.rb +++ b/lib/code_generator/parameter.rb @@ -49,7 +49,7 @@ class 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) + raise ArgumentError, 'Parameter name must be a Symbol' unless name.is_a?(Symbol) @type = type @name = name diff --git a/lib/code_generator/version.rb b/lib/code_generator/version.rb index b0fdf22..f57fd11 100644 --- a/lib/code_generator/version.rb +++ b/lib/code_generator/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module CodeGenerator - VERSION = "1.0.0" + VERSION = '1.0.0' end diff --git a/rakelib/docs.rake b/rakelib/docs.rake index 3609912..9b5a43e 100644 --- a/rakelib/docs.rake +++ b/rakelib/docs.rake @@ -5,8 +5,8 @@ require 'yard' require 'fileutils' GEM_NAME = Bundler.load_gemspec(Dir.glob('*.gemspec').first).name -DOCS_REPO_NAME = "#{GEM_NAME}_docs".freeze -DOCS_REPO_PATH = "../#{DOCS_REPO_NAME}".freeze +DOCS_REPO_NAME = "#{GEM_NAME}_docs" +DOCS_REPO_PATH = "../#{DOCS_REPO_NAME}" namespace :docs do # rubocop:disable Metrics/BlockLength desc 'Generate new docs and push them to repo' diff --git a/spec/code_generator/generator_spec.rb b/spec/code_generator/generator_spec.rb index 11de57e..b5d8444 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/code_generator/generator_spec.rb @@ -2,24 +2,24 @@ # frozen_string_literal: true RSpec.describe CodeGenerator::Generator do - describe "public methods" do - it "creates a simple public method" do + describe 'public methods' do + it 'creates a simple public method' do generator = CodeGenerator::Generator.new do |g| g.public_method :hello do |m| - m.returns "world" + m.returns 'world' end end klass = generator.build obj = klass.new - expect(obj.hello).to eq "world" + expect(obj.hello).to eq 'world' end - it "creates public method with required parameters" do + it 'creates public method with required parameters' do generator = CodeGenerator::Generator.new do |g| g.public_method :greet do |m| m.required :name - m.returns "Hello" + m.returns 'Hello' end end @@ -27,33 +27,33 @@ obj = klass.new # Should accept the parameter - expect(obj.greet("Alice")).to eq "Hello" + 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 + it 'creates public method with optional parameters' do generator = CodeGenerator::Generator.new do |g| g.public_method :greet do |m| m.required :name - m.optional :greeting, default: "Hello" - m.returns "done" + 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" + expect(obj.greet('Alice')).to eq 'done' + expect(obj.greet('Alice', 'Hi')).to eq 'done' end - it "creates public method with keyword parameters" do + it 'creates public method with keyword parameters' do generator = CodeGenerator::Generator.new do |g| g.public_method :process do |m| m.required :id - m.keyword :format, default: "json" + m.keyword :format, default: 'json' m.keyword_required :action m.returns true end @@ -63,15 +63,15 @@ obj = klass.new expect(obj.process(1, action: :create)).to eq true - expect(obj.process(1, format: "xml", action: :update)).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 + describe 'private methods' do + it 'creates a private method' do generator = CodeGenerator::Generator.new do |g| g.private_method :secret do |m| m.returns 42 @@ -85,7 +85,7 @@ expect { obj.secret }.to raise_error(NoMethodError) end - it "creates private method with parameters" do + it 'creates private method with parameters' do generator = CodeGenerator::Generator.new do |g| g.private_method :calculate do |m| m.required :x @@ -102,11 +102,11 @@ end end - describe "protected methods" do - it "creates a protected method" do + describe 'protected methods' do + it 'creates a protected method' do generator = CodeGenerator::Generator.new do |g| g.protected_method :internal do |m| - m.returns "protected" + m.returns 'protected' end end @@ -120,38 +120,38 @@ def access_protected end obj = subclass.new - expect(obj.access_protected).to eq "protected" + expect(obj.access_protected).to eq 'protected' end end - describe "class methods" do - it "creates public class method" do + describe 'class methods' do + it 'creates public class method' do generator = CodeGenerator::Generator.new do |g| g.public_class_method :helper do |m| - m.returns "class helper" + m.returns 'class helper' end end klass = generator.build - expect(klass.helper).to eq "class helper" + expect(klass.helper).to eq 'class helper' end - it "creates private class method" do + it 'creates private class method' do generator = CodeGenerator::Generator.new do |g| g.private_class_method :internal_class_method do |m| - m.returns "private class" + m.returns 'private class' end end klass = generator.build - expect(klass.send(:internal_class_method)).to eq "private class" + 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 + describe 'random generation' do + it 'generates random integers' do generator = CodeGenerator::Generator.new do |g| g.public_method :random_int do |m| m.returns Integer @@ -167,7 +167,7 @@ def access_protected expect(result).to be > 0 end - it "generates random strings" do + it 'generates random strings' do generator = CodeGenerator::Generator.new do |g| g.public_method :random_string do |m| m.returns String @@ -183,7 +183,7 @@ def access_protected expect(result.length).to eq 10 end - it "generates random symbols" do + it 'generates random symbols' do generator = CodeGenerator::Generator.new do |g| g.public_method :random_symbol do |m| m.returns Symbol @@ -199,29 +199,29 @@ def access_protected end end - describe "parameter validation" do - it "raises error for invalid parameter name" do + describe 'parameter validation' do + it 'raises error for invalid parameter name' do expect do CodeGenerator::Generator.new do |g| g.public_method :test do |m| - m.required "string_name" # Should be symbol + m.required 'string_name' # Should be symbol end end - end.to raise_error(ArgumentError, "Parameter name must be a Symbol") + end.to raise_error(ArgumentError, 'Parameter name must be a Symbol') end - it "raises error for invalid parameter type" do + it 'raises error for invalid parameter type' do expect do CodeGenerator::Parameter.new(:invalid_type, :name) - end.to raise_error(ArgumentError, "Invalid parameter type: invalid_type") + end.to raise_error(ArgumentError, 'Invalid parameter type: invalid_type') end end - describe "visibility validation" do - it "raises error for invalid visibility" do + describe 'visibility validation' do + it 'raises error for invalid visibility' do expect do CodeGenerator::MethodConfig.new(:test, :invalid_visibility) - end.to raise_error(ArgumentError, "Invalid visibility: invalid_visibility") + end.to raise_error(ArgumentError, 'Invalid visibility: invalid_visibility') end end end diff --git a/spec/code_generator_spec.rb b/spec/code_generator_spec.rb index 60c6609..18dfa14 100644 --- a/spec/code_generator_spec.rb +++ b/spec/code_generator_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe CodeGenerator do - it "has a version number" do + it 'has a version number' do expect(CodeGenerator::VERSION).not_to be nil end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2a86a22..e2a1d71 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require "code_generator" +require '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! From cb43374206a180c6f2a355fce6d22fca278f37bf Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 18:32:00 +0300 Subject: [PATCH 35/39] Updated `bin/release` --- bin/release | 105 +--------------------------------------------------- 1 file changed, 2 insertions(+), 103 deletions(-) diff --git a/bin/release b/bin/release index c9b3e2e..2e1da4b 100755 --- a/bin/release +++ b/bin/release @@ -1,106 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' - -log_error() { echo -e "${RED}ERROR: $1${NC}" >&2; } -log_warn() { echo -e "${YELLOW}WARNING: $1${NC}" >&2; } -log_success() { echo -e "${GREEN}$1${NC}"; } - -check_clean_working_directory() { - if ! git diff-index --quiet HEAD --; then - log_error "Working directory is not clean. Please commit or stash your changes." - exit 1 - fi -} - -check_main_branch() { - local current_branch - current_branch=$(git rev-parse --abbrev-ref HEAD) - if [[ "$current_branch" != "main" && "$current_branch" != "master" ]]; then - log_error "Must be on 'main' or 'master' branch to release. Current branch: $current_branch" - exit 1 - fi -} - -detect_gemspec() { - GEMSPEC=$(ls *.gemspec 2>/dev/null | head -n 1) - if [[ -z "${GEMSPEC}" ]]; then - log_error "No .gemspec file found in this directory." - exit 1 - fi - - GEM_NAME=$(ruby -r rubygems -e "spec = Gem::Specification.load('./${GEMSPEC}'); puts spec.name") - VERSION=$(ruby -r rubygems -e "spec = Gem::Specification.load('./${GEMSPEC}'); puts spec.version") -} - -detect_version() { - if git rev-parse "v${VERSION}" >/dev/null 2>&1; then - log_error "Git tag v${VERSION} already exists. Please update the version in your gemspec or version file first." - exit 1 - fi - - echo "Preparing to release version ${VERSION} for ${GEM_NAME}" -} -confirm_release() { - echo "" - log_warn "This will:" - echo " • Build gem ${GEM_NAME} version ${VERSION}" - echo " • Create git tag v${VERSION}" - echo " • Push tag to origin" - echo " • Publish to RubyGems" - echo "" - read -p "Are you sure you want to proceed? (y/N): " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - log_warn "Release cancelled." - exit 1 - fi -} - -build_gem() { - log_success "Building gem..." - gem build "${GEMSPEC}" - - if [[ ! -f "${GEM_NAME}-${VERSION}.gem" ]]; then - log_error "Gem build failed or gem file not found: ${GEM_NAME}-${VERSION}.gem" - exit 1 - fi -} - -create_and_push_tag() { - log_success "Creating and pushing git tag..." - git tag "v${VERSION}" -m "Release v${VERSION}" - git push origin "v${VERSION}" -} - -publish_to_rubygems() { - log_success "Publishing to RubyGems..." - gem push "${GEM_NAME}-${VERSION}.gem" -} - -main() { - log_success "Starting release process..." - - check_clean_working_directory - check_main_branch - detect_gemspec - detect_version - confirm_release - - build_gem - create_and_push_tag - publish_to_rubygems - - log_success "✅ Release v${VERSION} completed successfully!" - echo "" - echo "Next steps:" - echo " • Create a GitHub release: https://github.com/$(git remote get-url origin | sed -E 's|.*github.com[:/](.*/.*)\.git|\1|; s|.*github.com[:/](.*)|\1|')/releases/new?tag=v${VERSION}" - echo " • Update CHANGELOG.md for the next release" -} +set -euo pipefail -main "$@" +bash <(curl -sSL https://raw.githubusercontent.com/unurgunite/release_gem/refs/heads/master/release) From df13ba95fe2569067440b54dea05ba805d4baf6e Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 20:41:35 +0300 Subject: [PATCH 36/39] Drop Ruby 2.6 support --- .github/workflows/ci.yml | 2 +- .rubocop.yml | 2 +- Gemfile | 8 +++--- Gemfile.lock | 4 +-- README.md | 2 +- code_generator.gemspec | 2 +- sig/code_generator.rbs | 4 --- sig/lib/code_generator/generator.rbs | 40 ---------------------------- sig/validator.rbs | 9 ------- 9 files changed, 10 insertions(+), 63 deletions(-) delete mode 100644 sig/code_generator.rbs delete mode 100644 sig/lib/code_generator/generator.rbs delete mode 100644 sig/validator.rbs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e91f0de..0ac3372 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4] + ruby: [2.7, 3.0, 3.1, 3.2, 3.3, 3.4] steps: - uses: actions/checkout@v4 diff --git a/.rubocop.yml b/.rubocop.yml index cc2061d..674eafe 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,7 +3,7 @@ plugins: AllCops: NewCops: enable - TargetRubyVersion: 2.6 + TargetRubyVersion: 2.7 Layout/LineLength: Max: 120 diff --git a/Gemfile b/Gemfile index ad82082..e5b85b0 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gemspec gem 'rake', '~> 13.0' gem 'rspec', '~> 3.0' -gem 'rubocop' -gem 'rubocop-rspec', '~> 2.24' -gem 'rubocop-sorted_methods_by_call', '~> 1.0', '>= 1.0.1' -gem 'yard' +gem 'rubocop', require: false +gem 'rubocop-rspec', '~> 2.24', require: false +gem 'rubocop-sorted_methods_by_call', require: false +gem 'yard', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 35243b7..424f0d4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -60,7 +60,7 @@ GEM rubocop-rspec_rails (~> 2.28) rubocop-rspec_rails (2.29.1) rubocop (~> 1.61) - rubocop-sorted_methods_by_call (1.0.1) + rubocop-sorted_methods_by_call (1.1.1) lint_roller rubocop (>= 1.72.0) ruby-progressbar (1.13.0) @@ -79,7 +79,7 @@ DEPENDENCIES rspec (~> 3.0) rubocop rubocop-rspec (~> 2.24) - rubocop-sorted_methods_by_call (~> 1.0, >= 1.0.1) + rubocop-sorted_methods_by_call yard BUNDLED WITH diff --git a/README.md b/README.md index 26013ba..b183d39 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ This will install dependencies and start an interactive console. ## Requirements -- **Ruby**: >= 2.6.0 +- **Ruby**: >= 2.7.0 - **No external dependencies** ## Contributing diff --git a/code_generator.gemspec b/code_generator.gemspec index fca5014..517289e 100644 --- a/code_generator.gemspec +++ b/code_generator.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |spec| 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.required_ruby_version = '>= 2.7.0' spec.metadata['allowed_push_host'] = 'https://rubygems.org' 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/sig/lib/code_generator/generator.rbs b/sig/lib/code_generator/generator.rbs deleted file mode 100644 index 9ba66d5..0000000 --- a/sig/lib/code_generator/generator.rbs +++ /dev/null @@ -1,40 +0,0 @@ -module CodeGenerator - class Generator - @methods: untyped - - @class_methods: untyped - - def initialize: () -> void - - def self.new: () ?{ (?) -> untyped } -> untyped - - def build: () -> untyped - - def public_method: (untyped name) { (?) -> untyped } -> untyped - - def private_method: (untyped name) { (?) -> untyped } -> untyped - - def protected_method: (untyped name) { (?) -> untyped } -> untyped - - def public_class_method: (untyped name) { (?) -> untyped } -> untyped - - def private_class_method: (untyped name) { (?) -> untyped } -> untyped - - private - - attr_reader methods: untyped - - attr_reader class_methods: untyped - - def define_instance_methods: (untyped klass) -> (nil | untyped) - - def define_class_methods: (untyped klass) -> (nil | untyped) - - # lib/code_generator/generator.rb - def define_method_with_params: (untyped target_class, untyped method_config, untyped define_method_name) -> untyped - - def calculate_return_value: (untyped method_config) -> untyped - - def generate_random_object: (untyped klass) -> untyped - end -end diff --git a/sig/validator.rbs b/sig/validator.rbs deleted file mode 100644 index 595a16c..0000000 --- a/sig/validator.rbs +++ /dev/null @@ -1,9 +0,0 @@ -class Validator - def validate!: -> untyped - - private - - def arguments_table: -> untyped - - def validate: -> untyped -end From 4384d3941a5abaa5911c6384ea837a533ae0d2dd Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 20:45:21 +0300 Subject: [PATCH 37/39] Updated gems --- Gemfile | 8 -------- code_generator.gemspec | 15 ++++++++++----- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Gemfile b/Gemfile index e5b85b0..7f4f5e9 100644 --- a/Gemfile +++ b/Gemfile @@ -2,12 +2,4 @@ source 'https://rubygems.org' -# Specify your gem's dependencies in code_generator.gemspec gemspec - -gem 'rake', '~> 13.0' -gem 'rspec', '~> 3.0' -gem 'rubocop', require: false -gem 'rubocop-rspec', '~> 2.24', require: false -gem 'rubocop-sorted_methods_by_call', require: false -gem 'yard', require: false diff --git a/code_generator.gemspec b/code_generator.gemspec index 517289e..28c3ff7 100644 --- a/code_generator.gemspec +++ b/code_generator.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |spec| 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.description = 'This spec.add_development_dependencygives 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.7.0' @@ -20,8 +20,8 @@ Gem::Specification.new do |spec| spec.metadata['changelog_uri'] = 'https://github.com/unurgunite/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. + # Specify which files should be added to the spec.add_development_dependencywhen it is released. + # The `git ls-files -z` loads the files in the Rubyspec.add_development_dependencythat have been added into git. spec.files = Dir.chdir(__dir__) do `git ls-files -z`.split("\x0").reject do |f| (File.expand_path(f) == __FILE__) || @@ -32,8 +32,13 @@ Gem::Specification.new do |spec| 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.add_development_dependency 'rake', '~> 13.0' + spec.add_development_dependency'rake', '~> 13.0' + spec.add_development_dependency'rspec', '~> 3.0' + spec.add_development_dependency'rubocop', require: false + spec.add_development_dependency'rubocop-rspec', '~> 2.24', require: false + spec.add_development_dependency'rubocop-sorted_methods_by_call', require: false + spec.add_development_dependency'yard', require: false # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html From be2e133332c90001d86e3de3f1f3b82be413af24 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 20:46:09 +0300 Subject: [PATCH 38/39] Updated CI/CD --- .github/workflows/ci.yml | 1 - .rubocop.yml | 5 +++++ .rubocop_todo.yml | 23 +++++++++++++++++++++++ code_generator.gemspec | 21 ++++++++++----------- rakelib/docs.rake | 2 +- 5 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 .rubocop_todo.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ac3372..97bd577 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,6 @@ jobs: - name: RuboCop (self‑check) run: | bundle exec rubocop - bundle exec rubocop --config .rubocop.test.yml lib/ -A - name: RSpec run: bundle exec rspec diff --git a/.rubocop.yml b/.rubocop.yml index 674eafe..62e39f2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,5 @@ +inherit_from: .rubocop_todo.yml + plugins: - rubocop-sorted_methods_by_call @@ -8,5 +10,8 @@ AllCops: 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/code_generator.gemspec b/code_generator.gemspec index 28c3ff7..18b9e51 100644 --- a/code_generator.gemspec +++ b/code_generator.gemspec @@ -9,7 +9,9 @@ Gem::Specification.new do |spec| spec.email = ['senpaiguru1488@gmail.com'] spec.summary = 'Code generation tool based on preferences.' - spec.description = 'This spec.add_development_dependencygives 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.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/code_generator.' spec.required_ruby_version = '>= 2.7.0' @@ -20,25 +22,22 @@ Gem::Specification.new do |spec| spec.metadata['changelog_uri'] = 'https://github.com/unurgunite/code_generator/CHANGELOG.md' spec.metadata['rubygems_mfa_required'] = 'true' - # Specify which files should be added to the spec.add_development_dependencywhen it is released. - # The `git ls-files -z` loads the files in the Rubyspec.add_development_dependencythat have been added into git. + # 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. spec.files = Dir.chdir(__dir__) do `git ls-files -z`.split("\x0").reject do |f| (File.expand_path(f) == __FILE__) || 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'] spec.add_development_dependency 'rake', '~> 13.0' - spec.add_development_dependency'rake', '~> 13.0' - spec.add_development_dependency'rspec', '~> 3.0' - spec.add_development_dependency'rubocop', require: false - spec.add_development_dependency'rubocop-rspec', '~> 2.24', require: false - spec.add_development_dependency'rubocop-sorted_methods_by_call', require: false - spec.add_development_dependency'yard', require: false + 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/rakelib/docs.rake b/rakelib/docs.rake index 9b5a43e..466ae2a 100644 --- a/rakelib/docs.rake +++ b/rakelib/docs.rake @@ -8,7 +8,7 @@ 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 # rubocop:disable Metrics/BlockLength +namespace :docs do desc 'Generate new docs and push them to repo' task generate: :clean do puts 'Generating docs...' From 730bc4720d7fc9985bce16310d8252435ec7e191 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Thu, 6 Nov 2025 21:00:00 +0300 Subject: [PATCH 39/39] Renamed gem from `CodeGenerator` to `AdvancedCodeGenerator` --- Gemfile.lock | 4 +-- README.md | 18 +++++----- bin/console | 2 +- bin/setup | 2 +- code_generator.gemspec | 12 +++---- lib/advanced_code_generator.rb | 11 +++++++ .../generator.rb | 12 +++---- .../method_config.rb | 6 ++-- .../parameter.rb | 2 +- .../version.rb | 2 +- lib/code_generator.rb | 11 ------- .../generator_spec.rb | 33 +++++++++---------- spec/advanced_code_generator_spec.rb | 7 ++++ spec/code_generator_spec.rb | 7 ---- spec/spec_helper.rb | 2 +- 15 files changed, 65 insertions(+), 66 deletions(-) create mode 100644 lib/advanced_code_generator.rb rename lib/{code_generator => advanced_code_generator}/generator.rb (95%) rename lib/{code_generator => advanced_code_generator}/method_config.rb (97%) rename lib/{code_generator => advanced_code_generator}/parameter.rb (99%) rename lib/{code_generator => advanced_code_generator}/version.rb (65%) delete mode 100644 lib/code_generator.rb rename spec/{code_generator => advanced_code_generator}/generator_spec.rb (83%) create mode 100644 spec/advanced_code_generator_spec.rb delete mode 100644 spec/code_generator_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index 424f0d4..788b19c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - code_generator (1.0.0) + advanced_code_generator (1.0.0) GEM remote: https://rubygems.org/ @@ -74,7 +74,7 @@ PLATFORMS ruby DEPENDENCIES - code_generator! + advanced_code_generator! rake (~> 13.0) rspec (~> 3.0) rubocop diff --git a/README.md b/README.md index b183d39..b9c0d03 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Here's a **modern, professional README.md** that reflects your DSL-based code generator: -# CodeGenerator +# 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) @@ -8,7 +8,7 @@ Here's a **modern, professional README.md** that reflects your DSL-based code ge **A fluent DSL for generating Ruby classes with stubbed methods for testing and prototyping.** -* [CodeGenerator](#codegenerator) +* [AdvancedCodeGenerator (ACG)](#advancedcodegenerator-acg) * [Features](#features) * [Installation](#installation) * [Usage Examples](#usage-examples) @@ -40,7 +40,7 @@ Here's a **modern, professional README.md** that reflects your DSL-based code ge Add this line to your application's Gemfile: ```ruby -gem 'code_generator' +gem 'advanced_code_generator' ``` And then execute: @@ -52,7 +52,7 @@ bundle install Or install it yourself: ```bash -gem install code_generator +gem install advanced_code_generator ``` ## Usage Examples @@ -60,7 +60,7 @@ gem install code_generator ### Basic Public Method ```ruby -generator = CodeGenerator::Generator.new do |g| +generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :hello do |m| m.returns "world" end @@ -74,7 +74,7 @@ obj.hello # => "world" ### Method with Parameters ```ruby -generator = CodeGenerator::Generator.new do |g| +generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :greet do |m| m.required :name m.optional :greeting, default: "Hello" @@ -92,7 +92,7 @@ obj.greet("Bob", "Hi", format: :xml) # => true ### Private and Protected Methods ```ruby -generator = CodeGenerator::Generator.new do |g| +generator = AdvancedCodeGenerator::Generator.new do |g| g.private_method :secret_calculation do |m| m.returns 42 end @@ -119,7 +119,7 @@ Subclass.new.access_protected # => "protected result" ### Class Methods ```ruby -generator = CodeGenerator::Generator.new do |g| +generator = AdvancedCodeGenerator::Generator.new do |g| g.public_class_method :factory do |m| m.returns "class helper" end @@ -137,7 +137,7 @@ Klass.send(:setup) # => "private setup" ### Random Value Generation ```ruby -generator = CodeGenerator::Generator.new do |g| +generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :random_int do |m| m.returns Integer m.generate true diff --git a/bin/console b/bin/console index d68f5b4..48a26e2 100755 --- a/bin/console +++ b/bin/console @@ -2,7 +2,7 @@ # frozen_string_literal: true require 'bundler/setup' -require 'code_generator' +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. diff --git a/bin/setup b/bin/setup index 88a0436..4482f7b 100755 --- a/bin/setup +++ b/bin/setup @@ -3,7 +3,7 @@ set -euo pipefail IFS=$'\n\t' set -vx -PROJECT_NAME='code_generator' +PROJECT_NAME='advanced_code_generator' if pwd | grep $PROJECT_NAME; then cd "${PWD%$PROJECT_NAME*}$PROJECT_NAME" else diff --git a/code_generator.gemspec b/code_generator.gemspec index 18b9e51..cda8a60 100644 --- a/code_generator.gemspec +++ b/code_generator.gemspec @@ -1,10 +1,10 @@ # 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.name = 'advanced_code_generator' + spec.version = AdvancedCodeGenerator::VERSION spec.authors = ['unurgunite'] spec.email = ['senpaiguru1488@gmail.com'] @@ -12,14 +12,14 @@ Gem::Specification.new do |spec| 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/code_generator.' + 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['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['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. 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/code_generator/generator.rb b/lib/advanced_code_generator/generator.rb similarity index 95% rename from lib/code_generator/generator.rb rename to lib/advanced_code_generator/generator.rb index 18d0d42..8b10f05 100644 --- a/lib/code_generator/generator.rb +++ b/lib/advanced_code_generator/generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module CodeGenerator +module AdvancedCodeGenerator # Generates Ruby classes with stubbed methods using a fluent DSL. # # This class provides a domain-specific language (DSL) for defining @@ -27,10 +27,10 @@ module CodeGenerator # end # end class Generator - # @return [Array] List of instance method configurations + # @return [Array] List of instance method configurations attr_reader :methods - # @return [Array] List of class method configurations + # @return [Array] List of class method configurations attr_reader :class_methods # Initializes a new Generator instance with empty method collections. @@ -51,7 +51,7 @@ def initialize # # @yield [generator] The generator instance for DSL configuration # @yieldparam generator [CodeGenerator::Generator] The current generator instance - # @return [CodeGenerator::Generator] A configured generator instance + # @return [AdvancedCodeGenerator::Generator] A configured generator instance # # @example # generator = CodeGenerator::Generator.new do |g| @@ -231,7 +231,7 @@ def define_class_methods(klass) # 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 [CodeGenerator::MethodConfig] The method configuration + # @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) @@ -258,7 +258,7 @@ def define_method_with_params(target_class, method_config, define_method_name) # Handles both static return values and random object generation # based on the method configuration settings. # - # @param method_config [CodeGenerator::MethodConfig] The method configuration + # @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) diff --git a/lib/code_generator/method_config.rb b/lib/advanced_code_generator/method_config.rb similarity index 97% rename from lib/code_generator/method_config.rb rename to lib/advanced_code_generator/method_config.rb index 32c2487..b52f758 100644 --- a/lib/code_generator/method_config.rb +++ b/lib/advanced_code_generator/method_config.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module CodeGenerator +module AdvancedCodeGenerator # Represents the configuration for a single method definition. # # This class encapsulates all the metadata needed to define a method, @@ -24,7 +24,7 @@ class MethodConfig # @return [Symbol] The visibility level (:public, :private, :protected, :public_class, :private_class) attr_reader :visibility - # @return [Array] List of method parameters + # @return [Array] List of method parameters attr_reader :parameters # @return [Object, nil] The value that the method should return @@ -164,7 +164,7 @@ def generate(value: true) private # @!attribute [rw] parameters - # @return [Array] + # @return [Array] attr_writer :parameters # @!attribute [rw] return_value diff --git a/lib/code_generator/parameter.rb b/lib/advanced_code_generator/parameter.rb similarity index 99% rename from lib/code_generator/parameter.rb rename to lib/advanced_code_generator/parameter.rb index 0ffa4a0..67cb489 100644 --- a/lib/code_generator/parameter.rb +++ b/lib/advanced_code_generator/parameter.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module CodeGenerator +module AdvancedCodeGenerator # Represents a single method parameter configuration. # # This class encapsulates the metadata for a method parameter, including diff --git a/lib/code_generator/version.rb b/lib/advanced_code_generator/version.rb similarity index 65% rename from lib/code_generator/version.rb rename to lib/advanced_code_generator/version.rb index f57fd11..6ec0105 100644 --- a/lib/code_generator/version.rb +++ b/lib/advanced_code_generator/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -module CodeGenerator +module AdvancedCodeGenerator VERSION = '1.0.0' end diff --git a/lib/code_generator.rb b/lib/code_generator.rb deleted file mode 100644 index 4117adc..0000000 --- a/lib/code_generator.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -require 'securerandom' -require_relative 'code_generator/version' -require_relative 'code_generator/parameter' -require_relative 'code_generator/method_config' -require_relative 'code_generator/generator' - -module CodeGenerator - class Error < StandardError; end -end diff --git a/spec/code_generator/generator_spec.rb b/spec/advanced_code_generator/generator_spec.rb similarity index 83% rename from spec/code_generator/generator_spec.rb rename to spec/advanced_code_generator/generator_spec.rb index b5d8444..aeaed7b 100644 --- a/spec/code_generator/generator_spec.rb +++ b/spec/advanced_code_generator/generator_spec.rb @@ -1,10 +1,9 @@ -# spec/code_generator/generator_spec.rb # frozen_string_literal: true -RSpec.describe CodeGenerator::Generator do +RSpec.describe AdvancedCodeGenerator::Generator do describe 'public methods' do it 'creates a simple public method' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :hello do |m| m.returns 'world' end @@ -16,7 +15,7 @@ end it 'creates public method with required parameters' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :greet do |m| m.required :name m.returns 'Hello' @@ -34,7 +33,7 @@ end it 'creates public method with optional parameters' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :greet do |m| m.required :name m.optional :greeting, default: 'Hello' @@ -50,7 +49,7 @@ end it 'creates public method with keyword parameters' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :process do |m| m.required :id m.keyword :format, default: 'json' @@ -72,7 +71,7 @@ describe 'private methods' do it 'creates a private method' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.private_method :secret do |m| m.returns 42 end @@ -86,7 +85,7 @@ end it 'creates private method with parameters' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.private_method :calculate do |m| m.required :x m.required :y @@ -104,7 +103,7 @@ describe 'protected methods' do it 'creates a protected method' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.protected_method :internal do |m| m.returns 'protected' end @@ -126,7 +125,7 @@ def access_protected describe 'class methods' do it 'creates public class method' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.public_class_method :helper do |m| m.returns 'class helper' end @@ -137,7 +136,7 @@ def access_protected end it 'creates private class method' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.private_class_method :internal_class_method do |m| m.returns 'private class' end @@ -152,7 +151,7 @@ def access_protected describe 'random generation' do it 'generates random integers' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :random_int do |m| m.returns Integer m.generate value: true @@ -168,7 +167,7 @@ def access_protected end it 'generates random strings' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :random_string do |m| m.returns String m.generate value: true @@ -184,7 +183,7 @@ def access_protected end it 'generates random symbols' do - generator = CodeGenerator::Generator.new do |g| + generator = AdvancedCodeGenerator::Generator.new do |g| g.public_method :random_symbol do |m| m.returns Symbol m.generate value: true @@ -202,7 +201,7 @@ def access_protected describe 'parameter validation' do it 'raises error for invalid parameter name' do expect do - CodeGenerator::Generator.new do |g| + AdvancedCodeGenerator::Generator.new do |g| g.public_method :test do |m| m.required 'string_name' # Should be symbol end @@ -212,7 +211,7 @@ def access_protected it 'raises error for invalid parameter type' do expect do - CodeGenerator::Parameter.new(:invalid_type, :name) + AdvancedCodeGenerator::Parameter.new(:invalid_type, :name) end.to raise_error(ArgumentError, 'Invalid parameter type: invalid_type') end end @@ -220,7 +219,7 @@ def access_protected describe 'visibility validation' do it 'raises error for invalid visibility' do expect do - CodeGenerator::MethodConfig.new(:test, :invalid_visibility) + AdvancedCodeGenerator::MethodConfig.new(:test, :invalid_visibility) end.to raise_error(ArgumentError, 'Invalid visibility: invalid_visibility') 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 18dfa14..0000000 --- a/spec/code_generator_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe CodeGenerator do - it 'has a version number' do - expect(CodeGenerator::VERSION).not_to be nil - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e2a1d71..1699d44 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'code_generator' +require 'advanced_code_generator' RSpec.configure do |config| # Enable flags like --only-failures and --next-failure