[
  {
    "path": ".coveralls.yml",
    "content": "service_name: travis-ci\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\non: [push, pull_request]\njobs:\n  build:\n    strategy:\n      matrix:\n        ruby: ['3.0', '3.1', '3.2', '3.3']\n        gemfile: ['active6.0.6', 'active6.1.7.6', 'active7.0.8', 'active7.1.3', 'rails6.0.6', 'rails6.1.7.6', 'rails7.0.8', 'rails7.1.3']\n    runs-on: ubuntu-latest\n    env:\n      BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile\n      RUBY_OPT: --disable=did_you_mean\n    services:\n      mongodb:\n        image: mongo:4.4.10\n        ports:\n          - 8081:8081\n    steps:\n      - uses: actions/checkout@v3\n      - uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: ${{ matrix.ruby }}\n          bundler-cache: true\n          cache-version: 1\n      - run: bundle install --jobs 2 --retry 3\n      - run: bundle exec rubocop -P\n      - run: bundle exec rspec -f d spec\n"
  },
  {
    "path": ".gitignore",
    "content": "pkg/*\n.*.swp\n*~\n.bundle\nspec/rails_app/log\nspec/rails_app/db/development.sqlite*\n.rbx/\n.ruby-*\ncoverage/*\n"
  },
  {
    "path": ".rspec",
    "content": "--colour\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "inherit_from: .rubocop_todo.yml\n\nrequire: rubocop-performance\n\nAllCops:\n  DisplayCopNames: true\n  Exclude:\n    - 'gemfiles/**/*'\n    - 'vendor/**/*'\n  NewCops: enable\n  TargetRubyVersion: 2.5\n\nLayout/LineLength:\n  IgnoreCopDirectives: true\n  Max: 120\n\nNaming/FileName:\n  Exclude:\n    - 'Appraisals'\n\nNaming/VariableNumber:\n  EnforcedStyle: snake_case\n\n"
  },
  {
    "path": ".rubocop_todo.yml",
    "content": "# This configuration was generated by\n# `rubocop --auto-gen-config`\n# on 2024-01-20 09:02:51 UTC using RuboCop version 1.30.1.\n# The point is for the user to remove these configuration records\n# one by one as the offenses are removed from the code base.\n# Note that changes in the inspected code, or installation of new\n# versions of RuboCop, may require this file to be generated again.\n\n# Offense count: 1\n# This cop supports safe autocorrection (--autocorrect).\n# Configuration parameters: Include.\n# Include: **/*.gemspec\nGemspec/DeprecatedAttributeAssignment:\n  Exclude:\n    - 'comma.gemspec'\n\n# Offense count: 1\n# This cop supports safe autocorrection (--autocorrect).\n# Configuration parameters: Include.\n# Include: **/*.gemspec\nGemspec/RequireMFA:\n  Exclude:\n    - 'comma.gemspec'\n\n# Offense count: 1\n# Configuration parameters: Include.\n# Include: **/*.gemspec\nGemspec/RequiredRubyVersion:\n  Exclude:\n    - 'comma.gemspec'\n\n# Offense count: 1\n# This cop supports safe autocorrection (--autocorrect).\n# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, AllowAdjacentOneLineDefs, NumberOfEmptyLines.\nLayout/EmptyLineBetweenDefs:\n  Exclude:\n    - 'spec/rails_app/rails_app.rb'\n\n# Offense count: 4\n# This cop supports safe autocorrection (--autocorrect).\n# Configuration parameters: AllowAliasSyntax, AllowedMethods.\n# AllowedMethods: alias_method, public, protected, private\nLayout/EmptyLinesAroundAttributeAccessor:\n  Exclude:\n    - 'spec/comma/comma_spec.rb'\n\n# Offense count: 1\n# This cop supports safe autocorrection (--autocorrect).\nLayout/SpaceAroundMethodCallOperator:\n  Exclude:\n    - 'spec/controllers/users_controller_spec.rb'\n\n# Offense count: 17\n# Configuration parameters: AllowedMethods.\n# AllowedMethods: enums\nLint/ConstantDefinitionInBlock:\n  Exclude:\n    - 'spec/comma/comma_spec.rb'\n    - 'spec/comma/rails/active_record_spec.rb'\n    - 'spec/comma/rails/data_mapper_collection_spec.rb'\n    - 'spec/comma/rails/mongoid_spec.rb'\n\n# Offense count: 4\n# Configuration parameters: AllowComments, AllowEmptyLambdas.\nLint/EmptyBlock:\n  Exclude:\n    - 'spec/comma/comma_spec.rb'\n    - 'spec/comma/rails/data_mapper_collection_spec.rb'\n\n# Offense count: 1\nLint/MissingSuper:\n  Exclude:\n    - 'spec/comma/comma_spec.rb'\n\n# Offense count: 1\n# This cop supports unsafe autocorrection (--autocorrect-all).\nLint/NonDeterministicRequireOrder:\n  Exclude:\n    - 'spec/spec_helper.rb'\n\n# Offense count: 2\n# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.\nMetrics/AbcSize:\n  Max: 22\n\n# Offense count: 3\n# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.\nMetrics/MethodLength:\n  Max: 16\n\n# Offense count: 2\n# Configuration parameters: EnforcedStyle, AllowedIdentifiers, AllowedPatterns.\n# SupportedStyles: snake_case, camelCase\nNaming/VariableName:\n  Exclude:\n    - 'spec/comma/comma_spec.rb'\n\n# Offense count: 14\n# This cop supports safe autocorrection (--autocorrect).\n# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.\n# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces\n# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object\n# FunctionalMethods: let, let!, subject, watch\n# IgnoredMethods: lambda, proc, it\nStyle/BlockDelimiters:\n  Exclude:\n    - 'spec/comma/comma_spec.rb'\n    - 'spec/comma/data_extractor_spec.rb'\n    - 'spec/comma/header_extractor_spec.rb'\n\n# Offense count: 11\n# Configuration parameters: AllowedConstants.\nStyle/Documentation:\n  Exclude:\n    - 'spec/**/*'\n    - 'test/**/*'\n    - 'lib/comma/array.rb'\n    - 'lib/comma/data_extractor.rb'\n    - 'lib/comma/data_mapper_collection.rb'\n    - 'lib/comma/extractor.rb'\n    - 'lib/comma/generator.rb'\n    - 'lib/comma/header_extractor.rb'\n    - 'lib/comma/mongoid.rb'\n    - 'lib/comma/object.rb'\n    - 'lib/comma/relation.rb'\n\n# Offense count: 3\nStyle/MissingRespondToMissing:\n  Exclude:\n    - 'lib/comma/data_extractor.rb'\n    - 'lib/comma/header_extractor.rb'\n\n# Offense count: 1\n# This cop supports safe autocorrection (--autocorrect).\nStyle/RedundantBegin:\n  Exclude:\n    - 'spec/spec_helper.rb'\n\n# Offense count: 6\n# This cop supports unsafe autocorrection (--autocorrect-all).\n# Configuration parameters: Mode.\nStyle/StringConcatenation:\n  Exclude:\n    - 'spec/comma/comma_spec.rb'\n    - 'spec/comma/rails/active_record_spec.rb'\n    - 'spec/spec_helper.rb'\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\n\nservices: mongodb\n\nlanguage: ruby\ncache: bundler\nrvm:\n  - 2.4.10\n  - 2.5.8\n  - 2.6.6\n  - 2.7.2\ngemfile:\n  - gemfiles/active5.0.7.2.gemfile\n  - gemfiles/active5.1.7.gemfile\n  - gemfiles/active5.2.4.3.gemfile\n  - gemfiles/active6.0.3.1.gemfile\n  - gemfiles/active6.1.0.gemfile\n  - gemfiles/rails5.0.7.2.gemfile\n  - gemfiles/rails5.1.7.gemfile\n  - gemfiles/rails5.2.4.3.gemfile\n  - gemfiles/rails6.0.3.1.gemfile\n  - gemfiles/rails6.1.0.gemfile\n  - gemfiles/railsedge.gemfile\nmatrix:\n  exclude:\n    - rvm: 2.4.10\n      gemfile: gemfiles/active6.0.3.1.gemfile\n    - rvm: 2.4.10\n      gemfile: gemfiles/active6.1.0.gemfile\n    - rvm: 2.4.10\n      gemfile: gemfiles/rails6.0.3.1.gemfile\n    - rvm: 2.4.10\n      gemfile: gemfiles/rails6.1.0.gemfile\n    - rvm: 2.4.10\n      gemfile: gemfiles/railsedge.gemfile\n    - rvm: 2.5.8\n      gemfile: gemfiles/railsedge.gemfile\n  fast_finish: true\nbefore_install:\n  - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true\n  - gem install bundler -v '< 2'\nscript:\n  - bundle exec rubocop -P\n  - bundle exec rspec -f d spec\n"
  },
  {
    "path": "Appraisals",
    "content": "# frozen_string_literal: true\n\nappraise 'rails6.0.6' do\n  gem 'rails', '6.0.6'\n  gem 'rspec-rails'\n  gem 'test-unit'\nend\n\nappraise 'active6.0.6' do\n  gem 'activesupport', '6.0.6'\n  gem 'activerecord', '6.0.6'\nend\n\nappraise 'rails6.1.7.6' do\n  gem 'rails', '6.1.7.6'\n  gem 'rspec-rails'\n  gem 'test-unit'\nend\n\nappraise 'active6.1.7.6' do\n  gem 'activesupport', '6.1.7.6'\n  gem 'activerecord', '6.1.7.6'\nend\n\nappraise 'rails7.0.8' do\n  gem 'rails', '7.0.8'\n  gem 'rspec-rails'\nend\n\nappraise 'active7.0.8' do\n  gem 'activesupport', '7.0.8'\n  gem 'activerecord', '7.0.8'\nend\n\nappraise 'rails7.1.3' do\n  gem 'rails', '7.1.3'\n  gem 'rspec-rails'\nend\n\nappraise 'active7.1.3' do\n  gem 'activesupport', '7.1.3'\n  gem 'activerecord', '7.1.3'\nend\n"
  },
  {
    "path": "Gemfile",
    "content": "# frozen_string_literal: true\n\nsource 'https://rubygems.org'\n\ngemspec\n\ngem 'coveralls', require: false\ngem 'rubocop', '~> 1.30.0', require: false\ngem 'rubocop-performance', require: false\ngem 'sqlite3'\n"
  },
  {
    "path": "MIT-LICENSE",
    "content": "Copyright (c) 2009 Marcus Crafter\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Comma\n\nA library to generate comma seperated value (CSV) for Ruby objects like ActiveRecord and Array\n\n[![Gem Version](https://badge.fury.io/rb/comma.svg)](http://badge.fury.io/rb/comma) [![Build Status](https://github.com/comma-csv/comma/actions/workflows/build.yml/badge.svg)](https://github.com/comma-csv/comma/actions/workflows/build.yml) [![Code Climate](https://codeclimate.com/github/comma-csv/comma.svg)](https://codeclimate.com/github/comma-csv/comma)\n\n## Getting Started\n\n### Prerequisites\n\nYou need to use ruby 3.0 or later. If you generate CSV from ActiveRecord models, you need to have ActiveRecord 6.0 or later.\n\n### Installing\n\nComma is distributed as a gem, best installed via Bundler.\n\nInclude the gem in your Gemfile:\n\n```ruby\ngem 'comma', '~> 4.8.0'\n```\n\nOr, if you want to live life on the edge, you can get master from the main comma repository:\n\n```ruby\ngem 'comma',  git: 'git://github.com/comma-csv/comma.git'\n```\n\nThen, run `bundle install`.\n\n### Usage\n\nSee [this page](https://github.com/comma-csv/comma/wiki) for usages.\n\n## Running the tests\n\nTo run the test suite across multiple gem file sets, we're using [Appraisal](https://github.com/thoughtbot/appraisal), use the following commands:\n\n```sh\n$ bundle exec appraisal install\n$ bundle exec appraisal rake spec\n\n```\n\n## Contributing\n\n## Versioning\n\nWe use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/comma-csv/comma/tags).\n\n## Authors\n\n* Marcus Crafter - Initial work\n* Tom Meier - Initial work\n* Eito Katagiri\n\n## License\n\nThis project is licensed under the MIT License - see the [MIT-LICENSE](https://github.com/comma-csv/comma/blob/master/MIT-LICENSE) file fore details.\n"
  },
  {
    "path": "Rakefile",
    "content": "# frozen_string_literal: true\n\nrequire 'bundler/setup'\nrequire 'bundler/gem_tasks'\n\nrequire 'rspec/core/rake_task'\nRSpec::Core::RakeTask.new(:spec) do |spec|\n  spec.pattern = FileList['spec/**/*_spec.rb']\nend\n\nrequire 'rubocop/rake_task'\nRuboCop::RakeTask.new\n\ntask default: :spec\n"
  },
  {
    "path": "comma.gemspec",
    "content": "# frozen_string_literal: true\n\n$LOAD_PATH.push File.expand_path('lib', __dir__)\nrequire 'comma/version'\n\nGem::Specification.new do |s|\n  s.name        = 'comma'\n  s.version     = Comma::VERSION\n  s.authors     = ['Marcus Crafter', 'Tom Meier']\n  s.email       = ['crafterm@redartisan.com', 'tom@venombytes.com']\n  s.homepage    = 'http://github.com/comma-csv/comma'\n  s.summary     = %(Ruby Comma Seperated Values generation library)\n  s.description = %(Ruby Comma Seperated Values generation library)\n\n  s.files         = `git ls-files`.split(\"\\n\")\n  s.test_files    = `git ls-files -- {test,spec,features}/*`.split(\"\\n\")\n  s.require_paths = ['lib']\n\n  s.licenses = ['MIT']\n\n  s.add_dependency 'activesupport', '>= 4.2.0'\n\n  s.add_development_dependency 'appraisal', ['~> 1.0.0']\n  s.add_development_dependency 'minitest', '5.14.4'\n  s.add_development_dependency 'rake', '~> 13.0.1'\n  s.add_development_dependency 'rspec', ['~> 3.5.0']\n  s.add_development_dependency 'rspec-activemodel-mocks'\n  s.add_development_dependency 'rspec-its'\nend\n"
  },
  {
    "path": "gemfiles/active6.0.6.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"coveralls\", :require => false\ngem \"rubocop\", \"~> 1.30.0\", :require => false\ngem \"rubocop-performance\", :require => false\ngem \"sqlite3\"\ngem \"activesupport\", \"6.0.6\"\ngem \"activerecord\", \"6.0.6\"\n\ngemspec :path => \"../\"\n"
  },
  {
    "path": "gemfiles/active6.1.7.6.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"coveralls\", :require => false\ngem \"rubocop\", \"~> 1.30.0\", :require => false\ngem \"rubocop-performance\", :require => false\ngem \"sqlite3\"\ngem \"activesupport\", \"6.1.7.6\"\ngem \"activerecord\", \"6.1.7.6\"\n\ngemspec :path => \"../\"\n"
  },
  {
    "path": "gemfiles/active7.0.8.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"coveralls\", :require => false\ngem \"rubocop\", \"~> 1.30.0\", :require => false\ngem \"rubocop-performance\", :require => false\ngem \"sqlite3\"\ngem \"activesupport\", \"7.0.8\"\ngem \"activerecord\", \"7.0.8\"\n\ngemspec :path => \"../\"\n"
  },
  {
    "path": "gemfiles/active7.1.3.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"coveralls\", :require => false\ngem \"rubocop\", \"~> 1.30.0\", :require => false\ngem \"rubocop-performance\", :require => false\ngem \"sqlite3\"\ngem \"activesupport\", \"7.1.3\"\ngem \"activerecord\", \"7.1.3\"\n\ngemspec :path => \"../\"\n"
  },
  {
    "path": "gemfiles/rails6.0.6.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"coveralls\", :require => false\ngem \"rubocop\", \"~> 1.30.0\", :require => false\ngem \"rubocop-performance\", :require => false\ngem \"sqlite3\"\ngem \"rails\", \"6.0.6\"\ngem \"rspec-rails\"\ngem \"test-unit\"\n\ngemspec :path => \"../\"\n"
  },
  {
    "path": "gemfiles/rails6.1.7.6.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"coveralls\", :require => false\ngem \"rubocop\", \"~> 1.30.0\", :require => false\ngem \"rubocop-performance\", :require => false\ngem \"sqlite3\"\ngem \"rails\", \"6.1.7.6\"\ngem \"rspec-rails\"\ngem \"test-unit\"\n\ngemspec :path => \"../\"\n"
  },
  {
    "path": "gemfiles/rails7.0.8.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"coveralls\", :require => false\ngem \"rubocop\", \"~> 1.30.0\", :require => false\ngem \"rubocop-performance\", :require => false\ngem \"sqlite3\"\ngem \"rails\", \"7.0.8\"\ngem \"rspec-rails\"\n\ngemspec :path => \"../\"\n"
  },
  {
    "path": "gemfiles/rails7.1.3.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"coveralls\", :require => false\ngem \"rubocop\", \"~> 1.30.0\", :require => false\ngem \"rubocop-performance\", :require => false\ngem \"sqlite3\"\ngem \"rails\", \"7.1.3\"\ngem \"rspec-rails\"\n\ngemspec :path => \"../\"\n"
  },
  {
    "path": "init.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'comma'\n"
  },
  {
    "path": "lib/comma/array.rb",
    "content": "# frozen_string_literal: true\n\nclass Array\n  def to_comma(style = :default)\n    Comma::Generator.new(self, style).run(:each)\n  end\nend\n"
  },
  {
    "path": "lib/comma/data_extractor.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'comma/extractor'\n\nmodule Comma\n  class DataExtractor < Extractor\n    class ExtractValueFromInstance\n      def initialize(instance)\n        @instance = instance\n      end\n\n      def extract(sym, &block)\n        yield_block_with_value(extract_value(sym), &block)\n      end\n\n      private\n\n      def yield_block_with_value(value, &block)\n        block ? yield(value) : value\n      end\n\n      def extract_value(method)\n        extraction_object.send(method)\n      end\n\n      def extraction_object\n        @instance\n      end\n    end\n\n    class ExtractValueFromAssociationOfInstance < ExtractValueFromInstance\n      def initialize(instance, association_name)\n        super(instance)\n        @association_name = association_name\n      end\n\n      private\n\n      def extraction_object\n        @instance.send(@association_name) || null_association\n      end\n\n      def null_association\n        @null_association ||= Class.new(Class.const_defined?(:BasicObject) ? ::BasicObject : ::Object) do\n          def method_missing(_symbol, *_args, &_block)\n            nil\n          end\n        end.new\n      end\n    end\n\n    def method_missing(sym, *args, &block)\n      @results << ExtractValueFromInstance.new(@instance).extract(sym, &block) if\n        args.blank?\n\n      args.each do |arg|\n        case arg\n        when Hash\n          arg.each do |k, _v|\n            @results << ExtractValueFromAssociationOfInstance.new(@instance, sym).extract(k, &block)\n          end\n        when Symbol\n          @results << ExtractValueFromAssociationOfInstance.new(@instance, sym).extract(arg, &block)\n        when String\n          @results << ExtractValueFromInstance.new(@instance).extract(sym, &block)\n        else\n          raise \"Unknown data symbol #{arg.inspect}\"\n        end\n      end\n    end\n\n    def __static_column__(_header = nil, &block)\n      @results << (block ? yield(@instance) : nil)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/comma/data_mapper_collection.rb",
    "content": "# frozen_string_literal: true\n\nif defined?(DataMapper)\n  module DataMapper\n    class Collection\n      def to_comma(style = :default)\n        Comma::Generator.new(self, style).run(:each)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/comma/extractor.rb",
    "content": "# frozen_string_literal: true\n\nmodule Comma\n  class Extractor\n    def initialize(instance, style, formats)\n      @instance = instance\n      @style = style\n      @formats = formats\n      @results = []\n    end\n\n    def results\n      instance_eval(&@formats[@style])\n      @results.map { |r| convert_to_data_value(r) }\n    end\n\n    def id(*args, &block)\n      method_missing(:id, *args, &block)\n    end\n\n    def __use__(style)\n      # TODO: prevent infinite recursion\n      instance_eval(&@formats[style])\n    end\n\n    private\n\n    def convert_to_data_value(result)\n      result.nil? ? result : result.to_s\n    end\n  end\nend\n"
  },
  {
    "path": "lib/comma/generator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Comma\n  class Generator\n    def initialize(instance, style)\n      @instance = instance\n      @style    = style\n      @options  = {}\n\n      return unless @style.is_a?(Hash)\n\n      @options                  = @style.clone\n      @style                    = @options.delete(:style) || Comma::DEFAULT_OPTIONS[:style]\n      @filename                 = @options.delete(:filename)\n    end\n\n    def run(iterator_method)\n      if @filename\n        CSV_HANDLER.open(@filename, 'w', **@options) { |csv| append_csv(csv, iterator_method) } && (return true)\n      else\n        CSV_HANDLER.generate(**@options) { |csv| append_csv(csv, iterator_method) }\n      end\n    end\n\n    private\n\n    def append_csv(csv, iterator_method)\n      return '' if @instance.empty?\n\n      csv << @instance.first.to_comma_headers(@style) unless\n        @options.key?(:write_headers) && !@options[:write_headers]\n      @instance.send(iterator_method) do |object|\n        csv << object.to_comma(@style)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/comma/header_extractor.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'comma/extractor'\nrequire 'active_support'\nrequire 'active_support/core_ext/class/attribute'\nrequire 'active_support/core_ext/date_time/conversions'\nrequire 'active_support/core_ext/object/blank'\nrequire 'active_support/core_ext/string/inflections'\n\nmodule Comma\n  class HeaderExtractor < Extractor\n    class_attribute :value_humanizer\n\n    DEFAULT_VALUE_HUMANIZER = lambda do |value, _model_class|\n      value.is_a?(String) ? value : value.to_s.humanize\n    end\n    self.value_humanizer = DEFAULT_VALUE_HUMANIZER\n\n    def method_missing(sym, *args, &_block)\n      model_class = @instance.class\n      @results << value_humanizer.call(sym, model_class) if args.blank?\n      args.each do |arg|\n        case arg\n        when Hash\n          arg.each do |_k, v|\n            @results << value_humanizer.call(v, get_association_class(model_class, sym))\n          end\n        when Symbol\n          @results << value_humanizer.call(arg, get_association_class(model_class, sym))\n        when String\n          @results << value_humanizer.call(arg, model_class)\n        else\n          raise \"Unknown header symbol #{arg.inspect}\"\n        end\n      end\n    end\n\n    def __static_column__(header = '', &_block)\n      @results << header\n    end\n\n    private\n\n    def get_association_class(model_class, association)\n      return unless model_class.respond_to?(:reflect_on_association)\n\n      begin\n        model_class.reflect_on_association(association)&.klass\n      rescue ArgumentError, NameError\n        # Since Rails 5.2, ArgumentError is raised.\n        nil\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/comma/mongoid.rb",
    "content": "# frozen_string_literal: true\n\n# Conditionally set to_comma on Mongoid records if mongoid gem is installed\nbegin\n  require 'mongoid'\n\n  module Mongoid\n    class Criteria\n      def to_comma(style = :default)\n        Comma::Generator.new(self, style).run(:each)\n      end\n    end\n  end\nrescue LoadError => e\n  warn e.inspect\nend\n"
  },
  {
    "path": "lib/comma/object.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'comma/data_extractor'\nrequire 'comma/header_extractor'\n\nclass Object\n  class_attribute :comma_formats\n\n  class << self\n    def comma(style = :default, &block)\n      (self.comma_formats ||= {})[style] = block\n    end\n\n    def inherited(subclass)\n      super\n      subclass.comma_formats = self.comma_formats ? self.comma_formats.dup : {}\n    end\n  end\n\n  def to_comma(style = :default)\n    extract_with(Comma::DataExtractor, style)\n  end\n\n  def to_comma_headers(style = :default)\n    extract_with(Comma::HeaderExtractor, style)\n  end\n\n  private\n\n  def extract_with(extractor_class, style = :default)\n    raise_unless_style_exists(style)\n    extractor_class.new(self, style, self.comma_formats).results\n  end\n\n  def raise_unless_style_exists(style)\n    return if self.comma_formats && self.comma_formats[style]\n\n    raise \"No comma format for class #{self.class} defined for style #{style}\"\n  end\nend\n"
  },
  {
    "path": "lib/comma/relation.rb",
    "content": "# frozen_string_literal: true\n\nmodule ActiveRecord\n  class Relation\n    def to_comma(style = :default)\n      iterator_method =\n        if arel.ast.limit || !arel.ast.orders.empty?\n          Rails.logger.warn { <<~WARN } if defined?(Rails)\n            #to_comma is being used on a relation with limit or order clauses. Falling back to iterating with :each. This can cause performance issues.\n          WARN\n          :each\n        else\n          :find_each\n        end\n      Comma::Generator.new(self, style).run(iterator_method)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/comma/version.rb",
    "content": "# frozen_string_literal: true\n\nmodule Comma\n  VERSION = '4.8.0'\nend\n"
  },
  {
    "path": "lib/comma.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'csv'\nCSV_HANDLER = CSV\n\nmodule Comma\n  DEFAULT_OPTIONS = {\n    write_headers: true,\n    style: :default\n  }.freeze\nend\n\nrequire 'active_support'\nrequire 'active_support/lazy_load_hooks'\nActiveSupport.on_load(:active_record) do\n  require 'comma/relation' if defined?(ActiveRecord::Relation)\nend\n\nActiveSupport.on_load(:mongoid) do\n  require 'comma/mongoid'\nend\n\nrequire 'comma/data_mapper_collection' if defined? DataMapper\n\nrequire 'comma/generator'\nrequire 'comma/array'\nrequire 'comma/object'\n\n# Load into Rails controllers\nActiveSupport.on_load(:action_controller) do\n  if defined?(ActionController::Renderers) && ActionController::Renderers.respond_to?(:add)\n    ActionController::Renderers.add :csv do |obj, options|\n      filename    = options[:filename]  || 'data'\n      extension   = options[:extension] || 'csv'\n      mime_type = if Rails.version >= '5.0.0'\n                    options[:mime_type] || Mime[:csv]\n                  else\n                    options[:mime_type] || Mime::CSV\n                  end\n      with_bom = options.delete(:with_bom) || false\n\n      # Capture any CSV optional settings passed to comma or comma specific options\n      csv_options = options.slice(*CSV_HANDLER::DEFAULT_OPTIONS.merge(Comma::DEFAULT_OPTIONS).keys)\n      csv_options = csv_options.each_with_object({}) do |(k, v), h|\n        # XXX: Convert string to boolean\n        h[k] = case k\n               when :write_headers\n                 (v != 'false') if v.is_a?(String)\n               else\n                 v\n               end\n      end\n      data = obj.to_comma(csv_options)\n      data = \"\\xEF\\xBB\\xBF#{data}\" if with_bom\n      disposition = \"attachment; filename=\\\"#{filename}.#{extension}\\\"\"\n      send_data data, type: mime_type, disposition: disposition\n    end\n  end\nend\n"
  },
  {
    "path": "spec/comma/comma_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire File.dirname(__FILE__) + '/../spec_helper'\n\ndescribe Comma do\n  it 'should extend object to add a comma method' do\n    expect(Object).to respond_to(:comma)\n  end\n\n  it 'should extend object to have a to_comma method' do\n    expect(Object).to respond_to(:to_comma)\n  end\n\n  it 'should extend object to have a to_comma_headers method' do\n    expect(Object).to respond_to(:to_comma_headers)\n  end\n\n  describe '.to_comma_header' do\n    it 'should not crash (#94)' do\n      klass = Class.new\n      klass.instance_eval do\n        attr_accessor :name\n\n        comma :brief do\n          name\n        end\n      end\n      expect { klass.to_comma_headers(:brief) }.to_not raise_error\n    end\n  end\nend\n\ndescribe Comma, 'generating CSV' do # rubocop:disable Metrics/BlockLength\n  before do\n    @isbn = Isbn.new('123123123', '321321321')\n    @book = Book.new('Smalltalk-80', 'Language and Implementation', @isbn)\n\n    @books = []\n    @books << @book\n  end\n\n  it 'should extend Array to add a #to_comma method which will return CSV content for objects within the array' do\n    expected = \"Title,Description,Issuer,ISBN-10,ISBN-13\\nSmalltalk-80,Language and Implementation,ISBN,123123123,321321321\\n\" # rubocop:disable Layout/LineLength\n    expect(@books.to_comma).to eq(expected)\n  end\n\n  it 'should return an empty string when generating CSV from an empty array' do\n    expect([].to_comma).to eq('')\n  end\n\n  it 'should change the style when specified' do\n    expect(@books.to_comma(:brief)).to eq(\"Name,Description\\nSmalltalk-80,Language and Implementation\\n\")\n  end\n\n  describe 'with :filename specified' do\n    after { File.delete('comma.csv') }\n\n    it 'should write to the file' do\n      @books.to_comma(filename: 'comma.csv')\n      expected = \"Title,Description,Issuer,ISBN-10,ISBN-13\\nSmalltalk-80,Language and Implementation,ISBN,123123123,321321321\\n\" # rubocop:disable Layout/LineLength\n      expect(File.read('comma.csv')).to eq(expected)\n    end\n\n    it 'should accept FasterCSV options' do\n      @books.to_comma(filename: 'comma.csv', col_sep: ';', force_quotes: true)\n      expected = \"\\\"Title\\\";\\\"Description\\\";\\\"Issuer\\\";\\\"ISBN-10\\\";\\\"ISBN-13\\\"\\n\\\"Smalltalk-80\\\";\\\"Language and Implementation\\\";\\\"ISBN\\\";\\\"123123123\\\";\\\"321321321\\\"\\n\" # rubocop:disable Layout/LineLength\n      expect(File.read('comma.csv')).to eq(expected)\n    end\n  end\n\n  describe 'with FasterCSV options' do\n    it 'should not change when options are empty' do\n      expected = \"Title,Description,Issuer,ISBN-10,ISBN-13\\nSmalltalk-80,Language and Implementation,ISBN,123123123,321321321\\n\" # rubocop:disable Layout/LineLength\n      expect(@books.to_comma({})).to eq(expected)\n    end\n\n    it 'should accept the options in #to_comma and generate the appropriate CSV' do\n      expected = \"\\\"Title\\\";\\\"Description\\\";\\\"Issuer\\\";\\\"ISBN-10\\\";\\\"ISBN-13\\\"\\n\\\"Smalltalk-80\\\";\\\"Language and Implementation\\\";\\\"ISBN\\\";\\\"123123123\\\";\\\"321321321\\\"\\n\" # rubocop:disable Layout/LineLength\n      expect(@books.to_comma(col_sep: ';', force_quotes: true)).to eq(expected)\n    end\n\n    it 'should change the style when specified' do\n      expect(@books.to_comma(style: :brief, col_sep: ';', force_quotes: true))\n        .to eq(\"\\\"Name\\\";\\\"Description\\\"\\n\\\"Smalltalk-80\\\";\\\"Language and Implementation\\\"\\n\")\n    end\n  end\nend\n\ndescribe Comma, 'defining CSV descriptions' do\n  describe 'with an unnamed description' do\n    before do\n      class Foo\n        comma do; end\n      end\n    end\n\n    it 'should name the current description :default if no name has been provided' do\n      expect(Foo.comma_formats).not_to be_empty\n      expect(Foo.comma_formats[:default]).not_to be_nil\n    end\n  end\n\n  describe 'with a named description' do\n    before do\n      class Bar\n        comma do; end\n        comma :detailed do; end\n      end\n    end\n\n    it 'should use the provided name to index the comma format' do\n      expect(Bar.comma_formats).not_to be_empty\n      expect(Bar.comma_formats[:default]).not_to be_nil\n      expect(Bar.comma_formats[:detailed]).not_to be_nil\n    end\n  end\nend\n\ndescribe Comma, 'to_comma data/headers object extensions' do # rubocop:disable Metrics/BlockLength\n  describe 'with unnamed descriptions' do\n    before do\n      class Foo\n        attr_accessor :content\n        comma do; content; end\n\n        def initialize(content)\n          @content = content\n        end\n      end\n\n      @foo = Foo.new('content')\n    end\n\n    it 'should return and array of data content, using the :default CSV description if none requested' do\n      expect(@foo.to_comma).to eq(%w[content])\n    end\n\n    it 'should return and array of header content, using the :default CSV description if none requested' do\n      expect(@foo.to_comma_headers).to eq(%w[Content])\n    end\n\n    it 'should return the CSV representation including header and content when called on an array' do\n      expect(Array(@foo).to_comma).to eq(\"Content\\ncontent\\n\")\n    end\n  end\n\n  describe 'with named descriptions' do\n    before do\n      class Foo\n        attr_accessor :content\n        comma :detailed do; content; end\n\n        def initialize(content)\n          @content = content\n        end\n      end\n\n      @foo = Foo.new('content')\n    end\n\n    it 'should return and array of data content, using the :default CSV description if none requested' do\n      expect(@foo.to_comma(:detailed)).to eq(%w[content])\n    end\n\n    it 'should return and array of header content, using the :default CSV description if none requested' do\n      expect(@foo.to_comma_headers(:detailed)).to eq(%w[Content])\n    end\n\n    it 'should return the CSV representation including header and content when called on an array' do\n      expect(Array(@foo).to_comma(:detailed)).to eq(\"Content\\ncontent\\n\")\n    end\n\n    it 'should raise an error if the requested description is not avaliable' do\n      expect { @foo.to_comma(:bad) }.to raise_error(RuntimeError)\n      expect { @foo.to_comma_headers(:bad) }.to raise_error(RuntimeError)\n      expect { Array(@foo).to_comma(:bad) }.to raise_error(RuntimeError)\n    end\n  end\n\n  describe 'with block' do # rubocop:disable Metrics/BlockLength\n    before do\n      class Foo\n        attr_accessor :content, :created_at, :updated_at\n        comma do\n          content\n          content('Truncated Content') { |i| i && i.length > 10 ? i[0..10] : '---' }\n          created_at { |i| i&.to_formatted_s(:db) }\n          updated_at { |i| i&.to_formatted_s(:db) }\n          created_at 'Created Custom Label' do |i| i&.to_formatted_s(:short) end\n          updated_at 'Updated at Custom Label' do |i| i&.to_formatted_s(:short) end\n        end\n\n        def initialize(content, created_at = Time.now, updated_at = Time.now)\n          @content = content\n          @created_at = created_at\n          @updated_at = updated_at\n        end\n      end\n\n      @time = Time.now\n      @content = 'content ' * 5\n      @foo = Foo.new @content, @time, @time\n    end\n\n    it 'should return yielded values by block' do\n      _header, foo = Array(@foo).to_comma.split(\"\\n\")\n      expected = [\n        @content,\n        @content[0..10],\n        @time.to_formatted_s(:db),\n        @time.to_formatted_s(:db),\n        @time.to_formatted_s(:short),\n        @time.to_formatted_s(:short)\n      ].join(',')\n      expect(foo).to eq(expected)\n    end\n\n    it 'should return headers with custom labels from block' do\n      header, _foo = Array(@foo).to_comma.split(\"\\n\")\n      expected = [\n        'Content',\n        'Truncated Content',\n        'Created at',\n        'Updated at',\n        'Created Custom Label',\n        'Updated at Custom Label'\n      ].join(',')\n      expect(header).to eq(expected)\n    end\n\n    it 'should put headers in place when forced' do\n      header, _foo = Array(@foo).to_comma(write_headers: true).split(\"\\n\")\n      expected = [\n        'Content',\n        'Truncated Content',\n        'Created at',\n        'Updated at',\n        'Created Custom Label',\n        'Updated at Custom Label'\n      ].join(',')\n      expect(header).to eq(expected)\n    end\n\n    it 'should not write headers if specified' do\n      header, _foo = Array(@foo).to_comma(write_headers: false).split(\"\\n\")\n      expected = [\n        @content,\n        @content[0..10],\n        @time.to_formatted_s(:db),\n        @time.to_formatted_s(:db),\n        @time.to_formatted_s(:short),\n        @time.to_formatted_s(:short)\n      ].join(',')\n      expect(header).to eq(expected)\n    end\n  end\n\n  describe 'on an object with no comma declaration' do\n    it 'should raise an error mentioning there is no comma description defined for that class' do\n      expect { 'a string'.to_comma }.to raise_error('No comma format for class String defined for style default')\n      expect { 'a string'.to_comma_headers }\n        .to raise_error('No comma format for class String defined for style default')\n    end\n  end\n\n  describe 'on objects using Single Table Inheritance' do\n    before do\n      class MySuperClass\n        attr_accessor :content\n        comma do; content end\n\n        def initialize(content)\n          @content = 'super-' + content\n        end\n      end\n\n      class ChildClassComma < MySuperClass\n        comma do; content end\n\n        def initialize(content)\n          @content = 'sub-' + content\n        end\n      end\n\n      class ChildClassNoComma < MySuperClass\n      end\n\n      @childComma = ChildClassComma.new('content')\n      @childNoComma = ChildClassNoComma.new('content')\n    end\n\n    it 'should return and array of data content, as defined in comma block in child class' do\n      expect(@childComma.to_comma).to eq(%w[sub-content])\n    end\n\n    it 'should return and array of data content, as defined in comma block in super class, if not present in child' do\n      expect(@childNoComma.to_comma).to eq(%w[super-content])\n    end\n  end\nend\n\ndescribe Comma, '__use__ keyword' do\n  before(:all) do\n    @obj = Class.new(Struct.new(:id, :title, :description)) do\n      comma do\n        title\n        __use__ :description\n      end\n\n      comma :description do\n        __use__ :static\n        description\n      end\n\n      comma :static do\n        __static_column__ do 'Foo, Inc.' end\n      end\n    end.new(1, 'Programming Ruby', 'The Pickaxe book')\n  end\n\n  subject { @obj.to_comma }\n  its(:size) { should eq(3) }\n  it { should eq(['Programming Ruby', 'Foo, Inc.', 'The Pickaxe book']) }\nend\n"
  },
  {
    "path": "spec/comma/data_extractor_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\n# comma do\n#   name 'Title'\n#   description\n#\n#   isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'\n# end\n\ndescribe Comma::DataExtractor do # rubocop:disable Metrics/BlockLength\n  before do\n    @isbn = Isbn.new('123123123', '321321321')\n    @book = Book.new('Smalltalk-80', 'Language and Implementation', @isbn)\n\n    @data = @book.to_comma\n  end\n\n  describe 'when no parameters are provided' do\n    it 'should use the string value returned by sending the method name on the object' do\n      expect(@data).to include('Language and Implementation')\n    end\n  end\n\n  describe 'when given a string description as a parameter' do\n    it 'should use the string value returned by sending the method name on the object' do\n      expect(@data).to include('Smalltalk-80')\n    end\n  end\n\n  describe 'when an hash is passed as a parameter' do\n    describe 'with a string value' do\n      it 'should use the string value, returned by sending the hash key to the object' do\n        expect(@data).to include('123123123')\n        expect(@data).to include('321321321')\n      end\n\n      it 'should not fail when an associated object is nil' do\n        expect { Book.new('Smalltalk-80', 'Language and Implementation', nil).to_comma }.not_to raise_error\n      end\n    end\n  end\nend\n\ndescribe Comma::DataExtractor, 'id attribute' do\n  before do\n    @data = Class.new(Struct.new(:id)) do\n      comma do\n        id 'ID' do |_id| '42' end\n      end\n    end.new(1).to_comma\n  end\n\n  it 'id attribute should yield block' do\n    expect(@data).to include('42')\n  end\nend\n\ndescribe Comma::DataExtractor, 'with static column method' do\n  before do\n    @data = Class.new(Struct.new(:id, :name)) do\n      comma do\n        __static_column__\n        __static_column__ 'STATIC'\n        __static_column__ 'STATIC' do '' end\n        __static_column__ 'STATIC', &:name\n      end\n    end.new(1, 'John Doe').to_comma\n  end\n\n  it 'should extract headers' do\n    expect(@data).to eq([nil, nil, '', 'John Doe'])\n  end\nend\n\ndescribe Comma::DataExtractor, 'nil value' do\n  before do\n    @data = Class.new(Struct.new(:id, :name)) do\n      comma do\n        name\n        name 'Name'\n        name 'Name' do |_name| nil end\n      end\n    end.new(1, nil).to_comma\n  end\n\n  it 'should extract nil' do\n    expect(@data).to eq([nil, nil, nil])\n  end\nend\n"
  },
  {
    "path": "spec/comma/header_extractor_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\n# comma do\n#   name 'Title'\n#   description\n#\n#   isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'\n# end\n\ndescribe Comma::HeaderExtractor do # rubocop:disable Metrics/BlockLength\n  before do\n    @isbn = Isbn.new('123123123', '321321321')\n    @book = Book.new('Smalltalk-80', 'Language and Implementation', @isbn)\n\n    @headers = @book.to_comma_headers\n  end\n\n  describe 'when no parameters are provided' do\n    it 'should use the method name as the header name, humanized' do\n      expect(@headers).to include('Description')\n    end\n  end\n\n  describe 'when given a string description as a parameter' do\n    it 'should use the string value, unmodified' do\n      expect(@headers).to include('Title')\n    end\n  end\n\n  describe 'when an hash is passed as a parameter' do\n    describe 'with a string value' do\n      it 'should use the string value, unmodified' do\n        expect(@headers).to include('ISBN-10')\n      end\n    end\n\n    describe 'with a non-string value' do\n      it 'should use the non string value converted to a string, humanized' do\n        expect(@headers).to include('Issuer')\n      end\n    end\n  end\nend\n\ndescribe Comma::HeaderExtractor, 'with static column method' do\n  before do\n    @headers = Class.new(Struct.new(:id, :name)) do\n      comma do\n        __static_column__\n        __static_column__ 'STATIC'\n        __static_column__ 'STATIC' do '' end\n      end\n    end.new(1, 'John Doe').to_comma_headers\n  end\n\n  it 'should extract headers' do\n    expect(@headers).to eq(['', 'STATIC', 'STATIC'])\n  end\nend\n"
  },
  {
    "path": "spec/comma/rails/active_record_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nif defined? ActiveRecord\n\n  describe Comma, 'generating CSV from an ActiveRecord object' do # rubocop:disable Metrics/BlockLength\n    class Picture < ActiveRecord::Base\n      belongs_to :imageable, polymorphic: true\n\n      comma :pr_83 do\n        imageable name: 'Picture'\n      end\n    end\n\n    class Person < ActiveRecord::Base\n      scope(:teenagers, -> { where(age: 13..19) })\n\n      comma do\n        name\n        age\n      end\n\n      has_one :job\n\n      comma :issue_75 do\n        job :title\n      end\n\n      has_many :pictures, as: :imageable\n    end\n\n    class Job < ActiveRecord::Base\n      belongs_to :person\n\n      comma do\n        person_formatter name: 'Name'\n      end\n\n      def person_formatter\n        @person_formatter ||= PersonFormatter.new(person)\n      end\n    end\n\n    class PersonFormatter\n      def initialize(persor)\n        @person = persor\n      end\n\n      def name\n        @person.name\n      end\n    end\n\n    before(:all) do\n      # Setup AR model in memory\n      ActiveRecord::Base.connection.create_table :pictures, force: true do |table|\n        table.column :name, :string\n        table.column :imageable_id, :integer\n        table.column :imageable_type, :string\n      end\n\n      ActiveRecord::Base.connection.create_table :people, force: true do |table|\n        table.column :name, :string\n        table.column :age, :integer\n      end\n      Person.reset_column_information\n\n      ActiveRecord::Base.connection.create_table :jobs, force: true do |table|\n        table.column :person_id, :integer\n        table.column :title, :string\n      end\n      Job.reset_column_information\n\n      @person = Person.new(age: 18, name: 'Junior')\n      @person.build_job(title: 'Nice job')\n      @person.save!\n      Picture.create(name: 'photo.jpg', imageable_id: @person.id, imageable_type: 'Person')\n    end\n\n    describe '#to_comma on scopes' do\n      it 'should extend ActiveRecord::NamedScope::Scope to add a #to_comma method which will return CSV content for objects within the scope' do # rubocop:disable Layout/LineLength\n        expect(Person.teenagers.to_comma).to eq \"Name,Age\\nJunior,18\\n\"\n      end\n\n      it 'should find in batches' do\n        scope = Person.teenagers\n        expect(scope).to receive(:find_each).and_yield @person\n        scope.to_comma\n      end\n\n      it 'should fall back to iterating with each when scope has limit clause' do\n        scope = Person.limit(1)\n        expect(scope).to receive(:each).and_yield @person\n        scope.to_comma\n      end\n\n      it 'should fall back to iterating with each when scope has order clause' do\n        scope = Person.order(:age)\n        expect(scope).to receive(:each).and_yield @person\n        scope.to_comma\n      end\n    end\n\n    describe 'with custom value_humanizer' do\n      before do\n        Comma::HeaderExtractor.value_humanizer =\n          lambda do |value, model_class|\n            if model_class.respond_to?(:human_attribute_name)\n              model_class.human_attribute_name(value)\n            else\n              value.is_a?(String) ? value : value.to_s.humanize\n            end\n          end\n\n        I18n.config.backend.store_translations(:ja, activerecord: { attributes: { person: { age: '年齢', name: '名前' } } })\n        @original_locale = I18n.locale\n        I18n.locale = :ja\n      end\n\n      after do\n        I18n.locale = @original_locale\n\n        Comma::HeaderExtractor.value_humanizer =\n          Comma::HeaderExtractor::DEFAULT_VALUE_HUMANIZER\n      end\n\n      it 'should i18n-ize header values' do\n        expect(Person.teenagers.to_comma).to match(/^名前,年齢/)\n      end\n    end\n\n    describe 'github issue 75' do\n      it 'should find association' do\n        expect { Person.all.to_comma(:issue_75) }.not_to raise_error\n      end\n    end\n\n    describe 'with accessor' do\n      it 'should not raise exception' do\n        expect(Job.all.to_comma).to eq(\"Name\\nJunior\\n\")\n      end\n    end\n\n    describe 'github pull-request 83' do\n      it 'should not raise NameError' do\n        expect { Picture.all.to_comma(:pr_83) }.not_to raise_error\n      end\n    end\n  end\n\n  describe Comma, 'generating CSV from an ActiveRecord object using Single Table Inheritance' do # rubocop:disable Metrics/BlockLength\n    class Animal < ActiveRecord::Base\n      comma do\n        name 'Name' do |name|\n          'Super-' + name\n        end\n      end\n\n      comma :with_type do\n        name\n        type\n      end\n    end\n\n    class Dog < Animal\n      comma do\n        name 'Name' do |name|\n          'Dog-' + name\n        end\n      end\n    end\n\n    class Cat < Animal\n    end\n\n    before(:all) do\n      # Setup AR model in memory\n      ActiveRecord::Base.connection.create_table :animals, force: true do |table|\n        table.column :name, :string\n        table.column :type, :string\n      end\n\n      @dog = Dog.new(name: 'Rex')\n      @dog.save!\n      @cat = Cat.new(name: 'Kitty')\n      @cat.save!\n    end\n\n    it 'should return and array of data content, as defined in comma block in child class' do\n      expect(@dog.to_comma).to eq %w[Dog-Rex]\n    end\n\n    # FIXME: this one is failing - the comma block from Dog is executed instead of the one from the super class\n    it 'should return and array of data content, as defined in comma block in super class, if not present in child' do\n      expect(@cat.to_comma).to eq %w[Super-Kitty]\n    end\n\n    it 'should call definion in parent class' do\n      expect { @dog.to_comma(:with_type) }.not_to raise_error\n    end\n  end\nend\n"
  },
  {
    "path": "spec/comma/rails/data_mapper_collection_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nif defined? DataMapper\n\n  describe Comma, 'generating CSV from an DataMapper object' do # rubocop:disable Metrics/BlockLength\n    class Person\n      include DataMapper::Resource\n\n      property :id, Serial\n      property :name, String\n      property :age, Integer\n\n      def self.teenagers\n        all(:age.gte => 13) & all(:age.lte => 19)\n      end\n\n      comma do\n        name\n        age\n      end\n    end\n    DataMapper.finalize\n\n    before(:all) do\n      DataMapper.setup(:default, 'sqlite::memory:')\n      DataMapper.auto_migrate!\n    end\n\n    after(:all) do\n    end\n\n    describe 'case' do\n      before do\n        @person = Person.new(age: 18, name: 'Junior')\n        @person.save\n      end\n\n      it 'should extend scope to add a #to_comma method which will return CSV content for objects within the scope' do\n        Person.teenagers.to_comma.should == \"Name,Age\\nJunior,18\\n\"\n      end\n\n      it 'should find in batches' do\n        Person.teenagers.to_comma\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/comma/rails/mongoid_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nif defined? Mongoid\n\n  describe Comma, 'generating CSV from an Mongoid object' do\n    class Person\n      include Mongoid::Document\n\n      field :name, type: String\n      field :age, type: Integer\n\n      scope :teenagers, between(age: 13..19)\n\n      comma do\n        name\n        age\n      end\n    end\n\n    after(:all) do\n      Mongoid.purge!\n    end\n\n    describe 'case' do\n      before do\n        @person = Person.new(age: 18, name: 'Junior')\n        @person.save\n      end\n\n      it 'should extend ActiveRecord::NamedScope::Scope to add a #to_comma method which will return CSV content for objects within the scope' do # rubocop:disable Layout/LineLength\n        Person.teenagers.to_comma.should == \"Name,Age\\nJunior,18\\n\"\n      end\n\n      it 'should find in batches' do\n        Person.teenagers.to_comma\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/users_controller_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nif defined?(Rails)\n\n  RSpec.describe UsersController, type: :controller do # rubocop:disable Metrics/BlockLength\n    describe 'rails setup' do\n      it 'should capture the CSV renderer provided by Rails' do\n        mock_users = [mock_model(User), mock_model(User)]\n        allow(User).to receive(:all).and_return(mock_users)\n\n        expect(mock_users).to receive(:to_comma).once\n\n        get :index, format: :csv\n      end\n    end\n\n    describe 'controller' do # rubocop:disable Metrics/BlockLength\n      before(:all) do\n        @user_1 = User.create!(first_name: 'Fred', last_name: 'Flintstone')\n        @user_2 = User.create!(first_name: 'Wilma', last_name: 'Flintstone')\n      end\n\n      it 'should not affect html requested' do\n        get :index\n\n        expect(response.status).to eq 200\n        expect(response.media_type).to eq 'text/html'\n        expect(response.body).to eq 'Users!'\n      end\n\n      it 'should return a csv when requested' do\n        get :index, format: :csv\n\n        expect(response.status).to eq 200\n        expect(response.media_type).to eq 'text/csv'\n        expect(response.header['Content-Disposition']).to include('filename=\"data.csv\"')\n\n        expected_content = <<-CSV.gsub(/^\\s+/, '')\n        First name,Last name,Name\n        Fred,Flintstone,Fred Flintstone\n        Wilma,Flintstone,Wilma Flintstone\n        CSV\n\n        expect(response.body).to eq expected_content\n      end\n\n      describe 'with comma options' do\n        it 'should allow the style to be chosen from the renderer' do\n          # Must be passed in same format (string/symbol) eg:\n          # format.csv  { render User.all, :style => :shortened }\n\n          get :with_custom_style, format: :csv\n\n          expected_content = <<-CSV.gsub(/^\\s+/, '')\n          First name,Last name\n          Fred,Flintstone\n          Wilma,Flintstone\n          CSV\n\n          expect(response.body).to eq expected_content\n        end\n      end\n\n      describe 'with custom options' do # rubocop:disable Metrics/BlockLength\n        def is_rails_4?\n          Rails::VERSION::STRING =~ /^4.*/\n        end\n\n        def get_(name, **args)\n          if is_rails_4? && args[:params]\n            args.merge!(args[:params])\n            args.delete(:params)\n          end\n          get name, **args\n        end\n\n        it 'should allow a filename to be set' do\n          get_ :with_custom_options, format: :csv, params: { custom_options: { filename: 'my_custom_name' } }\n\n          expect(response.status).to eq 200\n          expect(response.media_type).to eq 'text/csv'\n          expect(response.header['Content-Disposition']).to include('filename=\"my_custom_name.csv\"')\n        end\n\n        it 'should allow a custom filename with spaces' do\n          require 'shellwords'\n          params = { custom_options: { filename: 'filename with a lot of spaces' } }\n          get_ :with_custom_options, format: :csv, params: params\n\n          expect(response.status).to eq 200\n          expect(response.media_type).to eq 'text/csv'\n          expect(response.header['Content-Disposition']).to include('filename=\"filename with a lot of spaces.csv\"')\n\n          filename_string = response.header['Content-Disposition'].split('=').last\n          # shellsplit honors quoted strings\n          expect(filename_string.shellsplit.length).to eq 1\n        end\n\n        it 'should allow a file extension to be set' do\n          get_ :with_custom_options, format: :csv, params: { custom_options: { extension: :txt } }\n\n          expect(response.status).to eq 200\n          expect(response.media_type).to eq 'text/csv'\n          expect(response.header['Content-Disposition']).to include('filename=\"data.txt\"')\n        end\n\n        it 'should allow mime type to be set' do\n          get_ :with_custom_options, format: :csv, params: { custom_options: { mime_type: 'text/plain' } }\n          expect(response.status).to eq 200\n          expect(response.media_type).to eq 'text/plain'\n        end\n\n        it 'should allow bom to be set' do\n          get_ :with_custom_options, format: :csv, params: { custom_options: { with_bom: true } }\n\n          expected_content = <<-CSV.gsub(/^\\s+/, '')\n          \\xEF\\xBB\\xBFFirst name,Last name,Name\n          Fred,Flintstone,Fred Flintstone\n          Wilma,Flintstone,Wilma Flintstone\n          CSV\n\n          expect(response.body). to eq expected_content\n        end\n\n        describe 'headers' do\n          it 'should allow toggling on' do\n            get_ :with_custom_options, format: :csv, params: { custom_options: { write_headers: 'true' } }\n\n            expect(response.status).to eq 200\n            expect(response.media_type).to eq 'text/csv'\n\n            expected_content = <<-CSV.gsub(/^\\s+/, '')\n            First name,Last name,Name\n            Fred,Flintstone,Fred Flintstone\n            Wilma,Flintstone,Wilma Flintstone\n            CSV\n\n            expect(response.body).to eq expected_content\n          end\n\n          it 'should allow toggling off' do\n            get_ :with_custom_options, format: :csv, params: { custom_options: { write_headers: false } }\n\n            expect(response.status).to eq 200\n            expect(response.media_type).to eq 'text/csv'\n\n            expected_content = <<-CSV.gsub(/^\\s+/, '')\n            Fred,Flintstone,Fred Flintstone\n            Wilma,Flintstone,Wilma Flintstone\n            CSV\n\n            expect(response.body).to eq expected_content\n          end\n        end\n\n        it 'should allow forcing of quotes' do\n          get_ :with_custom_options, format: :csv, params: { custom_options: { force_quotes: true } }\n\n          expect(response.status).to eq 200\n          expect(response.media_type).to eq 'text/csv'\n\n          expected_content = <<-CSV.gsub(/^\\s+/, '')\n          \"First name\",\"Last name\",\"Name\"\n          \"Fred\",\"Flintstone\",\"Fred Flintstone\"\n          \"Wilma\",\"Flintstone\",\"Wilma Flintstone\"\n          CSV\n\n          expect(response.body).to eq expected_content\n        end\n\n        it 'should allow combinations of options' do\n          params = {\n            custom_options: {\n              write_headers: false,\n              force_quotes: true,\n              col_sep: '||',\n              row_sep: \"ENDOFLINE\\n\"\n            }\n          }\n          get_ :with_custom_options, format: :csv, params: params\n\n          expect(response.status).to eq 200\n          expect(response.media_type).to eq 'text/csv'\n\n          expected_content = <<-CSV.gsub(/^\\s+/, '')\n          \"Fred\"||\"Flintstone\"||\"Fred Flintstone\"ENDOFLINE\n          \"Wilma\"||\"Flintstone\"||\"Wilma Flintstone\"ENDOFLINE\n          CSV\n\n          expect(response.body).to eq expected_content\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/non_rails_app/ruby_classes.rb",
    "content": "# frozen_string_literal: true\n\nclass Book\n  attr_accessor :name, :description, :isbn\n\n  def initialize(name, description, isbn)\n    @name = name\n    @description = description\n    @isbn = isbn\n  end\n\n  comma do\n    name 'Title'\n    description\n\n    isbn authority: :issuer\n    isbn number_10: 'ISBN-10'\n    isbn number_13: 'ISBN-13'\n  end\n\n  comma :brief do\n    name\n    description\n  end\nend\n\nclass Isbn\n  attr_accessor :number_10, :number_13\n\n  def initialize(isbn_10, isbn_13)\n    @number_10 = isbn_10\n    @number_13 = isbn_13\n  end\n\n  def authority\n    'ISBN'\n  end\nend\n"
  },
  {
    "path": "spec/rails_app/active_record/config.rb",
    "content": "# frozen_string_literal: true\n\nActiveRecord::Base.configurations = { 'test' => { 'adapter' => 'sqlite3', 'database' => ':memory:' } }\nActiveRecord::Base.establish_connection(:test)\n"
  },
  {
    "path": "spec/rails_app/active_record/models.rb",
    "content": "# frozen_string_literal: true\n\nclass Post < ActiveRecord::Base\n  has_one :user\n\n  comma do\n    title\n    description\n\n    user :full_name\n  end\nend\n\nclass User < ActiveRecord::Base\n  comma do\n    first_name\n    last_name\n    full_name 'Name'\n  end\n\n  comma :shortened do\n    first_name\n    last_name\n  end\n\n  def full_name\n    \"#{first_name} #{last_name}\".strip\n  end\nend\n\nclass CreateTables < ActiveRecord::Migration[4.2]\n  def self.up\n    create_table :users do |t|\n      t.string      :first_name\n      t.string      :last_name\n      t.timestamps\n    end\n\n    create_table :posts do |t|\n      t.references :user\n      t.string :title\n      t.string :description\n      t.timestamps\n    end\n  end\n\n  def self.down\n    drop_table :posts\n    drop_table :users\n  end\nend\nActiveRecord::Migration.verbose = false\nCreateTables.up\n"
  },
  {
    "path": "spec/rails_app/data_mapper/config.rb",
    "content": "# frozen_string_literal: true\n\nDataMapper.setup(:default, 'sqlite::memory:')\n"
  },
  {
    "path": "spec/rails_app/mongoid/config.rb",
    "content": "# frozen_string_literal: true\n\nMongoid.configure do |config|\n  config.sessions = {\n    default: {\n      hosts: ['localhost:27017'], database: 'comma_test'\n    }\n  }\nend\n"
  },
  {
    "path": "spec/rails_app/rails_app.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'action_controller/railtie'\nrequire 'action_view/railtie'\n\n# orm configs\nrequire 'rails_app/active_record/config' if defined?(ActiveRecord)\n\napp = CommaTestApp = Class.new(Rails::Application)\napp.config.secret_token = '6f6acf0443f74fd0aa8ff07a7c2fbe0a'\napp.config.session_store :cookie_store, key: '_rails_app_session'\napp.config.active_support.deprecation = :log\napp.config.eager_load = false\napp.config.root = File.dirname(__FILE__)\nRails.backtrace_cleaner.remove_silencers!\napp.initialize!\n\napp.routes.draw do\n  resources :users, only: [:index]\n  get 'with_custom_options', to: 'users#with_custom_options'\n  get 'with_custom_style', to: 'users#with_custom_style'\n  root to: 'users#index'\nend\n\n# models\nrequire 'rails_app/active_record/models' if defined?(ActiveRecord)\n\ndef is_rails_4?\n  Rails::VERSION::STRING =~ /^4.*/\nend\n\nif is_rails_4?\n  def symbolize_param_keys(params)\n    params.symbolize_keys\n  end\nelse\n  def symbolize_param_keys(params)\n    if params\n      params.to_unsafe_h.symbolize_keys\n    else\n      {}\n    end\n  end\nend\n\n# controllers\nclass ApplicationController < ActionController::Base; end\nclass UsersController < ApplicationController\n  def index\n    respond_to do |format|\n      format.html do\n        if is_rails_4?\n          render text: 'Users!'\n        else\n          render plain: 'Users!'\n        end\n      end\n      format.csv  { render csv: User.all }\n    end\n  end\n\n  def with_custom_options\n    render_options = { csv: User.all }.update(symbolize_param_keys(params[:custom_options]))\n\n    respond_to do |format|\n      format.csv  { render render_options }\n    end\n  end\n\n  def with_custom_style\n    respond_to do |format|\n      format.csv  { render csv: User.all, style: :shortened }\n    end\n  end\nend\n\n# helpers\nObject.const_set(:ApplicationHelper, Module.new)\n"
  },
  {
    "path": "spec/rails_app/tmp/.gitkeep",
    "content": ""
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'rubygems'\n$LOAD_PATH.unshift(File.expand_path(File.join('..', '..', 'lib'), __FILE__))\n\nrequire 'simplecov'\nrequire 'coveralls'\nSimpleCov.formatter = Coveralls::SimpleCov::Formatter\nif defined? Rails\n  SimpleCov.start('rails') do\n    add_filter %r{^/spec/comma/rails/data_mapper_collection_spec\\.rb$}\n    add_filter %r{^/spec/comma/rails/mongoid_spec\\.rb$}\n  end\nelse\n  SimpleCov.start do\n    add_filter %r{^/spec/comma/rails/data_mapper_collection_spec\\.rb}\n    add_filter %r{^/spec/comma/rails/mongoid_spec\\.rb}\n    add_filter %r{^/spec/controllers/}\n  end\nend\n\nrequire 'bundler/setup'\nBundler.require\n\nrequire 'rspec/active_model/mocks'\nrequire 'rspec/its'\n\nbegin\n  require 'rails'\nrescue LoadError\n  warn 'rails not loaded'\nend\n\n%w[data_mapper mongoid active_record].each do |orm|\n  begin\n    require orm\n    break\n  rescue LoadError\n    warn \"#{orm} not loaded\"\n  end\nend\n\nif defined? Rails\n  require 'rails_app/rails_app'\n  require 'rspec/rails'\nelse\n  require 'rails_app/data_mapper/config' if defined?(DataMapper)\n  require 'rails_app/mongoid/config' if defined?(Mongoid)\n  require 'rails_app/active_record/config' if defined?(ActiveRecord)\nend\n\nDir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |file| require file }\n\nrequire File.expand_path('../spec/non_rails_app/ruby_classes', __dir__)\n"
  }
]