diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b18fd29 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'weekly' diff --git a/.github/workflows/push_gem.yml b/.github/workflows/push_gem.yml new file mode 100644 index 0000000..f662094 --- /dev/null +++ b/.github/workflows/push_gem.yml @@ -0,0 +1,46 @@ +name: Publish gem to rubygems.org + +on: + push: + tags: + - 'v*' + +permissions: + contents: read + +jobs: + push: + if: github.repository == 'ruby/ruby2_keywords' + runs-on: ubuntu-latest + + environment: + name: rubygems.org + url: https://rubygems.org/gems/ruby2_keywords + + permissions: + contents: write + id-token: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 + with: + egress-policy: audit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set up Ruby + uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0 + with: + bundler-cache: true + ruby-version: "ruby" + + - name: Publish to RubyGems + uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1.1.1 + + - name: Create GitHub release + run: | + tag_name="$(git describe --tags --abbrev=0)" + gh release create "${tag_name}" --verify-tag --generate-notes + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..5932c89 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,56 @@ +name: test + +on: [push, pull_request] + +jobs: + ruby-versions: + uses: ruby/actions/.github/workflows/ruby_versions.yml@master + with: + engine: cruby + min_version: 2.1 + + build: + needs: ruby-versions + name: build (${{ matrix.ruby }} / ${{ matrix.os }}) + strategy: + matrix: + ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} + os: [ ubuntu-latest, macos-latest, windows-latest ] + include: + - { os: ubuntu-latest, ruby: truffleruby-head } + - { os: macos-latest, ruby: truffleruby-head } + exclude: + - { os: macos-latest, ruby: 2.1 } + - { os: macos-latest, ruby: 2.2 } + - { os: macos-latest, ruby: 2.3 } + - { os: macos-latest, ruby: 2.4 } + - { os: macos-latest, ruby: 2.5 } + runs-on: ${{ matrix.os }} + steps: + - name: git config + run: | + git config --global core.autocrlf false + git config --global core.eol lf + git config --global advice.detachedHead 0 + - uses: actions/checkout@master + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run test + run: bundle exec rake test + - id: build + run: | + rake build + echo "pkg=${GITHUB_REPOSITORY#*/}-${RUNNING_OS%-*}" >> $GITHUB_OUTPUT + env: + RUNNING_OS: ${{matrix.os}} + if: "matrix.ruby == '3.0'" + shell: bash + - name: Upload package + uses: actions/upload-artifact@v4 + with: + path: pkg/*.gem + name: ${{steps.build.outputs.pkg}} + if: steps.build.outputs.pkg diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..230b7e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +ChangeLog +logs/ +gems.locked +pkg/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3b9d383 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright 2019-2020 Nobuyoshi Nakada, Yusuke Endoh + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index f3b56aa..42e1157 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,34 @@ ruby2_keywords def oldstyle_keywords(options = {}) end ``` +You can do the same for a method defined by `Module#define_method`: + +```ruby +define_method :delegating_method do |*args, &block| + other_method(*args, &block) +end +ruby2_keywords :delegating_method +``` + ## Contributing -Bug reports and pull requests are welcome on GitHub at https://bugs.ruby-lang.org. +Bug reports and pull requests are welcome on [GitHub] or +[Ruby Issue Tracking System]. + +## Development + +After checking out the repo, run `bundle install` to install dependencies. +Then, run `bundle exec rake test` to run the tests. + +To test on older Ruby versions, you can use docker. E.g. to test on Ruby 2.0, +use `docker-compose run ruby-2.0`. ## License -The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause). +The gem is available as open source under the terms of the +[Ruby License] or the [2-Clause BSD License]. + +[GitHub]: https://github.com/ruby/ruby2_keywords/ +[Ruby Issue Tracking System]: https://bugs.ruby-lang.org +[Ruby License]: https://www.ruby-lang.org/en/about/license.txt +[2-Clause BSD License]: https://opensource.org/licenses/BSD-2-Clause diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..caacbf2 --- /dev/null +++ b/Rakefile @@ -0,0 +1,16 @@ +require "bundler/gem_tasks" +require "rake/testtask" + +Rake::TestTask.new(:test) do |t| + t.test_files = FileList["test/**/test_*.rb"] +end + +task :default => :test + +task "bump:minor" do + raise "can't bump up minor" +end + +task "bump:major" do + raise "can't bump up major" +end diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3135f88 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,57 @@ +version: '3.0' + +services: + ruby-2.0: + image: ruby:2.0 + volumes: + - .:/app + command: + bash -c 'cd /app && bundle install && bundle exec rake test' + ruby-2.1: + image: ruby:2.1 + volumes: + - .:/app + command: + bash -c 'cd /app && bundle install && bundle exec rake test' + ruby-2.2: + image: ruby:2.2 + volumes: + - .:/app + command: + bash -c 'cd /app && bundle install && bundle exec rake test' + ruby-2.3: + image: ruby:2.3 + volumes: + - .:/app + command: + bash -c 'cd /app && bundle install && bundle exec rake test' + ruby-2.4: + image: ruby:2.4 + volumes: + - .:/app + command: + bash -c 'cd /app && bundle install && bundle exec rake test' + ruby-2.5: + image: ruby:2.5 + volumes: + - .:/app + command: + bash -c 'cd /app && bundle install && bundle exec rake test' + ruby-2.6: + image: ruby:2.6 + volumes: + - .:/app + command: + bash -c 'cd /app && bundle install && bundle exec rake test' + ruby-2.7: + image: ruby:2.7 + volumes: + - .:/app + command: + bash -c 'cd /app && bundle install && bundle exec rake test' + ruby-3.0: + image: ruby:3.0 + volumes: + - .:/app + command: + bash -c 'cd /app && bundle install && bundle exec rake test' diff --git a/gems.rb b/gems.rb new file mode 100644 index 0000000..2b2a2c4 --- /dev/null +++ b/gems.rb @@ -0,0 +1,8 @@ +source 'https://rubygems.org' + +gemspec + +group :development do + gem "test-unit", ">= 2.2" + gem "rake" +end diff --git a/lib/ruby2_keywords.rb b/lib/ruby2_keywords.rb index 8c48333..09827b5 100644 --- a/lib/ruby2_keywords.rb +++ b/lib/ruby2_keywords.rb @@ -1,14 +1,22 @@ class Module - unless respond_to?(:ruby2_keywords, true) + unless private_method_defined?(:ruby2_keywords) private + # call-seq: + # ruby2_keywords(method_name, ...) + # + # Does nothing. def ruby2_keywords(name, *) # nil end end end -main = TOPLEVEL_BINDING.receiver +main = TOPLEVEL_BINDING.eval('self') unless main.respond_to?(:ruby2_keywords, true) + # call-seq: + # ruby2_keywords(method_name, ...) + # + # Does nothing. def main.ruby2_keywords(name, *) # nil end @@ -16,8 +24,34 @@ def main.ruby2_keywords(name, *) class Proc unless method_defined?(:ruby2_keywords) + # call-seq: + # proc.ruby2_keywords -> proc + # + # Does nothing and just returns the receiver. def ruby2_keywords self end end end + +class << Hash + unless method_defined?(:ruby2_keywords_hash?) + # call-seq: + # Hash.ruby2_keywords_hash?(hash) -> false + # + # Returns false. + def ruby2_keywords_hash?(hash) + false + end + end + + unless method_defined?(:ruby2_keywords_hash) + # call-seq: + # Hash.ruby2_keywords_hash(hash) -> new_hash + # + # Duplicates a given hash and returns the new hash. + def ruby2_keywords_hash(hash) + hash.dup + end + end +end diff --git a/rakelib/changelogs.rake b/rakelib/changelogs.rake new file mode 100644 index 0000000..df72f9d --- /dev/null +++ b/rakelib/changelogs.rake @@ -0,0 +1,34 @@ +task "build" => "changelogs" + +changelog = proc do |output, ver = nil, prev = nil| + ver &&= Gem::Version.new(ver) + range = [[prev], [ver, "HEAD"]].map {|ver, branch| ver ? "v#{ver.to_s}" : branch}.compact.join("..") + IO.popen(%W[git log --format=fuller --topo-order --no-merges #{range}]) do |log| + line = log.gets + FileUtils.mkpath(File.dirname(output)) + File.open(output, "wb") do |f| + f.print "-*- coding: utf-8 -*-\n\n", line + log.each_line do |line| + line.sub!(/^(?!:)(?:Author|Commit)?(?:Date)?: /, ' \&') + line.sub!(/ +$/, '') + f.print(line) + end + end + end +end + +tags = IO.popen(%w[git tag -l v[0-9]*]).grep(/v(.*)/) {$1} +tags.sort_by! {|tag| tag.scan(/\d+/).map(&:to_i)} +tags.inject(nil) do |prev, tag| + task("logs/ChangeLog-#{tag}") {|t| changelog[t.name, tag, prev]} + tag +end + +desc "Make ChangeLog" +task "ChangeLog", [:ver, :prev] do |t, ver: nil, prev: tags.last| + changelog[t.name, ver, prev] +end + +changelogs = ["ChangeLog", *tags.map {|tag| "logs/ChangeLog-#{tag}"}] +task "changelogs" => changelogs +CLOBBER.concat(changelogs) << "logs" diff --git a/rakelib/epoch.rake b/rakelib/epoch.rake new file mode 100644 index 0000000..80f27c9 --- /dev/null +++ b/rakelib/epoch.rake @@ -0,0 +1,5 @@ +task "build" => "date_epoch" + +task "date_epoch" do + ENV["SOURCE_DATE_EPOCH"] = IO.popen(%W[git -C #{__dir__} log -1 --format=%ct], &:read).chomp +end diff --git a/rakelib/version.rake b/rakelib/version.rake new file mode 100644 index 0000000..bbb327f --- /dev/null +++ b/rakelib/version.rake @@ -0,0 +1,44 @@ +class << (helper = Bundler::GemHelper.instance) + def update_gemspec + path = gemspec.loaded_from + File.open(path, "r+b") do |f| + d = f.read + if d.sub!(/^(_VERSION\s*=\s*)".*"/) {$1 + gemspec.version.to_s.dump} + f.rewind + f.truncate(0) + f.print(d) + end + end + end + + def commit_bump + sh(%W[git commit -m bump\ up\ to\ #{gemspec.version} + #{gemspec.loaded_from}]) + end + + def version=(v) + gemspec.version = v + update_gemspec + commit_bump + end +end + +major, minor, teeny = helper.gemspec.version.segments + +task "bump:teeny" do + helper.version = Gem::Version.new("#{major}.#{minor}.#{teeny+1}") +end + +task "bump:minor" do + helper.version = Gem::Version.new("#{major}.#{minor+1}.0") +end + +task "bump:major" do + helper.version = Gem::Version.new("#{major+1}.0.0") +end + +task "bump" => "bump:teeny" + +task "tag" do + helper.__send__(:tag_version) +end diff --git a/ruby2_keywords.gemspec b/ruby2_keywords.gemspec index 623c58c..e2cd397 100644 --- a/ruby2_keywords.gemspec +++ b/ruby2_keywords.gemspec @@ -1,16 +1,23 @@ -version = IO.popen(%W[git -C #{__dir__} describe --tags --match v[0-9]*], &:read)[/\Av?(\d+(?:\.\d+)*)/, 1] +_VERSION = "0.0.5" +abort "Version must not reach 1" if _VERSION[/\d+/].to_i >= 1 Gem::Specification.new do |s| s.name = "ruby2_keywords" - s.version = version + s.version = _VERSION s.summary = "Shim library for Module#ruby2_keywords" s.homepage = "https://github.com/ruby/ruby2_keywords" - s.licenses = ["Ruby"] + s.licenses = ["Ruby", "BSD-2-Clause"] s.authors = ["Nobuyoshi Nakada"] s.require_paths = ["lib"] - s.files = [ + s.rdoc_options = ["--main", "README.md"] + s.extra_rdoc_files = [ + "LICENSE", "README.md", + "ChangeLog", + *Dir.glob("#{__dir__}/logs/ChangeLog-*[^~]").map {|path| path[(__dir__.size+1)..-1]}, + ] + s.files = [ "lib/ruby2_keywords.rb", - "ruby2_keywords.gemspec", ] + s.required_ruby_version = '>= 2.0.0' end diff --git a/test/test_keyword.rb b/test/test_keyword.rb new file mode 100644 index 0000000..13d6041 --- /dev/null +++ b/test/test_keyword.rb @@ -0,0 +1,41 @@ +require 'test/unit' +LOADING_RUBY2_KEYWORDS = (RUBY_VERSION.scan(/\d+/).map(&:to_i) <=> [2, 7]) < 0 +if LOADING_RUBY2_KEYWORDS + require 'ruby2_keywords' +end + +class TestKeywordArguments < Test::Unit::TestCase + def test_loaded_features + list = $LOADED_FEATURES.grep(%r[/ruby2_keywords\.rb\z]) + if LOADING_RUBY2_KEYWORDS + assert_not_empty(list) + assert_not_include($LOADED_FEATURES, "ruby2_keywords.rb") + else + assert_empty(list) + assert_include($LOADED_FEATURES, "ruby2_keywords.rb") + end + end + + def test_module_ruby2_keywords + assert_send([Module, :private_method_defined?, :ruby2_keywords]) + assert_operator(Module.instance_method(:ruby2_keywords).arity, :<, 0) + end + + def test_toplevel_ruby2_keywords + main = TOPLEVEL_BINDING.eval('self') + assert_send([main, :respond_to?, :ruby2_keywords, true]) + assert_operator(main.method(:ruby2_keywords).arity, :<, 0) + end + + def test_proc_ruby2_keywords + assert_respond_to(Proc.new {}, :ruby2_keywords) + end + + def test_hash_ruby2_keywords_hash? + assert_false(Hash.ruby2_keywords_hash?({})) + end + + def test_hash_ruby2_keywords_hash + assert_equal({}, Hash.ruby2_keywords_hash({}.freeze)) + end +end