diff --git a/.github/workflows/rake.yml b/.github/workflows/rake.yml new file mode 100644 index 0000000..b13a247 --- /dev/null +++ b/.github/workflows/rake.yml @@ -0,0 +1,15 @@ +# Auto-generated by Cimas: Do not edit it manually! +# See https://github.com/metanorma/cimas +name: rake + +on: + push: + branches: [ master, main ] + tags: [ v* ] + pull_request: + +jobs: + rake: + uses: metanorma/ci/.github/workflows/generic-rake.yml@main + secrets: + pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d82d86e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,23 @@ +# Auto-generated by Cimas: Do not edit it manually! +# See https://github.com/metanorma/cimas +name: release + +on: + workflow_dispatch: + inputs: + next_version: + description: | + Next release version. Possible values: x.y.z, major, minor, patch or pre|rc|etc + required: true + default: 'skip' + repository_dispatch: + types: [ do-release ] + +jobs: + release: + uses: metanorma/ci/.github/workflows/rubygems-release.yml@main + with: + next_version: ${{ github.event.inputs.next_version }} + secrets: + rubygems-api-key: ${{ secrets.METANORMA_CI_RUBYGEMS_API_KEY }} + pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b04a8c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ + +# rspec failure tracking +.rspec_status diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..34c5164 --- /dev/null +++ b/.rspec @@ -0,0 +1,3 @@ +--format documentation +--color +--require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..762eebb --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,8 @@ +AllCops: + TargetRubyVersion: 3.0 + +Style/StringLiterals: + EnforcedStyle: double_quotes + +Style/StringLiteralsInInterpolation: + EnforcedStyle: double_quotes diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..67fe8ce --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..f175d94 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +# Specify your gem's dependencies in poepod.gemspec +gemspec diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..56be2cd --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,101 @@ +PATH + remote: . + specs: + poepod (0.1.0) + parallel (~> 1.20) + thor (~> 1.0) + tqdm + +GEM + remote: https://rubygems.org/ + specs: + activesupport (7.1.3.4) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + ast (2.4.2) + base64 (0.2.0) + bigdecimal (3.1.8) + concurrent-ruby (1.3.3) + connection_pool (2.4.1) + diff-lcs (1.5.1) + drb (2.2.1) + i18n (1.14.5) + concurrent-ruby (~> 1.0) + json (2.7.2) + language_server-protocol (3.17.0.3) + minitest (5.23.1) + mutex_m (0.2.0) + parallel (1.25.1) + parser (3.3.2.0) + ast (~> 2.4.1) + racc + racc (1.8.0) + rack (3.1.3) + rainbow (3.1.1) + rake (13.2.1) + regexp_parser (2.9.2) + rexml (3.3.0) + strscan + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) + rubocop (1.64.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.31.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) + rubocop-performance (1.21.0) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.25.0) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.33.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + ruby-progressbar (1.13.0) + strscan (3.1.0) + thor (1.3.1) + tqdm (0.4.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + +PLATFORMS + arm64-darwin-23 + ruby + +DEPENDENCIES + poepod! + rake + rspec + rubocop + rubocop-performance + rubocop-rails + +BUNDLED WITH + 2.5.11 diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..c17e23a --- /dev/null +++ b/README.adoc @@ -0,0 +1,84 @@ += Poepod + +Poepod is a Ruby gem that provides functionality to concatenate code files from +a directory into one text file for analysis by Poe. + +== Installation + +Add this line to your application's Gemfile: + +[source,ruby] +---- +gem 'poepod' +---- + +And then execute: + +[source,shell] +---- +$ bundle install +---- + +Or install it yourself as: + +[source,shell] +---- +$ gem install poepod +---- + +== Usage + +After installation, you can use the `poepod` command line tool to concatenate +code files: + +[source,shell] +---- +$ poepod help +Commands: + poepod concat DIRECTORY OUTPUT_FILE # Concatenate code from a directory into one text file + poepod help [COMMAND] # Describe available commands or one specific command + +$ poepod help concat +Usage: + poepod concat DIRECTORY OUTPUT_FILE + +Options: + [--exclude=one two three] # List of patterns to exclude + # Default: "node_modules/" ".git/" "build" "test" ".gitignore" ".DS_Store" "*.jpg" "*.jpeg" "*.png" "*.svg" "*.gif" "*.exe" "*.dll" "*.so" "*.bin" "*.o" "*.a" + [--config=CONFIG] # Path to configuration file + +Concatenate code from a directory into one text file +---- + +For example: + +[source,shell] +---- +$ poepod concat my_project +# => concatenated into my_project.txt +---- + +This will concatenate all code files from the specified directory into `output.txt`. + +You can also exclude certain directories or files by using the `--exclude` option: + +[source,shell] +---- +$ poepod concat my_project output.txt --exclude node_modules .git build test .gitignore .DS_Store .jpg .png .svg +---- + +== 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 +https://rubygems.org. + +== Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/riboseinc/poepod. \ No newline at end of file diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..cca7175 --- /dev/null +++ b/Rakefile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +require "rubocop/rake_task" + +RuboCop::RakeTask.new + +task default: %i[spec rubocop] diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..287f42a --- /dev/null +++ b/bin/console @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "bundler/setup" +require "poepod" + +# 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" +IRB.start(__FILE__) diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/exe/poepod b/exe/poepod new file mode 100755 index 0000000..d23ec13 --- /dev/null +++ b/exe/poepod @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative "../lib/poepod/cli" + +Poepod::Cli.start(ARGV) diff --git a/lib/poepod.rb b/lib/poepod.rb new file mode 100644 index 0000000..d73b812 --- /dev/null +++ b/lib/poepod.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require_relative "poepod/version" + +module Poepod + class Error < StandardError; end + # Your code goes here... +end diff --git a/lib/poepod/cli.rb b/lib/poepod/cli.rb new file mode 100644 index 0000000..b424d2c --- /dev/null +++ b/lib/poepod/cli.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "thor" +require "poepod/processor" + +module Poepod + class Cli < Thor + desc "concat DIRECTORY OUTPUT_FILE", "Concatenate code from a directory into one text file" + option :exclude, type: :array, default: Poepod::Processor::EXCLUDE_DEFAULT, desc: "List of patterns to exclude" + option :config, type: :string, desc: "Path to configuration file" + + def concat(directory, output_file = nil) + dir_path = Pathname.new(directory) + + dir_path = dir_path.expand_path unless dir_path.absolute? + + output_file ||= "#{dir_path.basename}.txt" + output_path = dir_path.dirname.join(output_file) + processor = Poepod::Processor.new(options[:config]) + total_files, copied_files = processor.write_directory_structure_to_file(directory, output_path, options[:exclude]) + + puts "-> #{total_files} files detected in the #{dir_path.relative_path_from(Dir.pwd)} directory." + puts "=> #{copied_files} files have been concatenated into #{Pathname.new(output_path).relative_path_from(Dir.pwd)}." + end + + def self.exit_on_failure? + true + end + end +end diff --git a/lib/poepod/processor.rb b/lib/poepod/processor.rb new file mode 100644 index 0000000..32dda3d --- /dev/null +++ b/lib/poepod/processor.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require "yaml" +require "tqdm" + +module Poepod + class Processor + EXCLUDE_DEFAULT = [ + "node_modules/", ".git/", ".gitignore", ".DS_Store", + "*.jpg", "*.jpeg", "*.png", "*.svg", "*.gif", + "*.exe", "*.dll", "*.so", "*.bin", "*.o", "*.a" + ].freeze + + def initialize(config_file = nil) + @failed_files = [] + @config = load_config(config_file) + end + + def load_config(config_file) + if config_file && File.exist?(config_file) + YAML.load_file(config_file) + else + {} + end + end + + def process_file(file_path) + content = File.read(file_path, encoding: "utf-8") + [file_path, content, nil] + rescue Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError + @failed_files << file_path + [file_path, nil, "Failed to decode the file, as it is not saved with UTF-8 encoding."] + end + + def gather_files(directory_path, exclude) + exclude += @config["exclude"] if @config["exclude"] + exclude_pattern = Regexp.union(exclude.map { |ex| Regexp.escape(ex) }) + + Dir.glob("#{directory_path}/**/*").reject do |file_path| + File.directory?(file_path) || file_path.match?(exclude_pattern) + end.map do |file_path| + Pathname.new(file_path).expand_path.to_s + end + end + + def write_results_to_file(results, output_file) + results.each_with_index do |(file_path, content, error), index| + relative = relative_path(file_path) + if content + output_file.puts "--- START FILE: #{relative} ---" + output_file.puts content + output_file.puts "--- END FILE: #{relative} ---" + elsif error + output_file.puts "#{relative}\n#{error}" + end + output_file.puts if index < results.size - 1 # Add a newline between files + end + end + + def relative_path(file_path) + Pathname.new(file_path).relative_path_from(Dir.pwd) + end + + def write_directory_structure_to_file(directory_path, output_file_name, exclude = EXCLUDE_DEFAULT) + dir_path = Pathname.new(directory_path) + + dir_path = dir_path.expand_path unless dir_path.absolute? + + file_list = gather_files(dir_path, exclude) + total_files = file_list.size + + File.open(output_file_name, "w", encoding: "utf-8") do |output_file| + results = file_list.tqdm(desc: "Progress", unit: " file").map do |file| + process_file(file) + end + write_results_to_file(results, output_file) + end + + copied_files = total_files - @failed_files.size + + [total_files, copied_files] + end + end +end diff --git a/lib/poepod/version.rb b/lib/poepod/version.rb new file mode 100644 index 0000000..15e09ef --- /dev/null +++ b/lib/poepod/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Poepod + VERSION = "0.1.0" +end diff --git a/poepod.gemspec b/poepod.gemspec new file mode 100644 index 0000000..0643cfc --- /dev/null +++ b/poepod.gemspec @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require_relative "lib/poepod/version" + +Gem::Specification.new do |spec| + spec.name = "poepod" + spec.version = Poepod::VERSION + spec.authors = ["Ribose Inc."] + spec.email = ["open.source@ribose.com"] + + spec.summary = "Utilities for uploading code to Poe" + spec.description = <<~DESCRIPTION + Utilities for uploading code to Poe + DESCRIPTION + + spec.homepage = "https://github.com/riboseinc/poepod" + spec.license = "BSD-2-Clause" + + spec.bindir = "bin" + spec.require_paths = ["lib"] + spec.files = `git ls-files`.split("\n") + spec.test_files = `git ls-files -- {spec}/*`.split("\n") + spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0") + + # 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(File.expand_path(__dir__)) do + `git ls-files -z`.split("\x0").reject do |f| + f.match(%r{^(test|spec|features)/}) + end + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_runtime_dependency "parallel", "~> 1.20" + spec.add_runtime_dependency "thor", "~> 1.0" + spec.add_runtime_dependency "tqdm" + spec.add_development_dependency "rake" + spec.add_development_dependency "rspec" + spec.add_development_dependency "rubocop" + spec.add_development_dependency "rubocop-performance" + spec.add_development_dependency "rubocop-rails" +end diff --git a/sig/poepod.rbs b/sig/poepod.rbs new file mode 100644 index 0000000..e7796db --- /dev/null +++ b/sig/poepod.rbs @@ -0,0 +1,4 @@ +module Poepod + VERSION: String + # See the writing guide of rbs: https://github.com/ruby/rbs#guides +end diff --git a/spec/poepod/processor_spec.rb b/spec/poepod/processor_spec.rb new file mode 100644 index 0000000..8662115 --- /dev/null +++ b/spec/poepod/processor_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +require "rspec" +require_relative "../../lib/poepod/processor" +require "tempfile" + +RSpec.describe Poepod::Processor do + let(:directory_path) { File.expand_path("#{__dir__}/../support/test_files") } + let(:output_file_name) { Tempfile.new("output.txt") } + let(:processor) { described_class.new } + + before do + File.write("#{directory_path}/file1.txt", "Content of file1.\n") + File.write("#{directory_path}/file2.txt", "Content of file2.\n") + end + + after do + File.delete(output_file_name) if File.exist?(output_file_name) + end + + describe "#process_file" do + it "reads the content of a file" do + file_path = "#{directory_path}/file1.txt" + _, content, error = processor.process_file(file_path) + expect(content).to eq("Content of file1.\n") + expect(error).to be_nil + end + + it "handles encoding errors gracefully" do + allow(File).to receive(:read).and_raise(Encoding::InvalidByteSequenceError) + file_path = "#{directory_path}/file1.txt" + _, content, error = processor.process_file(file_path) + expect(content).to be_nil + expect(error).to eq("Failed to decode the file, as it is not saved with UTF-8 encoding.") + end + end + + describe "#gather_files" do + it "gathers all file paths in a directory" do + files = processor.gather_files(directory_path, []) + expect(files).to contain_exactly( + "#{directory_path}/file1.txt", + "#{directory_path}/file2.txt" + ) + end + end + + describe "#write_results_to_file" do + it "writes the processed files to the output file" do + results = [ + ["#{directory_path}/file1.txt", "Content of file1.\n", nil], + ["#{directory_path}/file2.txt", "Content of file2.\n", nil] + ] + File.open(output_file_name, "w", encoding: "utf-8") do |output_file| + processor.write_results_to_file(results, output_file) + end + output_content = File.read(output_file_name, encoding: "utf-8") + expected_content = <<~TEXT + --- START FILE: spec/support/test_files/file1.txt --- + Content of file1. + --- END FILE: spec/support/test_files/file1.txt --- + + --- START FILE: spec/support/test_files/file2.txt --- + Content of file2. + --- END FILE: spec/support/test_files/file2.txt --- + TEXT + expect(output_content).to eq(expected_content) + end + end + + describe "#write_directory_structure_to_file" do + it "writes the directory structure to the output file with a progress bar" do + puts "directory_path: #{directory_path}" + puts Dir.glob("#{directory_path}/*") + total_files, copied_files = processor.write_directory_structure_to_file(directory_path, output_file_name) + expect(total_files).to eq(2) + expect(copied_files).to eq(2) + + output_content = File.read(output_file_name, encoding: "utf-8") + expected_content = <<~TEXT + --- START FILE: spec/support/test_files/file1.txt --- + Content of file1. + --- END FILE: spec/support/test_files/file1.txt --- + + --- START FILE: spec/support/test_files/file2.txt --- + Content of file2. + --- END FILE: spec/support/test_files/file2.txt --- + TEXT + expect(output_content).to eq(expected_content) + end + end +end diff --git a/spec/poepod_spec.rb b/spec/poepod_spec.rb new file mode 100644 index 0000000..a55a39d --- /dev/null +++ b/spec/poepod_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +RSpec.describe Poepod do + it "has a version number" do + expect(Poepod::VERSION).not_to be nil + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..dfb6a5f --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require "poepod" + +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end diff --git a/spec/support/test_files/file1.txt b/spec/support/test_files/file1.txt new file mode 100644 index 0000000..b4a774e --- /dev/null +++ b/spec/support/test_files/file1.txt @@ -0,0 +1 @@ +Content of file1. diff --git a/spec/support/test_files/file2.txt b/spec/support/test_files/file2.txt new file mode 100644 index 0000000..7e92326 --- /dev/null +++ b/spec/support/test_files/file2.txt @@ -0,0 +1 @@ +Content of file2.