├── .coveralls.yml ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .rubocop_todo.yml ├── .travis.yml ├── Appraisals ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── comma.gemspec ├── gemfiles ├── active6.0.6.gemfile ├── active6.0.6.gemfile.lock ├── active6.1.7.6.gemfile ├── active6.1.7.6.gemfile.lock ├── active7.0.8.gemfile ├── active7.0.8.gemfile.lock ├── active7.1.3.gemfile ├── active7.1.3.gemfile.lock ├── rails6.0.6.gemfile ├── rails6.0.6.gemfile.lock ├── rails6.1.7.6.gemfile ├── rails6.1.7.6.gemfile.lock ├── rails7.0.8.gemfile ├── rails7.0.8.gemfile.lock ├── rails7.1.3.gemfile └── rails7.1.3.gemfile.lock ├── init.rb ├── lib ├── comma.rb └── comma │ ├── array.rb │ ├── data_extractor.rb │ ├── data_mapper_collection.rb │ ├── extractor.rb │ ├── generator.rb │ ├── header_extractor.rb │ ├── mongoid.rb │ ├── object.rb │ ├── relation.rb │ └── version.rb └── spec ├── comma ├── comma_spec.rb ├── data_extractor_spec.rb ├── header_extractor_spec.rb └── rails │ ├── active_record_spec.rb │ ├── data_mapper_collection_spec.rb │ └── mongoid_spec.rb ├── controllers └── users_controller_spec.rb ├── non_rails_app └── ruby_classes.rb ├── rails_app ├── active_record │ ├── config.rb │ └── models.rb ├── data_mapper │ └── config.rb ├── mongoid │ └── config.rb ├── rails_app.rb └── tmp │ └── .gitkeep └── spec_helper.rb /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | strategy: 6 | matrix: 7 | ruby: ['3.0', '3.1', '3.2', '3.3'] 8 | 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'] 9 | runs-on: ubuntu-latest 10 | env: 11 | BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile 12 | RUBY_OPT: --disable=did_you_mean 13 | services: 14 | mongodb: 15 | image: mongo:4.4.10 16 | ports: 17 | - 8081:8081 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: ruby/setup-ruby@v1 21 | with: 22 | ruby-version: ${{ matrix.ruby }} 23 | bundler-cache: true 24 | cache-version: 1 25 | - run: bundle install --jobs 2 --retry 3 26 | - run: bundle exec rubocop -P 27 | - run: bundle exec rspec -f d spec 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/* 2 | .*.swp 3 | *~ 4 | .bundle 5 | spec/rails_app/log 6 | spec/rails_app/db/development.sqlite* 7 | .rbx/ 8 | .ruby-* 9 | coverage/* 10 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: .rubocop_todo.yml 2 | 3 | require: rubocop-performance 4 | 5 | AllCops: 6 | DisplayCopNames: true 7 | Exclude: 8 | - 'gemfiles/**/*' 9 | - 'vendor/**/*' 10 | NewCops: enable 11 | TargetRubyVersion: 2.5 12 | 13 | Layout/LineLength: 14 | IgnoreCopDirectives: true 15 | Max: 120 16 | 17 | Naming/FileName: 18 | Exclude: 19 | - 'Appraisals' 20 | 21 | Naming/VariableNumber: 22 | EnforcedStyle: snake_case 23 | 24 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2024-01-20 09:02:51 UTC using RuboCop version 1.30.1. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 1 10 | # This cop supports safe autocorrection (--autocorrect). 11 | # Configuration parameters: Include. 12 | # Include: **/*.gemspec 13 | Gemspec/DeprecatedAttributeAssignment: 14 | Exclude: 15 | - 'comma.gemspec' 16 | 17 | # Offense count: 1 18 | # This cop supports safe autocorrection (--autocorrect). 19 | # Configuration parameters: Include. 20 | # Include: **/*.gemspec 21 | Gemspec/RequireMFA: 22 | Exclude: 23 | - 'comma.gemspec' 24 | 25 | # Offense count: 1 26 | # Configuration parameters: Include. 27 | # Include: **/*.gemspec 28 | Gemspec/RequiredRubyVersion: 29 | Exclude: 30 | - 'comma.gemspec' 31 | 32 | # Offense count: 1 33 | # This cop supports safe autocorrection (--autocorrect). 34 | # Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, AllowAdjacentOneLineDefs, NumberOfEmptyLines. 35 | Layout/EmptyLineBetweenDefs: 36 | Exclude: 37 | - 'spec/rails_app/rails_app.rb' 38 | 39 | # Offense count: 4 40 | # This cop supports safe autocorrection (--autocorrect). 41 | # Configuration parameters: AllowAliasSyntax, AllowedMethods. 42 | # AllowedMethods: alias_method, public, protected, private 43 | Layout/EmptyLinesAroundAttributeAccessor: 44 | Exclude: 45 | - 'spec/comma/comma_spec.rb' 46 | 47 | # Offense count: 1 48 | # This cop supports safe autocorrection (--autocorrect). 49 | Layout/SpaceAroundMethodCallOperator: 50 | Exclude: 51 | - 'spec/controllers/users_controller_spec.rb' 52 | 53 | # Offense count: 17 54 | # Configuration parameters: AllowedMethods. 55 | # AllowedMethods: enums 56 | Lint/ConstantDefinitionInBlock: 57 | Exclude: 58 | - 'spec/comma/comma_spec.rb' 59 | - 'spec/comma/rails/active_record_spec.rb' 60 | - 'spec/comma/rails/data_mapper_collection_spec.rb' 61 | - 'spec/comma/rails/mongoid_spec.rb' 62 | 63 | # Offense count: 4 64 | # Configuration parameters: AllowComments, AllowEmptyLambdas. 65 | Lint/EmptyBlock: 66 | Exclude: 67 | - 'spec/comma/comma_spec.rb' 68 | - 'spec/comma/rails/data_mapper_collection_spec.rb' 69 | 70 | # Offense count: 1 71 | Lint/MissingSuper: 72 | Exclude: 73 | - 'spec/comma/comma_spec.rb' 74 | 75 | # Offense count: 1 76 | # This cop supports unsafe autocorrection (--autocorrect-all). 77 | Lint/NonDeterministicRequireOrder: 78 | Exclude: 79 | - 'spec/spec_helper.rb' 80 | 81 | # Offense count: 2 82 | # Configuration parameters: IgnoredMethods, CountRepeatedAttributes. 83 | Metrics/AbcSize: 84 | Max: 22 85 | 86 | # Offense count: 3 87 | # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. 88 | Metrics/MethodLength: 89 | Max: 16 90 | 91 | # Offense count: 2 92 | # Configuration parameters: EnforcedStyle, AllowedIdentifiers, AllowedPatterns. 93 | # SupportedStyles: snake_case, camelCase 94 | Naming/VariableName: 95 | Exclude: 96 | - 'spec/comma/comma_spec.rb' 97 | 98 | # Offense count: 14 99 | # This cop supports safe autocorrection (--autocorrect). 100 | # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. 101 | # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces 102 | # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object 103 | # FunctionalMethods: let, let!, subject, watch 104 | # IgnoredMethods: lambda, proc, it 105 | Style/BlockDelimiters: 106 | Exclude: 107 | - 'spec/comma/comma_spec.rb' 108 | - 'spec/comma/data_extractor_spec.rb' 109 | - 'spec/comma/header_extractor_spec.rb' 110 | 111 | # Offense count: 11 112 | # Configuration parameters: AllowedConstants. 113 | Style/Documentation: 114 | Exclude: 115 | - 'spec/**/*' 116 | - 'test/**/*' 117 | - 'lib/comma/array.rb' 118 | - 'lib/comma/data_extractor.rb' 119 | - 'lib/comma/data_mapper_collection.rb' 120 | - 'lib/comma/extractor.rb' 121 | - 'lib/comma/generator.rb' 122 | - 'lib/comma/header_extractor.rb' 123 | - 'lib/comma/mongoid.rb' 124 | - 'lib/comma/object.rb' 125 | - 'lib/comma/relation.rb' 126 | 127 | # Offense count: 3 128 | Style/MissingRespondToMissing: 129 | Exclude: 130 | - 'lib/comma/data_extractor.rb' 131 | - 'lib/comma/header_extractor.rb' 132 | 133 | # Offense count: 1 134 | # This cop supports safe autocorrection (--autocorrect). 135 | Style/RedundantBegin: 136 | Exclude: 137 | - 'spec/spec_helper.rb' 138 | 139 | # Offense count: 6 140 | # This cop supports unsafe autocorrection (--autocorrect-all). 141 | # Configuration parameters: Mode. 142 | Style/StringConcatenation: 143 | Exclude: 144 | - 'spec/comma/comma_spec.rb' 145 | - 'spec/comma/rails/active_record_spec.rb' 146 | - 'spec/spec_helper.rb' 147 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | services: mongodb 4 | 5 | language: ruby 6 | cache: bundler 7 | rvm: 8 | - 2.4.10 9 | - 2.5.8 10 | - 2.6.6 11 | - 2.7.2 12 | gemfile: 13 | - gemfiles/active5.0.7.2.gemfile 14 | - gemfiles/active5.1.7.gemfile 15 | - gemfiles/active5.2.4.3.gemfile 16 | - gemfiles/active6.0.3.1.gemfile 17 | - gemfiles/active6.1.0.gemfile 18 | - gemfiles/rails5.0.7.2.gemfile 19 | - gemfiles/rails5.1.7.gemfile 20 | - gemfiles/rails5.2.4.3.gemfile 21 | - gemfiles/rails6.0.3.1.gemfile 22 | - gemfiles/rails6.1.0.gemfile 23 | - gemfiles/railsedge.gemfile 24 | matrix: 25 | exclude: 26 | - rvm: 2.4.10 27 | gemfile: gemfiles/active6.0.3.1.gemfile 28 | - rvm: 2.4.10 29 | gemfile: gemfiles/active6.1.0.gemfile 30 | - rvm: 2.4.10 31 | gemfile: gemfiles/rails6.0.3.1.gemfile 32 | - rvm: 2.4.10 33 | gemfile: gemfiles/rails6.1.0.gemfile 34 | - rvm: 2.4.10 35 | gemfile: gemfiles/railsedge.gemfile 36 | - rvm: 2.5.8 37 | gemfile: gemfiles/railsedge.gemfile 38 | fast_finish: true 39 | before_install: 40 | - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true 41 | - gem install bundler -v '< 2' 42 | script: 43 | - bundle exec rubocop -P 44 | - bundle exec rspec -f d spec 45 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | appraise 'rails6.0.6' do 4 | gem 'rails', '6.0.6' 5 | gem 'rspec-rails' 6 | gem 'test-unit' 7 | end 8 | 9 | appraise 'active6.0.6' do 10 | gem 'activesupport', '6.0.6' 11 | gem 'activerecord', '6.0.6' 12 | end 13 | 14 | appraise 'rails6.1.7.6' do 15 | gem 'rails', '6.1.7.6' 16 | gem 'rspec-rails' 17 | gem 'test-unit' 18 | end 19 | 20 | appraise 'active6.1.7.6' do 21 | gem 'activesupport', '6.1.7.6' 22 | gem 'activerecord', '6.1.7.6' 23 | end 24 | 25 | appraise 'rails7.0.8' do 26 | gem 'rails', '7.0.8' 27 | gem 'rspec-rails' 28 | end 29 | 30 | appraise 'active7.0.8' do 31 | gem 'activesupport', '7.0.8' 32 | gem 'activerecord', '7.0.8' 33 | end 34 | 35 | appraise 'rails7.1.3' do 36 | gem 'rails', '7.1.3' 37 | gem 'rspec-rails' 38 | end 39 | 40 | appraise 'active7.1.3' do 41 | gem 'activesupport', '7.1.3' 42 | gem 'activerecord', '7.1.3' 43 | end 44 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | 7 | gem 'coveralls', require: false 8 | gem 'rubocop', '~> 1.30.0', require: false 9 | gem 'rubocop-performance', require: false 10 | gem 'sqlite3' 11 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | comma (4.8.0) 5 | activesupport (>= 4.2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | activemodel (7.1.3) 11 | activesupport (= 7.1.3) 12 | activesupport (7.1.3) 13 | base64 14 | bigdecimal 15 | concurrent-ruby (~> 1.0, >= 1.0.2) 16 | connection_pool (>= 2.2.5) 17 | drb 18 | i18n (>= 1.6, < 2) 19 | minitest (>= 5.1) 20 | mutex_m 21 | tzinfo (~> 2.0) 22 | appraisal (1.0.3) 23 | bundler 24 | rake 25 | thor (>= 0.14.0) 26 | ast (2.4.2) 27 | base64 (0.2.0) 28 | bigdecimal (3.1.6) 29 | concurrent-ruby (1.2.3) 30 | connection_pool (2.4.1) 31 | coveralls (0.8.23) 32 | json (>= 1.8, < 3) 33 | simplecov (~> 0.16.1) 34 | term-ansicolor (~> 1.3) 35 | thor (>= 0.19.4, < 2.0) 36 | tins (~> 1.6) 37 | diff-lcs (1.2.5) 38 | docile (1.4.0) 39 | drb (2.2.0) 40 | ruby2_keywords 41 | i18n (1.14.1) 42 | concurrent-ruby (~> 1.0) 43 | json (2.7.1) 44 | mini_portile2 (2.8.5) 45 | minitest (5.14.4) 46 | mutex_m (0.2.0) 47 | parallel (1.24.0) 48 | parser (3.3.0.4) 49 | ast (~> 2.4.1) 50 | racc 51 | racc (1.7.3) 52 | rainbow (3.1.1) 53 | rake (13.0.1) 54 | regexp_parser (2.9.0) 55 | rexml (3.2.8) 56 | strscan (>= 3.0.9) 57 | rspec (3.5.0) 58 | rspec-core (~> 3.5.0) 59 | rspec-expectations (~> 3.5.0) 60 | rspec-mocks (~> 3.5.0) 61 | rspec-activemodel-mocks (1.1.0) 62 | activemodel (>= 3.0) 63 | activesupport (>= 3.0) 64 | rspec-mocks (>= 2.99, < 4.0) 65 | rspec-core (3.5.2) 66 | rspec-support (~> 3.5.0) 67 | rspec-expectations (3.5.0) 68 | diff-lcs (>= 1.2.0, < 2.0) 69 | rspec-support (~> 3.5.0) 70 | rspec-its (1.2.0) 71 | rspec-core (>= 3.0.0) 72 | rspec-expectations (>= 3.0.0) 73 | rspec-mocks (3.5.0) 74 | diff-lcs (>= 1.2.0, < 2.0) 75 | rspec-support (~> 3.5.0) 76 | rspec-support (3.5.0) 77 | rubocop (1.30.1) 78 | parallel (~> 1.10) 79 | parser (>= 3.1.0.0) 80 | rainbow (>= 2.2.2, < 4.0) 81 | regexp_parser (>= 1.8, < 3.0) 82 | rexml (>= 3.2.5, < 4.0) 83 | rubocop-ast (>= 1.18.0, < 2.0) 84 | ruby-progressbar (~> 1.7) 85 | unicode-display_width (>= 1.4.0, < 3.0) 86 | rubocop-ast (1.30.0) 87 | parser (>= 3.2.1.0) 88 | rubocop-performance (1.10.2) 89 | rubocop (>= 0.90.0, < 2.0) 90 | rubocop-ast (>= 0.4.0) 91 | ruby-progressbar (1.13.0) 92 | ruby2_keywords (0.0.5) 93 | simplecov (0.16.1) 94 | docile (~> 1.1) 95 | json (>= 1.8, < 3) 96 | simplecov-html (~> 0.10.0) 97 | simplecov-html (0.10.2) 98 | sqlite3 (1.7.0) 99 | mini_portile2 (~> 2.8.0) 100 | strscan (3.1.0) 101 | sync (0.5.0) 102 | term-ansicolor (1.7.1) 103 | tins (~> 1.0) 104 | thor (1.3.0) 105 | tins (1.32.1) 106 | sync 107 | tzinfo (2.0.6) 108 | concurrent-ruby (~> 1.0) 109 | unicode-display_width (2.5.0) 110 | 111 | PLATFORMS 112 | ruby 113 | 114 | DEPENDENCIES 115 | appraisal (~> 1.0.0) 116 | comma! 117 | coveralls 118 | minitest (= 5.14.4) 119 | rake (~> 13.0.1) 120 | rspec (~> 3.5.0) 121 | rspec-activemodel-mocks 122 | rspec-its 123 | rubocop (~> 1.30.0) 124 | rubocop-performance 125 | sqlite3 126 | 127 | BUNDLED WITH 128 | 2.5.5 129 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Marcus Crafter 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Comma 2 | 3 | A library to generate comma seperated value (CSV) for Ruby objects like ActiveRecord and Array 4 | 5 | [![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) 6 | 7 | ## Getting Started 8 | 9 | ### Prerequisites 10 | 11 | You need to use ruby 3.0 or later. If you generate CSV from ActiveRecord models, you need to have ActiveRecord 6.0 or later. 12 | 13 | ### Installing 14 | 15 | Comma is distributed as a gem, best installed via Bundler. 16 | 17 | Include the gem in your Gemfile: 18 | 19 | ```ruby 20 | gem 'comma', '~> 4.8.0' 21 | ``` 22 | 23 | Or, if you want to live life on the edge, you can get master from the main comma repository: 24 | 25 | ```ruby 26 | gem 'comma', git: 'git://github.com/comma-csv/comma.git' 27 | ``` 28 | 29 | Then, run `bundle install`. 30 | 31 | ### Usage 32 | 33 | See [this page](https://github.com/comma-csv/comma/wiki) for usages. 34 | 35 | ## Running the tests 36 | 37 | To run the test suite across multiple gem file sets, we're using [Appraisal](https://github.com/thoughtbot/appraisal), use the following commands: 38 | 39 | ```sh 40 | $ bundle exec appraisal install 41 | $ bundle exec appraisal rake spec 42 | 43 | ``` 44 | 45 | ## Contributing 46 | 47 | ## Versioning 48 | 49 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/comma-csv/comma/tags). 50 | 51 | ## Authors 52 | 53 | * Marcus Crafter - Initial work 54 | * Tom Meier - Initial work 55 | * Eito Katagiri 56 | 57 | ## License 58 | 59 | This project is licensed under the MIT License - see the [MIT-LICENSE](https://github.com/comma-csv/comma/blob/master/MIT-LICENSE) file fore details. 60 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/setup' 4 | require 'bundler/gem_tasks' 5 | 6 | require 'rspec/core/rake_task' 7 | RSpec::Core::RakeTask.new(:spec) do |spec| 8 | spec.pattern = FileList['spec/**/*_spec.rb'] 9 | end 10 | 11 | require 'rubocop/rake_task' 12 | RuboCop::RakeTask.new 13 | 14 | task default: :spec 15 | -------------------------------------------------------------------------------- /comma.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push File.expand_path('lib', __dir__) 4 | require 'comma/version' 5 | 6 | Gem::Specification.new do |s| 7 | s.name = 'comma' 8 | s.version = Comma::VERSION 9 | s.authors = ['Marcus Crafter', 'Tom Meier'] 10 | s.email = ['crafterm@redartisan.com', 'tom@venombytes.com'] 11 | s.homepage = 'http://github.com/comma-csv/comma' 12 | s.summary = %(Ruby Comma Seperated Values generation library) 13 | s.description = %(Ruby Comma Seperated Values generation library) 14 | 15 | s.files = `git ls-files`.split("\n") 16 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 17 | s.require_paths = ['lib'] 18 | 19 | s.licenses = ['MIT'] 20 | 21 | s.add_dependency 'activesupport', '>= 4.2.0' 22 | 23 | s.add_development_dependency 'appraisal', ['~> 1.0.0'] 24 | s.add_development_dependency 'minitest', '5.14.4' 25 | s.add_development_dependency 'rake', '~> 13.0.1' 26 | s.add_development_dependency 'rspec', ['~> 3.5.0'] 27 | s.add_development_dependency 'rspec-activemodel-mocks' 28 | s.add_development_dependency 'rspec-its' 29 | end 30 | -------------------------------------------------------------------------------- /gemfiles/active6.0.6.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "coveralls", :require => false 6 | gem "rubocop", "~> 1.30.0", :require => false 7 | gem "rubocop-performance", :require => false 8 | gem "sqlite3" 9 | gem "activesupport", "6.0.6" 10 | gem "activerecord", "6.0.6" 11 | 12 | gemspec :path => "../" 13 | -------------------------------------------------------------------------------- /gemfiles/active6.0.6.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | comma (4.8.0) 5 | activesupport (>= 4.2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | activemodel (6.0.6) 11 | activesupport (= 6.0.6) 12 | activerecord (6.0.6) 13 | activemodel (= 6.0.6) 14 | activesupport (= 6.0.6) 15 | activesupport (6.0.6) 16 | concurrent-ruby (~> 1.0, >= 1.0.2) 17 | i18n (>= 0.7, < 2) 18 | minitest (~> 5.1) 19 | tzinfo (~> 1.1) 20 | zeitwerk (~> 2.2, >= 2.2.2) 21 | appraisal (1.0.3) 22 | bundler 23 | rake 24 | thor (>= 0.14.0) 25 | ast (2.4.2) 26 | concurrent-ruby (1.2.3) 27 | coveralls (0.8.23) 28 | json (>= 1.8, < 3) 29 | simplecov (~> 0.16.1) 30 | term-ansicolor (~> 1.3) 31 | thor (>= 0.19.4, < 2.0) 32 | tins (~> 1.6) 33 | diff-lcs (1.5.0) 34 | docile (1.4.0) 35 | i18n (1.14.1) 36 | concurrent-ruby (~> 1.0) 37 | json (2.7.1) 38 | mini_portile2 (2.8.5) 39 | minitest (5.14.4) 40 | parallel (1.24.0) 41 | parser (3.3.0.4) 42 | ast (~> 2.4.1) 43 | racc 44 | racc (1.7.3) 45 | rainbow (3.1.1) 46 | rake (13.0.6) 47 | regexp_parser (2.9.0) 48 | rexml (3.2.6) 49 | rspec (3.5.0) 50 | rspec-core (~> 3.5.0) 51 | rspec-expectations (~> 3.5.0) 52 | rspec-mocks (~> 3.5.0) 53 | rspec-activemodel-mocks (1.2.0) 54 | activemodel (>= 3.0) 55 | activesupport (>= 3.0) 56 | rspec-mocks (>= 2.99, < 4.0) 57 | rspec-core (3.5.4) 58 | rspec-support (~> 3.5.0) 59 | rspec-expectations (3.5.0) 60 | diff-lcs (>= 1.2.0, < 2.0) 61 | rspec-support (~> 3.5.0) 62 | rspec-its (1.3.0) 63 | rspec-core (>= 3.0.0) 64 | rspec-expectations (>= 3.0.0) 65 | rspec-mocks (3.5.0) 66 | diff-lcs (>= 1.2.0, < 2.0) 67 | rspec-support (~> 3.5.0) 68 | rspec-support (3.5.0) 69 | rubocop (1.30.1) 70 | parallel (~> 1.10) 71 | parser (>= 3.1.0.0) 72 | rainbow (>= 2.2.2, < 4.0) 73 | regexp_parser (>= 1.8, < 3.0) 74 | rexml (>= 3.2.5, < 4.0) 75 | rubocop-ast (>= 1.18.0, < 2.0) 76 | ruby-progressbar (~> 1.7) 77 | unicode-display_width (>= 1.4.0, < 3.0) 78 | rubocop-ast (1.30.0) 79 | parser (>= 3.2.1.0) 80 | rubocop-performance (1.19.1) 81 | rubocop (>= 1.7.0, < 2.0) 82 | rubocop-ast (>= 0.4.0) 83 | ruby-progressbar (1.13.0) 84 | simplecov (0.16.1) 85 | docile (~> 1.1) 86 | json (>= 1.8, < 3) 87 | simplecov-html (~> 0.10.0) 88 | simplecov-html (0.10.2) 89 | sqlite3 (1.7.0) 90 | mini_portile2 (~> 2.8.0) 91 | sync (0.5.0) 92 | term-ansicolor (1.7.1) 93 | tins (~> 1.0) 94 | thor (1.3.0) 95 | thread_safe (0.3.6) 96 | tins (1.32.1) 97 | sync 98 | tzinfo (1.2.11) 99 | thread_safe (~> 0.1) 100 | unicode-display_width (2.5.0) 101 | zeitwerk (2.6.12) 102 | 103 | PLATFORMS 104 | ruby 105 | 106 | DEPENDENCIES 107 | activerecord (= 6.0.6) 108 | activesupport (= 6.0.6) 109 | appraisal (~> 1.0.0) 110 | comma! 111 | coveralls 112 | minitest (= 5.14.4) 113 | rake (~> 13.0.1) 114 | rspec (~> 3.5.0) 115 | rspec-activemodel-mocks 116 | rspec-its 117 | rubocop (~> 1.30.0) 118 | rubocop-performance 119 | sqlite3 120 | 121 | BUNDLED WITH 122 | 2.5.5 123 | -------------------------------------------------------------------------------- /gemfiles/active6.1.7.6.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "coveralls", :require => false 6 | gem "rubocop", "~> 1.30.0", :require => false 7 | gem "rubocop-performance", :require => false 8 | gem "sqlite3" 9 | gem "activesupport", "6.1.7.6" 10 | gem "activerecord", "6.1.7.6" 11 | 12 | gemspec :path => "../" 13 | -------------------------------------------------------------------------------- /gemfiles/active6.1.7.6.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | comma (4.8.0) 5 | activesupport (>= 4.2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | activemodel (6.1.7.6) 11 | activesupport (= 6.1.7.6) 12 | activerecord (6.1.7.6) 13 | activemodel (= 6.1.7.6) 14 | activesupport (= 6.1.7.6) 15 | activesupport (6.1.7.6) 16 | concurrent-ruby (~> 1.0, >= 1.0.2) 17 | i18n (>= 1.6, < 2) 18 | minitest (>= 5.1) 19 | tzinfo (~> 2.0) 20 | zeitwerk (~> 2.3) 21 | appraisal (1.0.3) 22 | bundler 23 | rake 24 | thor (>= 0.14.0) 25 | ast (2.4.2) 26 | concurrent-ruby (1.2.3) 27 | coveralls (0.8.23) 28 | json (>= 1.8, < 3) 29 | simplecov (~> 0.16.1) 30 | term-ansicolor (~> 1.3) 31 | thor (>= 0.19.4, < 2.0) 32 | tins (~> 1.6) 33 | diff-lcs (1.5.0) 34 | docile (1.4.0) 35 | i18n (1.14.1) 36 | concurrent-ruby (~> 1.0) 37 | json (2.7.1) 38 | mini_portile2 (2.8.5) 39 | minitest (5.14.4) 40 | parallel (1.24.0) 41 | parser (3.3.0.4) 42 | ast (~> 2.4.1) 43 | racc 44 | racc (1.7.3) 45 | rainbow (3.1.1) 46 | rake (13.0.6) 47 | regexp_parser (2.9.0) 48 | rexml (3.2.6) 49 | rspec (3.5.0) 50 | rspec-core (~> 3.5.0) 51 | rspec-expectations (~> 3.5.0) 52 | rspec-mocks (~> 3.5.0) 53 | rspec-activemodel-mocks (1.2.0) 54 | activemodel (>= 3.0) 55 | activesupport (>= 3.0) 56 | rspec-mocks (>= 2.99, < 4.0) 57 | rspec-core (3.5.4) 58 | rspec-support (~> 3.5.0) 59 | rspec-expectations (3.5.0) 60 | diff-lcs (>= 1.2.0, < 2.0) 61 | rspec-support (~> 3.5.0) 62 | rspec-its (1.3.0) 63 | rspec-core (>= 3.0.0) 64 | rspec-expectations (>= 3.0.0) 65 | rspec-mocks (3.5.0) 66 | diff-lcs (>= 1.2.0, < 2.0) 67 | rspec-support (~> 3.5.0) 68 | rspec-support (3.5.0) 69 | rubocop (1.30.1) 70 | parallel (~> 1.10) 71 | parser (>= 3.1.0.0) 72 | rainbow (>= 2.2.2, < 4.0) 73 | regexp_parser (>= 1.8, < 3.0) 74 | rexml (>= 3.2.5, < 4.0) 75 | rubocop-ast (>= 1.18.0, < 2.0) 76 | ruby-progressbar (~> 1.7) 77 | unicode-display_width (>= 1.4.0, < 3.0) 78 | rubocop-ast (1.30.0) 79 | parser (>= 3.2.1.0) 80 | rubocop-performance (1.19.1) 81 | rubocop (>= 1.7.0, < 2.0) 82 | rubocop-ast (>= 0.4.0) 83 | ruby-progressbar (1.13.0) 84 | simplecov (0.16.1) 85 | docile (~> 1.1) 86 | json (>= 1.8, < 3) 87 | simplecov-html (~> 0.10.0) 88 | simplecov-html (0.10.2) 89 | sqlite3 (1.7.0) 90 | mini_portile2 (~> 2.8.0) 91 | sync (0.5.0) 92 | term-ansicolor (1.7.1) 93 | tins (~> 1.0) 94 | thor (1.3.0) 95 | tins (1.32.1) 96 | sync 97 | tzinfo (2.0.6) 98 | concurrent-ruby (~> 1.0) 99 | unicode-display_width (2.5.0) 100 | zeitwerk (2.6.12) 101 | 102 | PLATFORMS 103 | ruby 104 | 105 | DEPENDENCIES 106 | activerecord (= 6.1.7.6) 107 | activesupport (= 6.1.7.6) 108 | appraisal (~> 1.0.0) 109 | comma! 110 | coveralls 111 | minitest (= 5.14.4) 112 | rake (~> 13.0.1) 113 | rspec (~> 3.5.0) 114 | rspec-activemodel-mocks 115 | rspec-its 116 | rubocop (~> 1.30.0) 117 | rubocop-performance 118 | sqlite3 119 | 120 | BUNDLED WITH 121 | 2.5.5 122 | -------------------------------------------------------------------------------- /gemfiles/active7.0.8.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "coveralls", :require => false 6 | gem "rubocop", "~> 1.30.0", :require => false 7 | gem "rubocop-performance", :require => false 8 | gem "sqlite3" 9 | gem "activesupport", "7.0.8" 10 | gem "activerecord", "7.0.8" 11 | 12 | gemspec :path => "../" 13 | -------------------------------------------------------------------------------- /gemfiles/active7.0.8.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | comma (4.8.0) 5 | activesupport (>= 4.2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | activemodel (7.0.8) 11 | activesupport (= 7.0.8) 12 | activerecord (7.0.8) 13 | activemodel (= 7.0.8) 14 | activesupport (= 7.0.8) 15 | activesupport (7.0.8) 16 | concurrent-ruby (~> 1.0, >= 1.0.2) 17 | i18n (>= 1.6, < 2) 18 | minitest (>= 5.1) 19 | tzinfo (~> 2.0) 20 | appraisal (1.0.3) 21 | bundler 22 | rake 23 | thor (>= 0.14.0) 24 | ast (2.4.2) 25 | concurrent-ruby (1.2.3) 26 | coveralls (0.8.23) 27 | json (>= 1.8, < 3) 28 | simplecov (~> 0.16.1) 29 | term-ansicolor (~> 1.3) 30 | thor (>= 0.19.4, < 2.0) 31 | tins (~> 1.6) 32 | diff-lcs (1.5.0) 33 | docile (1.4.0) 34 | i18n (1.14.1) 35 | concurrent-ruby (~> 1.0) 36 | json (2.7.1) 37 | mini_portile2 (2.8.5) 38 | minitest (5.14.4) 39 | parallel (1.24.0) 40 | parser (3.3.0.4) 41 | ast (~> 2.4.1) 42 | racc 43 | racc (1.7.3) 44 | rainbow (3.1.1) 45 | rake (13.0.6) 46 | regexp_parser (2.9.0) 47 | rexml (3.2.6) 48 | rspec (3.5.0) 49 | rspec-core (~> 3.5.0) 50 | rspec-expectations (~> 3.5.0) 51 | rspec-mocks (~> 3.5.0) 52 | rspec-activemodel-mocks (1.2.0) 53 | activemodel (>= 3.0) 54 | activesupport (>= 3.0) 55 | rspec-mocks (>= 2.99, < 4.0) 56 | rspec-core (3.5.4) 57 | rspec-support (~> 3.5.0) 58 | rspec-expectations (3.5.0) 59 | diff-lcs (>= 1.2.0, < 2.0) 60 | rspec-support (~> 3.5.0) 61 | rspec-its (1.3.0) 62 | rspec-core (>= 3.0.0) 63 | rspec-expectations (>= 3.0.0) 64 | rspec-mocks (3.5.0) 65 | diff-lcs (>= 1.2.0, < 2.0) 66 | rspec-support (~> 3.5.0) 67 | rspec-support (3.5.0) 68 | rubocop (1.30.1) 69 | parallel (~> 1.10) 70 | parser (>= 3.1.0.0) 71 | rainbow (>= 2.2.2, < 4.0) 72 | regexp_parser (>= 1.8, < 3.0) 73 | rexml (>= 3.2.5, < 4.0) 74 | rubocop-ast (>= 1.18.0, < 2.0) 75 | ruby-progressbar (~> 1.7) 76 | unicode-display_width (>= 1.4.0, < 3.0) 77 | rubocop-ast (1.30.0) 78 | parser (>= 3.2.1.0) 79 | rubocop-performance (1.19.1) 80 | rubocop (>= 1.7.0, < 2.0) 81 | rubocop-ast (>= 0.4.0) 82 | ruby-progressbar (1.13.0) 83 | simplecov (0.16.1) 84 | docile (~> 1.1) 85 | json (>= 1.8, < 3) 86 | simplecov-html (~> 0.10.0) 87 | simplecov-html (0.10.2) 88 | sqlite3 (1.7.0) 89 | mini_portile2 (~> 2.8.0) 90 | sync (0.5.0) 91 | term-ansicolor (1.7.1) 92 | tins (~> 1.0) 93 | thor (1.3.0) 94 | tins (1.32.1) 95 | sync 96 | tzinfo (2.0.6) 97 | concurrent-ruby (~> 1.0) 98 | unicode-display_width (2.5.0) 99 | 100 | PLATFORMS 101 | ruby 102 | 103 | DEPENDENCIES 104 | activerecord (= 7.0.8) 105 | activesupport (= 7.0.8) 106 | appraisal (~> 1.0.0) 107 | comma! 108 | coveralls 109 | minitest (= 5.14.4) 110 | rake (~> 13.0.1) 111 | rspec (~> 3.5.0) 112 | rspec-activemodel-mocks 113 | rspec-its 114 | rubocop (~> 1.30.0) 115 | rubocop-performance 116 | sqlite3 117 | 118 | BUNDLED WITH 119 | 2.5.5 120 | -------------------------------------------------------------------------------- /gemfiles/active7.1.3.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "coveralls", :require => false 6 | gem "rubocop", "~> 1.30.0", :require => false 7 | gem "rubocop-performance", :require => false 8 | gem "sqlite3" 9 | gem "activesupport", "7.1.3" 10 | gem "activerecord", "7.1.3" 11 | 12 | gemspec :path => "../" 13 | -------------------------------------------------------------------------------- /gemfiles/active7.1.3.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | comma (4.8.0) 5 | activesupport (>= 4.2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | activemodel (7.1.3) 11 | activesupport (= 7.1.3) 12 | activerecord (7.1.3) 13 | activemodel (= 7.1.3) 14 | activesupport (= 7.1.3) 15 | timeout (>= 0.4.0) 16 | activesupport (7.1.3) 17 | base64 18 | bigdecimal 19 | concurrent-ruby (~> 1.0, >= 1.0.2) 20 | connection_pool (>= 2.2.5) 21 | drb 22 | i18n (>= 1.6, < 2) 23 | minitest (>= 5.1) 24 | mutex_m 25 | tzinfo (~> 2.0) 26 | appraisal (1.0.3) 27 | bundler 28 | rake 29 | thor (>= 0.14.0) 30 | ast (2.4.2) 31 | base64 (0.2.0) 32 | bigdecimal (3.1.6) 33 | concurrent-ruby (1.2.3) 34 | connection_pool (2.4.1) 35 | coveralls (0.8.23) 36 | json (>= 1.8, < 3) 37 | simplecov (~> 0.16.1) 38 | term-ansicolor (~> 1.3) 39 | thor (>= 0.19.4, < 2.0) 40 | tins (~> 1.6) 41 | diff-lcs (1.5.0) 42 | docile (1.4.0) 43 | drb (2.2.0) 44 | ruby2_keywords 45 | i18n (1.14.1) 46 | concurrent-ruby (~> 1.0) 47 | json (2.7.1) 48 | mini_portile2 (2.8.5) 49 | minitest (5.14.4) 50 | mutex_m (0.2.0) 51 | parallel (1.24.0) 52 | parser (3.3.0.4) 53 | ast (~> 2.4.1) 54 | racc 55 | racc (1.7.3) 56 | rainbow (3.1.1) 57 | rake (13.0.6) 58 | regexp_parser (2.9.0) 59 | rexml (3.2.6) 60 | rspec (3.5.0) 61 | rspec-core (~> 3.5.0) 62 | rspec-expectations (~> 3.5.0) 63 | rspec-mocks (~> 3.5.0) 64 | rspec-activemodel-mocks (1.2.0) 65 | activemodel (>= 3.0) 66 | activesupport (>= 3.0) 67 | rspec-mocks (>= 2.99, < 4.0) 68 | rspec-core (3.5.4) 69 | rspec-support (~> 3.5.0) 70 | rspec-expectations (3.5.0) 71 | diff-lcs (>= 1.2.0, < 2.0) 72 | rspec-support (~> 3.5.0) 73 | rspec-its (1.3.0) 74 | rspec-core (>= 3.0.0) 75 | rspec-expectations (>= 3.0.0) 76 | rspec-mocks (3.5.0) 77 | diff-lcs (>= 1.2.0, < 2.0) 78 | rspec-support (~> 3.5.0) 79 | rspec-support (3.5.0) 80 | rubocop (1.30.1) 81 | parallel (~> 1.10) 82 | parser (>= 3.1.0.0) 83 | rainbow (>= 2.2.2, < 4.0) 84 | regexp_parser (>= 1.8, < 3.0) 85 | rexml (>= 3.2.5, < 4.0) 86 | rubocop-ast (>= 1.18.0, < 2.0) 87 | ruby-progressbar (~> 1.7) 88 | unicode-display_width (>= 1.4.0, < 3.0) 89 | rubocop-ast (1.30.0) 90 | parser (>= 3.2.1.0) 91 | rubocop-performance (1.19.1) 92 | rubocop (>= 1.7.0, < 2.0) 93 | rubocop-ast (>= 0.4.0) 94 | ruby-progressbar (1.13.0) 95 | ruby2_keywords (0.0.5) 96 | simplecov (0.16.1) 97 | docile (~> 1.1) 98 | json (>= 1.8, < 3) 99 | simplecov-html (~> 0.10.0) 100 | simplecov-html (0.10.2) 101 | sqlite3 (1.7.0) 102 | mini_portile2 (~> 2.8.0) 103 | sync (0.5.0) 104 | term-ansicolor (1.7.1) 105 | tins (~> 1.0) 106 | thor (1.3.0) 107 | timeout (0.4.1) 108 | tins (1.32.1) 109 | sync 110 | tzinfo (2.0.6) 111 | concurrent-ruby (~> 1.0) 112 | unicode-display_width (2.5.0) 113 | 114 | PLATFORMS 115 | ruby 116 | 117 | DEPENDENCIES 118 | activerecord (= 7.1.3) 119 | activesupport (= 7.1.3) 120 | appraisal (~> 1.0.0) 121 | comma! 122 | coveralls 123 | minitest (= 5.14.4) 124 | rake (~> 13.0.1) 125 | rspec (~> 3.5.0) 126 | rspec-activemodel-mocks 127 | rspec-its 128 | rubocop (~> 1.30.0) 129 | rubocop-performance 130 | sqlite3 131 | 132 | BUNDLED WITH 133 | 2.5.5 134 | -------------------------------------------------------------------------------- /gemfiles/rails6.0.6.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "coveralls", :require => false 6 | gem "rubocop", "~> 1.30.0", :require => false 7 | gem "rubocop-performance", :require => false 8 | gem "sqlite3" 9 | gem "rails", "6.0.6" 10 | gem "rspec-rails" 11 | gem "test-unit" 12 | 13 | gemspec :path => "../" 14 | -------------------------------------------------------------------------------- /gemfiles/rails6.0.6.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | comma (4.8.0) 5 | activesupport (>= 4.2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | actioncable (6.0.6) 11 | actionpack (= 6.0.6) 12 | nio4r (~> 2.0) 13 | websocket-driver (>= 0.6.1) 14 | actionmailbox (6.0.6) 15 | actionpack (= 6.0.6) 16 | activejob (= 6.0.6) 17 | activerecord (= 6.0.6) 18 | activestorage (= 6.0.6) 19 | activesupport (= 6.0.6) 20 | mail (>= 2.7.1) 21 | actionmailer (6.0.6) 22 | actionpack (= 6.0.6) 23 | actionview (= 6.0.6) 24 | activejob (= 6.0.6) 25 | mail (~> 2.5, >= 2.5.4) 26 | rails-dom-testing (~> 2.0) 27 | actionpack (6.0.6) 28 | actionview (= 6.0.6) 29 | activesupport (= 6.0.6) 30 | rack (~> 2.0, >= 2.0.8) 31 | rack-test (>= 0.6.3) 32 | rails-dom-testing (~> 2.0) 33 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 34 | actiontext (6.0.6) 35 | actionpack (= 6.0.6) 36 | activerecord (= 6.0.6) 37 | activestorage (= 6.0.6) 38 | activesupport (= 6.0.6) 39 | nokogiri (>= 1.8.5) 40 | actionview (6.0.6) 41 | activesupport (= 6.0.6) 42 | builder (~> 3.1) 43 | erubi (~> 1.4) 44 | rails-dom-testing (~> 2.0) 45 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 46 | activejob (6.0.6) 47 | activesupport (= 6.0.6) 48 | globalid (>= 0.3.6) 49 | activemodel (6.0.6) 50 | activesupport (= 6.0.6) 51 | activerecord (6.0.6) 52 | activemodel (= 6.0.6) 53 | activesupport (= 6.0.6) 54 | activestorage (6.0.6) 55 | actionpack (= 6.0.6) 56 | activejob (= 6.0.6) 57 | activerecord (= 6.0.6) 58 | marcel (~> 1.0) 59 | activesupport (6.0.6) 60 | concurrent-ruby (~> 1.0, >= 1.0.2) 61 | i18n (>= 0.7, < 2) 62 | minitest (~> 5.1) 63 | tzinfo (~> 1.1) 64 | zeitwerk (~> 2.2, >= 2.2.2) 65 | appraisal (1.0.3) 66 | bundler 67 | rake 68 | thor (>= 0.14.0) 69 | ast (2.4.2) 70 | builder (3.2.4) 71 | concurrent-ruby (1.2.3) 72 | coveralls (0.8.23) 73 | json (>= 1.8, < 3) 74 | simplecov (~> 0.16.1) 75 | term-ansicolor (~> 1.3) 76 | thor (>= 0.19.4, < 2.0) 77 | tins (~> 1.6) 78 | crass (1.0.6) 79 | date (3.3.4) 80 | diff-lcs (1.5.0) 81 | docile (1.4.0) 82 | erubi (1.12.0) 83 | globalid (1.1.0) 84 | activesupport (>= 5.0) 85 | i18n (1.14.1) 86 | concurrent-ruby (~> 1.0) 87 | json (2.7.1) 88 | loofah (2.22.0) 89 | crass (~> 1.0.2) 90 | nokogiri (>= 1.12.0) 91 | mail (2.8.1) 92 | mini_mime (>= 0.1.1) 93 | net-imap 94 | net-pop 95 | net-smtp 96 | marcel (1.0.2) 97 | method_source (1.0.0) 98 | mini_mime (1.1.5) 99 | mini_portile2 (2.8.5) 100 | minitest (5.14.4) 101 | net-imap (0.4.9.1) 102 | date 103 | net-protocol 104 | net-pop (0.1.2) 105 | net-protocol 106 | net-protocol (0.2.2) 107 | timeout 108 | net-smtp (0.4.0.1) 109 | net-protocol 110 | nio4r (2.7.0) 111 | nokogiri (1.16.0) 112 | mini_portile2 (~> 2.8.2) 113 | racc (~> 1.4) 114 | nokogiri (1.16.0-aarch64-linux) 115 | racc (~> 1.4) 116 | nokogiri (1.16.0-arm-linux) 117 | racc (~> 1.4) 118 | nokogiri (1.16.0-arm64-darwin) 119 | racc (~> 1.4) 120 | nokogiri (1.16.0-x86-linux) 121 | racc (~> 1.4) 122 | nokogiri (1.16.0-x86_64-linux) 123 | racc (~> 1.4) 124 | parallel (1.24.0) 125 | parser (3.3.0.4) 126 | ast (~> 2.4.1) 127 | racc 128 | power_assert (2.0.3) 129 | racc (1.7.3) 130 | rack (2.2.8) 131 | rack-test (2.1.0) 132 | rack (>= 1.3) 133 | rails (6.0.6) 134 | actioncable (= 6.0.6) 135 | actionmailbox (= 6.0.6) 136 | actionmailer (= 6.0.6) 137 | actionpack (= 6.0.6) 138 | actiontext (= 6.0.6) 139 | actionview (= 6.0.6) 140 | activejob (= 6.0.6) 141 | activemodel (= 6.0.6) 142 | activerecord (= 6.0.6) 143 | activestorage (= 6.0.6) 144 | activesupport (= 6.0.6) 145 | bundler (>= 1.3.0) 146 | railties (= 6.0.6) 147 | sprockets-rails (>= 2.0.0) 148 | rails-dom-testing (2.2.0) 149 | activesupport (>= 5.0.0) 150 | minitest 151 | nokogiri (>= 1.6) 152 | rails-html-sanitizer (1.6.0) 153 | loofah (~> 2.21) 154 | nokogiri (~> 1.14) 155 | railties (6.0.6) 156 | actionpack (= 6.0.6) 157 | activesupport (= 6.0.6) 158 | method_source 159 | rake (>= 0.8.7) 160 | thor (>= 0.20.3, < 2.0) 161 | rainbow (3.1.1) 162 | rake (13.0.6) 163 | regexp_parser (2.9.0) 164 | rexml (3.2.6) 165 | rspec (3.5.0) 166 | rspec-core (~> 3.5.0) 167 | rspec-expectations (~> 3.5.0) 168 | rspec-mocks (~> 3.5.0) 169 | rspec-activemodel-mocks (1.2.0) 170 | activemodel (>= 3.0) 171 | activesupport (>= 3.0) 172 | rspec-mocks (>= 2.99, < 4.0) 173 | rspec-core (3.5.4) 174 | rspec-support (~> 3.5.0) 175 | rspec-expectations (3.5.0) 176 | diff-lcs (>= 1.2.0, < 2.0) 177 | rspec-support (~> 3.5.0) 178 | rspec-its (1.3.0) 179 | rspec-core (>= 3.0.0) 180 | rspec-expectations (>= 3.0.0) 181 | rspec-mocks (3.5.0) 182 | diff-lcs (>= 1.2.0, < 2.0) 183 | rspec-support (~> 3.5.0) 184 | rspec-rails (3.5.2) 185 | actionpack (>= 3.0) 186 | activesupport (>= 3.0) 187 | railties (>= 3.0) 188 | rspec-core (~> 3.5.0) 189 | rspec-expectations (~> 3.5.0) 190 | rspec-mocks (~> 3.5.0) 191 | rspec-support (~> 3.5.0) 192 | rspec-support (3.5.0) 193 | rubocop (1.30.1) 194 | parallel (~> 1.10) 195 | parser (>= 3.1.0.0) 196 | rainbow (>= 2.2.2, < 4.0) 197 | regexp_parser (>= 1.8, < 3.0) 198 | rexml (>= 3.2.5, < 4.0) 199 | rubocop-ast (>= 1.18.0, < 2.0) 200 | ruby-progressbar (~> 1.7) 201 | unicode-display_width (>= 1.4.0, < 3.0) 202 | rubocop-ast (1.30.0) 203 | parser (>= 3.2.1.0) 204 | rubocop-performance (1.19.1) 205 | rubocop (>= 1.7.0, < 2.0) 206 | rubocop-ast (>= 0.4.0) 207 | ruby-progressbar (1.13.0) 208 | simplecov (0.16.1) 209 | docile (~> 1.1) 210 | json (>= 1.8, < 3) 211 | simplecov-html (~> 0.10.0) 212 | simplecov-html (0.10.2) 213 | sprockets (4.2.1) 214 | concurrent-ruby (~> 1.0) 215 | rack (>= 2.2.4, < 4) 216 | sprockets-rails (3.4.2) 217 | actionpack (>= 5.2) 218 | activesupport (>= 5.2) 219 | sprockets (>= 3.0.0) 220 | sqlite3 (1.7.0) 221 | mini_portile2 (~> 2.8.0) 222 | sqlite3 (1.7.0-aarch64-linux) 223 | sqlite3 (1.7.0-arm-linux) 224 | sqlite3 (1.7.0-arm64-darwin) 225 | sqlite3 (1.7.0-x86-linux) 226 | sqlite3 (1.7.0-x86_64-linux) 227 | sync (0.5.0) 228 | term-ansicolor (1.7.1) 229 | tins (~> 1.0) 230 | test-unit (3.6.1) 231 | power_assert 232 | thor (1.3.0) 233 | thread_safe (0.3.6) 234 | timeout (0.4.1) 235 | tins (1.32.1) 236 | sync 237 | tzinfo (1.2.11) 238 | thread_safe (~> 0.1) 239 | unicode-display_width (2.5.0) 240 | websocket-driver (0.7.6) 241 | websocket-extensions (>= 0.1.0) 242 | websocket-extensions (0.1.5) 243 | zeitwerk (2.6.12) 244 | 245 | PLATFORMS 246 | aarch64-linux 247 | arm-linux 248 | arm64-darwin 249 | ruby 250 | x86-linux 251 | x86_64-linux 252 | 253 | DEPENDENCIES 254 | appraisal (~> 1.0.0) 255 | comma! 256 | coveralls 257 | minitest (= 5.14.4) 258 | rails (= 6.0.6) 259 | rake (~> 13.0.1) 260 | rspec (~> 3.5.0) 261 | rspec-activemodel-mocks 262 | rspec-its 263 | rspec-rails 264 | rubocop (~> 1.30.0) 265 | rubocop-performance 266 | sqlite3 267 | test-unit 268 | 269 | BUNDLED WITH 270 | 2.5.5 271 | -------------------------------------------------------------------------------- /gemfiles/rails6.1.7.6.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "coveralls", :require => false 6 | gem "rubocop", "~> 1.30.0", :require => false 7 | gem "rubocop-performance", :require => false 8 | gem "sqlite3" 9 | gem "rails", "6.1.7.6" 10 | gem "rspec-rails" 11 | gem "test-unit" 12 | 13 | gemspec :path => "../" 14 | -------------------------------------------------------------------------------- /gemfiles/rails6.1.7.6.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | comma (4.8.0) 5 | activesupport (>= 4.2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | actioncable (6.1.7.6) 11 | actionpack (= 6.1.7.6) 12 | activesupport (= 6.1.7.6) 13 | nio4r (~> 2.0) 14 | websocket-driver (>= 0.6.1) 15 | actionmailbox (6.1.7.6) 16 | actionpack (= 6.1.7.6) 17 | activejob (= 6.1.7.6) 18 | activerecord (= 6.1.7.6) 19 | activestorage (= 6.1.7.6) 20 | activesupport (= 6.1.7.6) 21 | mail (>= 2.7.1) 22 | actionmailer (6.1.7.6) 23 | actionpack (= 6.1.7.6) 24 | actionview (= 6.1.7.6) 25 | activejob (= 6.1.7.6) 26 | activesupport (= 6.1.7.6) 27 | mail (~> 2.5, >= 2.5.4) 28 | rails-dom-testing (~> 2.0) 29 | actionpack (6.1.7.6) 30 | actionview (= 6.1.7.6) 31 | activesupport (= 6.1.7.6) 32 | rack (~> 2.0, >= 2.0.9) 33 | rack-test (>= 0.6.3) 34 | rails-dom-testing (~> 2.0) 35 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 36 | actiontext (6.1.7.6) 37 | actionpack (= 6.1.7.6) 38 | activerecord (= 6.1.7.6) 39 | activestorage (= 6.1.7.6) 40 | activesupport (= 6.1.7.6) 41 | nokogiri (>= 1.8.5) 42 | actionview (6.1.7.6) 43 | activesupport (= 6.1.7.6) 44 | builder (~> 3.1) 45 | erubi (~> 1.4) 46 | rails-dom-testing (~> 2.0) 47 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 48 | activejob (6.1.7.6) 49 | activesupport (= 6.1.7.6) 50 | globalid (>= 0.3.6) 51 | activemodel (6.1.7.6) 52 | activesupport (= 6.1.7.6) 53 | activerecord (6.1.7.6) 54 | activemodel (= 6.1.7.6) 55 | activesupport (= 6.1.7.6) 56 | activestorage (6.1.7.6) 57 | actionpack (= 6.1.7.6) 58 | activejob (= 6.1.7.6) 59 | activerecord (= 6.1.7.6) 60 | activesupport (= 6.1.7.6) 61 | marcel (~> 1.0) 62 | mini_mime (>= 1.1.0) 63 | activesupport (6.1.7.6) 64 | concurrent-ruby (~> 1.0, >= 1.0.2) 65 | i18n (>= 1.6, < 2) 66 | minitest (>= 5.1) 67 | tzinfo (~> 2.0) 68 | zeitwerk (~> 2.3) 69 | appraisal (1.0.3) 70 | bundler 71 | rake 72 | thor (>= 0.14.0) 73 | ast (2.4.2) 74 | builder (3.2.4) 75 | concurrent-ruby (1.2.3) 76 | coveralls (0.8.23) 77 | json (>= 1.8, < 3) 78 | simplecov (~> 0.16.1) 79 | term-ansicolor (~> 1.3) 80 | thor (>= 0.19.4, < 2.0) 81 | tins (~> 1.6) 82 | crass (1.0.6) 83 | date (3.3.4) 84 | diff-lcs (1.5.0) 85 | docile (1.4.0) 86 | erubi (1.12.0) 87 | globalid (1.2.1) 88 | activesupport (>= 6.1) 89 | i18n (1.14.1) 90 | concurrent-ruby (~> 1.0) 91 | json (2.7.1) 92 | loofah (2.22.0) 93 | crass (~> 1.0.2) 94 | nokogiri (>= 1.12.0) 95 | mail (2.8.1) 96 | mini_mime (>= 0.1.1) 97 | net-imap 98 | net-pop 99 | net-smtp 100 | marcel (1.0.2) 101 | method_source (1.0.0) 102 | mini_mime (1.1.5) 103 | mini_portile2 (2.8.5) 104 | minitest (5.14.4) 105 | net-imap (0.4.9.1) 106 | date 107 | net-protocol 108 | net-pop (0.1.2) 109 | net-protocol 110 | net-protocol (0.2.2) 111 | timeout 112 | net-smtp (0.4.0.1) 113 | net-protocol 114 | nio4r (2.7.0) 115 | nokogiri (1.16.0) 116 | mini_portile2 (~> 2.8.2) 117 | racc (~> 1.4) 118 | nokogiri (1.16.0-aarch64-linux) 119 | racc (~> 1.4) 120 | nokogiri (1.16.0-arm-linux) 121 | racc (~> 1.4) 122 | nokogiri (1.16.0-arm64-darwin) 123 | racc (~> 1.4) 124 | nokogiri (1.16.0-x86-linux) 125 | racc (~> 1.4) 126 | nokogiri (1.16.0-x86_64-linux) 127 | racc (~> 1.4) 128 | parallel (1.24.0) 129 | parser (3.3.0.4) 130 | ast (~> 2.4.1) 131 | racc 132 | power_assert (2.0.3) 133 | racc (1.7.3) 134 | rack (2.2.8) 135 | rack-test (2.1.0) 136 | rack (>= 1.3) 137 | rails (6.1.7.6) 138 | actioncable (= 6.1.7.6) 139 | actionmailbox (= 6.1.7.6) 140 | actionmailer (= 6.1.7.6) 141 | actionpack (= 6.1.7.6) 142 | actiontext (= 6.1.7.6) 143 | actionview (= 6.1.7.6) 144 | activejob (= 6.1.7.6) 145 | activemodel (= 6.1.7.6) 146 | activerecord (= 6.1.7.6) 147 | activestorage (= 6.1.7.6) 148 | activesupport (= 6.1.7.6) 149 | bundler (>= 1.15.0) 150 | railties (= 6.1.7.6) 151 | sprockets-rails (>= 2.0.0) 152 | rails-dom-testing (2.2.0) 153 | activesupport (>= 5.0.0) 154 | minitest 155 | nokogiri (>= 1.6) 156 | rails-html-sanitizer (1.6.0) 157 | loofah (~> 2.21) 158 | nokogiri (~> 1.14) 159 | railties (6.1.7.6) 160 | actionpack (= 6.1.7.6) 161 | activesupport (= 6.1.7.6) 162 | method_source 163 | rake (>= 12.2) 164 | thor (~> 1.0) 165 | rainbow (3.1.1) 166 | rake (13.0.6) 167 | regexp_parser (2.9.0) 168 | rexml (3.2.6) 169 | rspec (3.5.0) 170 | rspec-core (~> 3.5.0) 171 | rspec-expectations (~> 3.5.0) 172 | rspec-mocks (~> 3.5.0) 173 | rspec-activemodel-mocks (1.2.0) 174 | activemodel (>= 3.0) 175 | activesupport (>= 3.0) 176 | rspec-mocks (>= 2.99, < 4.0) 177 | rspec-core (3.5.4) 178 | rspec-support (~> 3.5.0) 179 | rspec-expectations (3.5.0) 180 | diff-lcs (>= 1.2.0, < 2.0) 181 | rspec-support (~> 3.5.0) 182 | rspec-its (1.3.0) 183 | rspec-core (>= 3.0.0) 184 | rspec-expectations (>= 3.0.0) 185 | rspec-mocks (3.5.0) 186 | diff-lcs (>= 1.2.0, < 2.0) 187 | rspec-support (~> 3.5.0) 188 | rspec-rails (3.5.2) 189 | actionpack (>= 3.0) 190 | activesupport (>= 3.0) 191 | railties (>= 3.0) 192 | rspec-core (~> 3.5.0) 193 | rspec-expectations (~> 3.5.0) 194 | rspec-mocks (~> 3.5.0) 195 | rspec-support (~> 3.5.0) 196 | rspec-support (3.5.0) 197 | rubocop (1.30.1) 198 | parallel (~> 1.10) 199 | parser (>= 3.1.0.0) 200 | rainbow (>= 2.2.2, < 4.0) 201 | regexp_parser (>= 1.8, < 3.0) 202 | rexml (>= 3.2.5, < 4.0) 203 | rubocop-ast (>= 1.18.0, < 2.0) 204 | ruby-progressbar (~> 1.7) 205 | unicode-display_width (>= 1.4.0, < 3.0) 206 | rubocop-ast (1.30.0) 207 | parser (>= 3.2.1.0) 208 | rubocop-performance (1.19.1) 209 | rubocop (>= 1.7.0, < 2.0) 210 | rubocop-ast (>= 0.4.0) 211 | ruby-progressbar (1.13.0) 212 | simplecov (0.16.1) 213 | docile (~> 1.1) 214 | json (>= 1.8, < 3) 215 | simplecov-html (~> 0.10.0) 216 | simplecov-html (0.10.2) 217 | sprockets (4.2.1) 218 | concurrent-ruby (~> 1.0) 219 | rack (>= 2.2.4, < 4) 220 | sprockets-rails (3.4.2) 221 | actionpack (>= 5.2) 222 | activesupport (>= 5.2) 223 | sprockets (>= 3.0.0) 224 | sqlite3 (1.7.0) 225 | mini_portile2 (~> 2.8.0) 226 | sqlite3 (1.7.0-aarch64-linux) 227 | sqlite3 (1.7.0-arm-linux) 228 | sqlite3 (1.7.0-arm64-darwin) 229 | sqlite3 (1.7.0-x86-linux) 230 | sqlite3 (1.7.0-x86_64-linux) 231 | sync (0.5.0) 232 | term-ansicolor (1.7.1) 233 | tins (~> 1.0) 234 | test-unit (3.6.1) 235 | power_assert 236 | thor (1.3.0) 237 | timeout (0.4.1) 238 | tins (1.32.1) 239 | sync 240 | tzinfo (2.0.6) 241 | concurrent-ruby (~> 1.0) 242 | unicode-display_width (2.5.0) 243 | websocket-driver (0.7.6) 244 | websocket-extensions (>= 0.1.0) 245 | websocket-extensions (0.1.5) 246 | zeitwerk (2.6.12) 247 | 248 | PLATFORMS 249 | aarch64-linux 250 | arm-linux 251 | arm64-darwin 252 | ruby 253 | x86-linux 254 | x86_64-linux 255 | 256 | DEPENDENCIES 257 | appraisal (~> 1.0.0) 258 | comma! 259 | coveralls 260 | minitest (= 5.14.4) 261 | rails (= 6.1.7.6) 262 | rake (~> 13.0.1) 263 | rspec (~> 3.5.0) 264 | rspec-activemodel-mocks 265 | rspec-its 266 | rspec-rails 267 | rubocop (~> 1.30.0) 268 | rubocop-performance 269 | sqlite3 270 | test-unit 271 | 272 | BUNDLED WITH 273 | 2.5.5 274 | -------------------------------------------------------------------------------- /gemfiles/rails7.0.8.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "coveralls", :require => false 6 | gem "rubocop", "~> 1.30.0", :require => false 7 | gem "rubocop-performance", :require => false 8 | gem "sqlite3" 9 | gem "rails", "7.0.8" 10 | gem "rspec-rails" 11 | 12 | gemspec :path => "../" 13 | -------------------------------------------------------------------------------- /gemfiles/rails7.0.8.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | comma (4.8.0) 5 | activesupport (>= 4.2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | actioncable (7.0.8) 11 | actionpack (= 7.0.8) 12 | activesupport (= 7.0.8) 13 | nio4r (~> 2.0) 14 | websocket-driver (>= 0.6.1) 15 | actionmailbox (7.0.8) 16 | actionpack (= 7.0.8) 17 | activejob (= 7.0.8) 18 | activerecord (= 7.0.8) 19 | activestorage (= 7.0.8) 20 | activesupport (= 7.0.8) 21 | mail (>= 2.7.1) 22 | net-imap 23 | net-pop 24 | net-smtp 25 | actionmailer (7.0.8) 26 | actionpack (= 7.0.8) 27 | actionview (= 7.0.8) 28 | activejob (= 7.0.8) 29 | activesupport (= 7.0.8) 30 | mail (~> 2.5, >= 2.5.4) 31 | net-imap 32 | net-pop 33 | net-smtp 34 | rails-dom-testing (~> 2.0) 35 | actionpack (7.0.8) 36 | actionview (= 7.0.8) 37 | activesupport (= 7.0.8) 38 | rack (~> 2.0, >= 2.2.4) 39 | rack-test (>= 0.6.3) 40 | rails-dom-testing (~> 2.0) 41 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 42 | actiontext (7.0.8) 43 | actionpack (= 7.0.8) 44 | activerecord (= 7.0.8) 45 | activestorage (= 7.0.8) 46 | activesupport (= 7.0.8) 47 | globalid (>= 0.6.0) 48 | nokogiri (>= 1.8.5) 49 | actionview (7.0.8) 50 | activesupport (= 7.0.8) 51 | builder (~> 3.1) 52 | erubi (~> 1.4) 53 | rails-dom-testing (~> 2.0) 54 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 55 | activejob (7.0.8) 56 | activesupport (= 7.0.8) 57 | globalid (>= 0.3.6) 58 | activemodel (7.0.8) 59 | activesupport (= 7.0.8) 60 | activerecord (7.0.8) 61 | activemodel (= 7.0.8) 62 | activesupport (= 7.0.8) 63 | activestorage (7.0.8) 64 | actionpack (= 7.0.8) 65 | activejob (= 7.0.8) 66 | activerecord (= 7.0.8) 67 | activesupport (= 7.0.8) 68 | marcel (~> 1.0) 69 | mini_mime (>= 1.1.0) 70 | activesupport (7.0.8) 71 | concurrent-ruby (~> 1.0, >= 1.0.2) 72 | i18n (>= 1.6, < 2) 73 | minitest (>= 5.1) 74 | tzinfo (~> 2.0) 75 | appraisal (1.0.3) 76 | bundler 77 | rake 78 | thor (>= 0.14.0) 79 | ast (2.4.2) 80 | builder (3.2.4) 81 | concurrent-ruby (1.2.3) 82 | coveralls (0.8.23) 83 | json (>= 1.8, < 3) 84 | simplecov (~> 0.16.1) 85 | term-ansicolor (~> 1.3) 86 | thor (>= 0.19.4, < 2.0) 87 | tins (~> 1.6) 88 | crass (1.0.6) 89 | date (3.3.4) 90 | diff-lcs (1.5.0) 91 | docile (1.4.0) 92 | erubi (1.12.0) 93 | globalid (1.2.1) 94 | activesupport (>= 6.1) 95 | i18n (1.14.1) 96 | concurrent-ruby (~> 1.0) 97 | json (2.7.1) 98 | loofah (2.22.0) 99 | crass (~> 1.0.2) 100 | nokogiri (>= 1.12.0) 101 | mail (2.8.1) 102 | mini_mime (>= 0.1.1) 103 | net-imap 104 | net-pop 105 | net-smtp 106 | marcel (1.0.2) 107 | method_source (1.0.0) 108 | mini_mime (1.1.5) 109 | mini_portile2 (2.8.5) 110 | minitest (5.14.4) 111 | net-imap (0.4.9.1) 112 | date 113 | net-protocol 114 | net-pop (0.1.2) 115 | net-protocol 116 | net-protocol (0.2.2) 117 | timeout 118 | net-smtp (0.4.0.1) 119 | net-protocol 120 | nio4r (2.7.0) 121 | nokogiri (1.16.0) 122 | mini_portile2 (~> 2.8.2) 123 | racc (~> 1.4) 124 | parallel (1.24.0) 125 | parser (3.3.0.4) 126 | ast (~> 2.4.1) 127 | racc 128 | racc (1.7.3) 129 | rack (2.2.8) 130 | rack-test (2.1.0) 131 | rack (>= 1.3) 132 | rails (7.0.8) 133 | actioncable (= 7.0.8) 134 | actionmailbox (= 7.0.8) 135 | actionmailer (= 7.0.8) 136 | actionpack (= 7.0.8) 137 | actiontext (= 7.0.8) 138 | actionview (= 7.0.8) 139 | activejob (= 7.0.8) 140 | activemodel (= 7.0.8) 141 | activerecord (= 7.0.8) 142 | activestorage (= 7.0.8) 143 | activesupport (= 7.0.8) 144 | bundler (>= 1.15.0) 145 | railties (= 7.0.8) 146 | rails-dom-testing (2.2.0) 147 | activesupport (>= 5.0.0) 148 | minitest 149 | nokogiri (>= 1.6) 150 | rails-html-sanitizer (1.6.0) 151 | loofah (~> 2.21) 152 | nokogiri (~> 1.14) 153 | railties (7.0.8) 154 | actionpack (= 7.0.8) 155 | activesupport (= 7.0.8) 156 | method_source 157 | rake (>= 12.2) 158 | thor (~> 1.0) 159 | zeitwerk (~> 2.5) 160 | rainbow (3.1.1) 161 | rake (13.0.6) 162 | regexp_parser (2.9.0) 163 | rexml (3.2.6) 164 | rspec (3.5.0) 165 | rspec-core (~> 3.5.0) 166 | rspec-expectations (~> 3.5.0) 167 | rspec-mocks (~> 3.5.0) 168 | rspec-activemodel-mocks (1.2.0) 169 | activemodel (>= 3.0) 170 | activesupport (>= 3.0) 171 | rspec-mocks (>= 2.99, < 4.0) 172 | rspec-core (3.5.4) 173 | rspec-support (~> 3.5.0) 174 | rspec-expectations (3.5.0) 175 | diff-lcs (>= 1.2.0, < 2.0) 176 | rspec-support (~> 3.5.0) 177 | rspec-its (1.3.0) 178 | rspec-core (>= 3.0.0) 179 | rspec-expectations (>= 3.0.0) 180 | rspec-mocks (3.5.0) 181 | diff-lcs (>= 1.2.0, < 2.0) 182 | rspec-support (~> 3.5.0) 183 | rspec-rails (3.5.2) 184 | actionpack (>= 3.0) 185 | activesupport (>= 3.0) 186 | railties (>= 3.0) 187 | rspec-core (~> 3.5.0) 188 | rspec-expectations (~> 3.5.0) 189 | rspec-mocks (~> 3.5.0) 190 | rspec-support (~> 3.5.0) 191 | rspec-support (3.5.0) 192 | rubocop (1.30.1) 193 | parallel (~> 1.10) 194 | parser (>= 3.1.0.0) 195 | rainbow (>= 2.2.2, < 4.0) 196 | regexp_parser (>= 1.8, < 3.0) 197 | rexml (>= 3.2.5, < 4.0) 198 | rubocop-ast (>= 1.18.0, < 2.0) 199 | ruby-progressbar (~> 1.7) 200 | unicode-display_width (>= 1.4.0, < 3.0) 201 | rubocop-ast (1.30.0) 202 | parser (>= 3.2.1.0) 203 | rubocop-performance (1.19.1) 204 | rubocop (>= 1.7.0, < 2.0) 205 | rubocop-ast (>= 0.4.0) 206 | ruby-progressbar (1.13.0) 207 | simplecov (0.16.1) 208 | docile (~> 1.1) 209 | json (>= 1.8, < 3) 210 | simplecov-html (~> 0.10.0) 211 | simplecov-html (0.10.2) 212 | sqlite3 (1.7.0) 213 | mini_portile2 (~> 2.8.0) 214 | sync (0.5.0) 215 | term-ansicolor (1.7.1) 216 | tins (~> 1.0) 217 | thor (1.3.0) 218 | timeout (0.4.1) 219 | tins (1.32.1) 220 | sync 221 | tzinfo (2.0.6) 222 | concurrent-ruby (~> 1.0) 223 | unicode-display_width (2.5.0) 224 | websocket-driver (0.7.6) 225 | websocket-extensions (>= 0.1.0) 226 | websocket-extensions (0.1.5) 227 | zeitwerk (2.6.12) 228 | 229 | PLATFORMS 230 | ruby 231 | 232 | DEPENDENCIES 233 | appraisal (~> 1.0.0) 234 | comma! 235 | coveralls 236 | minitest (= 5.14.4) 237 | rails (= 7.0.8) 238 | rake (~> 13.0.1) 239 | rspec (~> 3.5.0) 240 | rspec-activemodel-mocks 241 | rspec-its 242 | rspec-rails 243 | rubocop (~> 1.30.0) 244 | rubocop-performance 245 | sqlite3 246 | 247 | BUNDLED WITH 248 | 2.5.5 249 | -------------------------------------------------------------------------------- /gemfiles/rails7.1.3.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "coveralls", :require => false 6 | gem "rubocop", "~> 1.30.0", :require => false 7 | gem "rubocop-performance", :require => false 8 | gem "sqlite3" 9 | gem "rails", "7.1.3" 10 | gem "rspec-rails" 11 | 12 | gemspec :path => "../" 13 | -------------------------------------------------------------------------------- /gemfiles/rails7.1.3.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | comma (4.8.0) 5 | activesupport (>= 4.2.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | actioncable (7.1.3) 11 | actionpack (= 7.1.3) 12 | activesupport (= 7.1.3) 13 | nio4r (~> 2.0) 14 | websocket-driver (>= 0.6.1) 15 | zeitwerk (~> 2.6) 16 | actionmailbox (7.1.3) 17 | actionpack (= 7.1.3) 18 | activejob (= 7.1.3) 19 | activerecord (= 7.1.3) 20 | activestorage (= 7.1.3) 21 | activesupport (= 7.1.3) 22 | mail (>= 2.7.1) 23 | net-imap 24 | net-pop 25 | net-smtp 26 | actionmailer (7.1.3) 27 | actionpack (= 7.1.3) 28 | actionview (= 7.1.3) 29 | activejob (= 7.1.3) 30 | activesupport (= 7.1.3) 31 | mail (~> 2.5, >= 2.5.4) 32 | net-imap 33 | net-pop 34 | net-smtp 35 | rails-dom-testing (~> 2.2) 36 | actionpack (7.1.3) 37 | actionview (= 7.1.3) 38 | activesupport (= 7.1.3) 39 | nokogiri (>= 1.8.5) 40 | racc 41 | rack (>= 2.2.4) 42 | rack-session (>= 1.0.1) 43 | rack-test (>= 0.6.3) 44 | rails-dom-testing (~> 2.2) 45 | rails-html-sanitizer (~> 1.6) 46 | actiontext (7.1.3) 47 | actionpack (= 7.1.3) 48 | activerecord (= 7.1.3) 49 | activestorage (= 7.1.3) 50 | activesupport (= 7.1.3) 51 | globalid (>= 0.6.0) 52 | nokogiri (>= 1.8.5) 53 | actionview (7.1.3) 54 | activesupport (= 7.1.3) 55 | builder (~> 3.1) 56 | erubi (~> 1.11) 57 | rails-dom-testing (~> 2.2) 58 | rails-html-sanitizer (~> 1.6) 59 | activejob (7.1.3) 60 | activesupport (= 7.1.3) 61 | globalid (>= 0.3.6) 62 | activemodel (7.1.3) 63 | activesupport (= 7.1.3) 64 | activerecord (7.1.3) 65 | activemodel (= 7.1.3) 66 | activesupport (= 7.1.3) 67 | timeout (>= 0.4.0) 68 | activestorage (7.1.3) 69 | actionpack (= 7.1.3) 70 | activejob (= 7.1.3) 71 | activerecord (= 7.1.3) 72 | activesupport (= 7.1.3) 73 | marcel (~> 1.0) 74 | activesupport (7.1.3) 75 | base64 76 | bigdecimal 77 | concurrent-ruby (~> 1.0, >= 1.0.2) 78 | connection_pool (>= 2.2.5) 79 | drb 80 | i18n (>= 1.6, < 2) 81 | minitest (>= 5.1) 82 | mutex_m 83 | tzinfo (~> 2.0) 84 | appraisal (1.0.3) 85 | bundler 86 | rake 87 | thor (>= 0.14.0) 88 | ast (2.4.2) 89 | base64 (0.2.0) 90 | bigdecimal (3.1.6) 91 | builder (3.2.4) 92 | concurrent-ruby (1.2.3) 93 | connection_pool (2.4.1) 94 | coveralls (0.8.23) 95 | json (>= 1.8, < 3) 96 | simplecov (~> 0.16.1) 97 | term-ansicolor (~> 1.3) 98 | thor (>= 0.19.4, < 2.0) 99 | tins (~> 1.6) 100 | crass (1.0.6) 101 | date (3.3.4) 102 | diff-lcs (1.5.0) 103 | docile (1.4.0) 104 | drb (2.2.0) 105 | ruby2_keywords 106 | erubi (1.12.0) 107 | globalid (1.2.1) 108 | activesupport (>= 6.1) 109 | i18n (1.14.1) 110 | concurrent-ruby (~> 1.0) 111 | io-console (0.7.2) 112 | irb (1.11.1) 113 | rdoc 114 | reline (>= 0.4.2) 115 | json (2.7.1) 116 | loofah (2.22.0) 117 | crass (~> 1.0.2) 118 | nokogiri (>= 1.12.0) 119 | mail (2.8.1) 120 | mini_mime (>= 0.1.1) 121 | net-imap 122 | net-pop 123 | net-smtp 124 | marcel (1.0.2) 125 | mini_mime (1.1.5) 126 | mini_portile2 (2.8.5) 127 | minitest (5.14.4) 128 | mutex_m (0.2.0) 129 | net-imap (0.4.9.1) 130 | date 131 | net-protocol 132 | net-pop (0.1.2) 133 | net-protocol 134 | net-protocol (0.2.2) 135 | timeout 136 | net-smtp (0.4.0.1) 137 | net-protocol 138 | nio4r (2.7.0) 139 | nokogiri (1.16.0) 140 | mini_portile2 (~> 2.8.2) 141 | racc (~> 1.4) 142 | parallel (1.24.0) 143 | parser (3.3.0.4) 144 | ast (~> 2.4.1) 145 | racc 146 | psych (5.1.2) 147 | stringio 148 | racc (1.7.3) 149 | rack (3.0.8) 150 | rack-session (2.0.0) 151 | rack (>= 3.0.0) 152 | rack-test (2.1.0) 153 | rack (>= 1.3) 154 | rackup (2.1.0) 155 | rack (>= 3) 156 | webrick (~> 1.8) 157 | rails (7.1.3) 158 | actioncable (= 7.1.3) 159 | actionmailbox (= 7.1.3) 160 | actionmailer (= 7.1.3) 161 | actionpack (= 7.1.3) 162 | actiontext (= 7.1.3) 163 | actionview (= 7.1.3) 164 | activejob (= 7.1.3) 165 | activemodel (= 7.1.3) 166 | activerecord (= 7.1.3) 167 | activestorage (= 7.1.3) 168 | activesupport (= 7.1.3) 169 | bundler (>= 1.15.0) 170 | railties (= 7.1.3) 171 | rails-dom-testing (2.2.0) 172 | activesupport (>= 5.0.0) 173 | minitest 174 | nokogiri (>= 1.6) 175 | rails-html-sanitizer (1.6.0) 176 | loofah (~> 2.21) 177 | nokogiri (~> 1.14) 178 | railties (7.1.3) 179 | actionpack (= 7.1.3) 180 | activesupport (= 7.1.3) 181 | irb 182 | rackup (>= 1.0.0) 183 | rake (>= 12.2) 184 | thor (~> 1.0, >= 1.2.2) 185 | zeitwerk (~> 2.6) 186 | rainbow (3.1.1) 187 | rake (13.0.6) 188 | rdoc (6.6.2) 189 | psych (>= 4.0.0) 190 | regexp_parser (2.9.0) 191 | reline (0.4.2) 192 | io-console (~> 0.5) 193 | rexml (3.2.6) 194 | rspec (3.5.0) 195 | rspec-core (~> 3.5.0) 196 | rspec-expectations (~> 3.5.0) 197 | rspec-mocks (~> 3.5.0) 198 | rspec-activemodel-mocks (1.2.0) 199 | activemodel (>= 3.0) 200 | activesupport (>= 3.0) 201 | rspec-mocks (>= 2.99, < 4.0) 202 | rspec-core (3.5.4) 203 | rspec-support (~> 3.5.0) 204 | rspec-expectations (3.5.0) 205 | diff-lcs (>= 1.2.0, < 2.0) 206 | rspec-support (~> 3.5.0) 207 | rspec-its (1.3.0) 208 | rspec-core (>= 3.0.0) 209 | rspec-expectations (>= 3.0.0) 210 | rspec-mocks (3.5.0) 211 | diff-lcs (>= 1.2.0, < 2.0) 212 | rspec-support (~> 3.5.0) 213 | rspec-rails (3.5.2) 214 | actionpack (>= 3.0) 215 | activesupport (>= 3.0) 216 | railties (>= 3.0) 217 | rspec-core (~> 3.5.0) 218 | rspec-expectations (~> 3.5.0) 219 | rspec-mocks (~> 3.5.0) 220 | rspec-support (~> 3.5.0) 221 | rspec-support (3.5.0) 222 | rubocop (1.30.1) 223 | parallel (~> 1.10) 224 | parser (>= 3.1.0.0) 225 | rainbow (>= 2.2.2, < 4.0) 226 | regexp_parser (>= 1.8, < 3.0) 227 | rexml (>= 3.2.5, < 4.0) 228 | rubocop-ast (>= 1.18.0, < 2.0) 229 | ruby-progressbar (~> 1.7) 230 | unicode-display_width (>= 1.4.0, < 3.0) 231 | rubocop-ast (1.30.0) 232 | parser (>= 3.2.1.0) 233 | rubocop-performance (1.19.1) 234 | rubocop (>= 1.7.0, < 2.0) 235 | rubocop-ast (>= 0.4.0) 236 | ruby-progressbar (1.13.0) 237 | ruby2_keywords (0.0.5) 238 | simplecov (0.16.1) 239 | docile (~> 1.1) 240 | json (>= 1.8, < 3) 241 | simplecov-html (~> 0.10.0) 242 | simplecov-html (0.10.2) 243 | sqlite3 (1.7.0) 244 | mini_portile2 (~> 2.8.0) 245 | stringio (3.1.0) 246 | sync (0.5.0) 247 | term-ansicolor (1.7.1) 248 | tins (~> 1.0) 249 | thor (1.3.0) 250 | timeout (0.4.1) 251 | tins (1.32.1) 252 | sync 253 | tzinfo (2.0.6) 254 | concurrent-ruby (~> 1.0) 255 | unicode-display_width (2.5.0) 256 | webrick (1.8.1) 257 | websocket-driver (0.7.6) 258 | websocket-extensions (>= 0.1.0) 259 | websocket-extensions (0.1.5) 260 | zeitwerk (2.6.12) 261 | 262 | PLATFORMS 263 | ruby 264 | 265 | DEPENDENCIES 266 | appraisal (~> 1.0.0) 267 | comma! 268 | coveralls 269 | minitest (= 5.14.4) 270 | rails (= 7.1.3) 271 | rake (~> 13.0.1) 272 | rspec (~> 3.5.0) 273 | rspec-activemodel-mocks 274 | rspec-its 275 | rspec-rails 276 | rubocop (~> 1.30.0) 277 | rubocop-performance 278 | sqlite3 279 | 280 | BUNDLED WITH 281 | 2.5.5 282 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'comma' 4 | -------------------------------------------------------------------------------- /lib/comma.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'csv' 4 | CSV_HANDLER = CSV 5 | 6 | module Comma 7 | DEFAULT_OPTIONS = { 8 | write_headers: true, 9 | style: :default 10 | }.freeze 11 | end 12 | 13 | require 'active_support' 14 | require 'active_support/lazy_load_hooks' 15 | ActiveSupport.on_load(:active_record) do 16 | require 'comma/relation' if defined?(ActiveRecord::Relation) 17 | end 18 | 19 | ActiveSupport.on_load(:mongoid) do 20 | require 'comma/mongoid' 21 | end 22 | 23 | require 'comma/data_mapper_collection' if defined? DataMapper 24 | 25 | require 'comma/generator' 26 | require 'comma/array' 27 | require 'comma/object' 28 | 29 | # Load into Rails controllers 30 | ActiveSupport.on_load(:action_controller) do 31 | if defined?(ActionController::Renderers) && ActionController::Renderers.respond_to?(:add) 32 | ActionController::Renderers.add :csv do |obj, options| 33 | filename = options[:filename] || 'data' 34 | extension = options[:extension] || 'csv' 35 | mime_type = if Rails.version >= '5.0.0' 36 | options[:mime_type] || Mime[:csv] 37 | else 38 | options[:mime_type] || Mime::CSV 39 | end 40 | with_bom = options.delete(:with_bom) || false 41 | 42 | # Capture any CSV optional settings passed to comma or comma specific options 43 | csv_options = options.slice(*CSV_HANDLER::DEFAULT_OPTIONS.merge(Comma::DEFAULT_OPTIONS).keys) 44 | csv_options = csv_options.each_with_object({}) do |(k, v), h| 45 | # XXX: Convert string to boolean 46 | h[k] = case k 47 | when :write_headers 48 | (v != 'false') if v.is_a?(String) 49 | else 50 | v 51 | end 52 | end 53 | data = obj.to_comma(csv_options) 54 | data = "\xEF\xBB\xBF#{data}" if with_bom 55 | disposition = "attachment; filename=\"#{filename}.#{extension}\"" 56 | send_data data, type: mime_type, disposition: disposition 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/comma/array.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Array 4 | def to_comma(style = :default) 5 | Comma::Generator.new(self, style).run(:each) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/comma/data_extractor.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'comma/extractor' 4 | 5 | module Comma 6 | class DataExtractor < Extractor 7 | class ExtractValueFromInstance 8 | def initialize(instance) 9 | @instance = instance 10 | end 11 | 12 | def extract(sym, &block) 13 | yield_block_with_value(extract_value(sym), &block) 14 | end 15 | 16 | private 17 | 18 | def yield_block_with_value(value, &block) 19 | block ? yield(value) : value 20 | end 21 | 22 | def extract_value(method) 23 | extraction_object.send(method) 24 | end 25 | 26 | def extraction_object 27 | @instance 28 | end 29 | end 30 | 31 | class ExtractValueFromAssociationOfInstance < ExtractValueFromInstance 32 | def initialize(instance, association_name) 33 | super(instance) 34 | @association_name = association_name 35 | end 36 | 37 | private 38 | 39 | def extraction_object 40 | @instance.send(@association_name) || null_association 41 | end 42 | 43 | def null_association 44 | @null_association ||= Class.new(Class.const_defined?(:BasicObject) ? ::BasicObject : ::Object) do 45 | def method_missing(_symbol, *_args, &_block) 46 | nil 47 | end 48 | end.new 49 | end 50 | end 51 | 52 | def method_missing(sym, *args, &block) 53 | @results << ExtractValueFromInstance.new(@instance).extract(sym, &block) if 54 | args.blank? 55 | 56 | args.each do |arg| 57 | case arg 58 | when Hash 59 | arg.each do |k, _v| 60 | @results << ExtractValueFromAssociationOfInstance.new(@instance, sym).extract(k, &block) 61 | end 62 | when Symbol 63 | @results << ExtractValueFromAssociationOfInstance.new(@instance, sym).extract(arg, &block) 64 | when String 65 | @results << ExtractValueFromInstance.new(@instance).extract(sym, &block) 66 | else 67 | raise "Unknown data symbol #{arg.inspect}" 68 | end 69 | end 70 | end 71 | 72 | def __static_column__(_header = nil, &block) 73 | @results << (block ? yield(@instance) : nil) 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /lib/comma/data_mapper_collection.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if defined?(DataMapper) 4 | module DataMapper 5 | class Collection 6 | def to_comma(style = :default) 7 | Comma::Generator.new(self, style).run(:each) 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/comma/extractor.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Comma 4 | class Extractor 5 | def initialize(instance, style, formats) 6 | @instance = instance 7 | @style = style 8 | @formats = formats 9 | @results = [] 10 | end 11 | 12 | def results 13 | instance_eval(&@formats[@style]) 14 | @results.map { |r| convert_to_data_value(r) } 15 | end 16 | 17 | def id(*args, &block) 18 | method_missing(:id, *args, &block) 19 | end 20 | 21 | def __use__(style) 22 | # TODO: prevent infinite recursion 23 | instance_eval(&@formats[style]) 24 | end 25 | 26 | private 27 | 28 | def convert_to_data_value(result) 29 | result.nil? ? result : result.to_s 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/comma/generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Comma 4 | class Generator 5 | def initialize(instance, style) 6 | @instance = instance 7 | @style = style 8 | @options = {} 9 | 10 | return unless @style.is_a?(Hash) 11 | 12 | @options = @style.clone 13 | @style = @options.delete(:style) || Comma::DEFAULT_OPTIONS[:style] 14 | @filename = @options.delete(:filename) 15 | end 16 | 17 | def run(iterator_method) 18 | if @filename 19 | CSV_HANDLER.open(@filename, 'w', **@options) { |csv| append_csv(csv, iterator_method) } && (return true) 20 | else 21 | CSV_HANDLER.generate(**@options) { |csv| append_csv(csv, iterator_method) } 22 | end 23 | end 24 | 25 | private 26 | 27 | def append_csv(csv, iterator_method) 28 | return '' if @instance.empty? 29 | 30 | csv << @instance.first.to_comma_headers(@style) unless 31 | @options.key?(:write_headers) && !@options[:write_headers] 32 | @instance.send(iterator_method) do |object| 33 | csv << object.to_comma(@style) 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/comma/header_extractor.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'comma/extractor' 4 | require 'active_support' 5 | require 'active_support/core_ext/class/attribute' 6 | require 'active_support/core_ext/date_time/conversions' 7 | require 'active_support/core_ext/object/blank' 8 | require 'active_support/core_ext/string/inflections' 9 | 10 | module Comma 11 | class HeaderExtractor < Extractor 12 | class_attribute :value_humanizer 13 | 14 | DEFAULT_VALUE_HUMANIZER = lambda do |value, _model_class| 15 | value.is_a?(String) ? value : value.to_s.humanize 16 | end 17 | self.value_humanizer = DEFAULT_VALUE_HUMANIZER 18 | 19 | def method_missing(sym, *args, &_block) 20 | model_class = @instance.class 21 | @results << value_humanizer.call(sym, model_class) if args.blank? 22 | args.each do |arg| 23 | case arg 24 | when Hash 25 | arg.each do |_k, v| 26 | @results << value_humanizer.call(v, get_association_class(model_class, sym)) 27 | end 28 | when Symbol 29 | @results << value_humanizer.call(arg, get_association_class(model_class, sym)) 30 | when String 31 | @results << value_humanizer.call(arg, model_class) 32 | else 33 | raise "Unknown header symbol #{arg.inspect}" 34 | end 35 | end 36 | end 37 | 38 | def __static_column__(header = '', &_block) 39 | @results << header 40 | end 41 | 42 | private 43 | 44 | def get_association_class(model_class, association) 45 | return unless model_class.respond_to?(:reflect_on_association) 46 | 47 | begin 48 | model_class.reflect_on_association(association)&.klass 49 | rescue ArgumentError, NameError 50 | # Since Rails 5.2, ArgumentError is raised. 51 | nil 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/comma/mongoid.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Conditionally set to_comma on Mongoid records if mongoid gem is installed 4 | begin 5 | require 'mongoid' 6 | 7 | module Mongoid 8 | class Criteria 9 | def to_comma(style = :default) 10 | Comma::Generator.new(self, style).run(:each) 11 | end 12 | end 13 | end 14 | rescue LoadError => e 15 | warn e.inspect 16 | end 17 | -------------------------------------------------------------------------------- /lib/comma/object.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'comma/data_extractor' 4 | require 'comma/header_extractor' 5 | 6 | class Object 7 | class_attribute :comma_formats 8 | 9 | class << self 10 | def comma(style = :default, &block) 11 | (self.comma_formats ||= {})[style] = block 12 | end 13 | 14 | def inherited(subclass) 15 | super 16 | subclass.comma_formats = self.comma_formats ? self.comma_formats.dup : {} 17 | end 18 | end 19 | 20 | def to_comma(style = :default) 21 | extract_with(Comma::DataExtractor, style) 22 | end 23 | 24 | def to_comma_headers(style = :default) 25 | extract_with(Comma::HeaderExtractor, style) 26 | end 27 | 28 | private 29 | 30 | def extract_with(extractor_class, style = :default) 31 | raise_unless_style_exists(style) 32 | extractor_class.new(self, style, self.comma_formats).results 33 | end 34 | 35 | def raise_unless_style_exists(style) 36 | return if self.comma_formats && self.comma_formats[style] 37 | 38 | raise "No comma format for class #{self.class} defined for style #{style}" 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/comma/relation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveRecord 4 | class Relation 5 | def to_comma(style = :default) 6 | iterator_method = 7 | if arel.ast.limit || !arel.ast.orders.empty? 8 | Rails.logger.warn { <<~WARN } if defined?(Rails) 9 | #to_comma is being used on a relation with limit or order clauses. Falling back to iterating with :each. This can cause performance issues. 10 | WARN 11 | :each 12 | else 13 | :find_each 14 | end 15 | Comma::Generator.new(self, style).run(iterator_method) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/comma/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Comma 4 | VERSION = '4.8.0' 5 | end 6 | -------------------------------------------------------------------------------- /spec/comma/comma_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.dirname(__FILE__) + '/../spec_helper' 4 | 5 | describe Comma do 6 | it 'should extend object to add a comma method' do 7 | expect(Object).to respond_to(:comma) 8 | end 9 | 10 | it 'should extend object to have a to_comma method' do 11 | expect(Object).to respond_to(:to_comma) 12 | end 13 | 14 | it 'should extend object to have a to_comma_headers method' do 15 | expect(Object).to respond_to(:to_comma_headers) 16 | end 17 | 18 | describe '.to_comma_header' do 19 | it 'should not crash (#94)' do 20 | klass = Class.new 21 | klass.instance_eval do 22 | attr_accessor :name 23 | 24 | comma :brief do 25 | name 26 | end 27 | end 28 | expect { klass.to_comma_headers(:brief) }.to_not raise_error 29 | end 30 | end 31 | end 32 | 33 | describe Comma, 'generating CSV' do # rubocop:disable Metrics/BlockLength 34 | before do 35 | @isbn = Isbn.new('123123123', '321321321') 36 | @book = Book.new('Smalltalk-80', 'Language and Implementation', @isbn) 37 | 38 | @books = [] 39 | @books << @book 40 | end 41 | 42 | it 'should extend Array to add a #to_comma method which will return CSV content for objects within the array' do 43 | expected = "Title,Description,Issuer,ISBN-10,ISBN-13\nSmalltalk-80,Language and Implementation,ISBN,123123123,321321321\n" # rubocop:disable Layout/LineLength 44 | expect(@books.to_comma).to eq(expected) 45 | end 46 | 47 | it 'should return an empty string when generating CSV from an empty array' do 48 | expect([].to_comma).to eq('') 49 | end 50 | 51 | it 'should change the style when specified' do 52 | expect(@books.to_comma(:brief)).to eq("Name,Description\nSmalltalk-80,Language and Implementation\n") 53 | end 54 | 55 | describe 'with :filename specified' do 56 | after { File.delete('comma.csv') } 57 | 58 | it 'should write to the file' do 59 | @books.to_comma(filename: 'comma.csv') 60 | expected = "Title,Description,Issuer,ISBN-10,ISBN-13\nSmalltalk-80,Language and Implementation,ISBN,123123123,321321321\n" # rubocop:disable Layout/LineLength 61 | expect(File.read('comma.csv')).to eq(expected) 62 | end 63 | 64 | it 'should accept FasterCSV options' do 65 | @books.to_comma(filename: 'comma.csv', col_sep: ';', force_quotes: true) 66 | expected = "\"Title\";\"Description\";\"Issuer\";\"ISBN-10\";\"ISBN-13\"\n\"Smalltalk-80\";\"Language and Implementation\";\"ISBN\";\"123123123\";\"321321321\"\n" # rubocop:disable Layout/LineLength 67 | expect(File.read('comma.csv')).to eq(expected) 68 | end 69 | end 70 | 71 | describe 'with FasterCSV options' do 72 | it 'should not change when options are empty' do 73 | expected = "Title,Description,Issuer,ISBN-10,ISBN-13\nSmalltalk-80,Language and Implementation,ISBN,123123123,321321321\n" # rubocop:disable Layout/LineLength 74 | expect(@books.to_comma({})).to eq(expected) 75 | end 76 | 77 | it 'should accept the options in #to_comma and generate the appropriate CSV' do 78 | expected = "\"Title\";\"Description\";\"Issuer\";\"ISBN-10\";\"ISBN-13\"\n\"Smalltalk-80\";\"Language and Implementation\";\"ISBN\";\"123123123\";\"321321321\"\n" # rubocop:disable Layout/LineLength 79 | expect(@books.to_comma(col_sep: ';', force_quotes: true)).to eq(expected) 80 | end 81 | 82 | it 'should change the style when specified' do 83 | expect(@books.to_comma(style: :brief, col_sep: ';', force_quotes: true)) 84 | .to eq("\"Name\";\"Description\"\n\"Smalltalk-80\";\"Language and Implementation\"\n") 85 | end 86 | end 87 | end 88 | 89 | describe Comma, 'defining CSV descriptions' do 90 | describe 'with an unnamed description' do 91 | before do 92 | class Foo 93 | comma do; end 94 | end 95 | end 96 | 97 | it 'should name the current description :default if no name has been provided' do 98 | expect(Foo.comma_formats).not_to be_empty 99 | expect(Foo.comma_formats[:default]).not_to be_nil 100 | end 101 | end 102 | 103 | describe 'with a named description' do 104 | before do 105 | class Bar 106 | comma do; end 107 | comma :detailed do; end 108 | end 109 | end 110 | 111 | it 'should use the provided name to index the comma format' do 112 | expect(Bar.comma_formats).not_to be_empty 113 | expect(Bar.comma_formats[:default]).not_to be_nil 114 | expect(Bar.comma_formats[:detailed]).not_to be_nil 115 | end 116 | end 117 | end 118 | 119 | describe Comma, 'to_comma data/headers object extensions' do # rubocop:disable Metrics/BlockLength 120 | describe 'with unnamed descriptions' do 121 | before do 122 | class Foo 123 | attr_accessor :content 124 | comma do; content; end 125 | 126 | def initialize(content) 127 | @content = content 128 | end 129 | end 130 | 131 | @foo = Foo.new('content') 132 | end 133 | 134 | it 'should return and array of data content, using the :default CSV description if none requested' do 135 | expect(@foo.to_comma).to eq(%w[content]) 136 | end 137 | 138 | it 'should return and array of header content, using the :default CSV description if none requested' do 139 | expect(@foo.to_comma_headers).to eq(%w[Content]) 140 | end 141 | 142 | it 'should return the CSV representation including header and content when called on an array' do 143 | expect(Array(@foo).to_comma).to eq("Content\ncontent\n") 144 | end 145 | end 146 | 147 | describe 'with named descriptions' do 148 | before do 149 | class Foo 150 | attr_accessor :content 151 | comma :detailed do; content; end 152 | 153 | def initialize(content) 154 | @content = content 155 | end 156 | end 157 | 158 | @foo = Foo.new('content') 159 | end 160 | 161 | it 'should return and array of data content, using the :default CSV description if none requested' do 162 | expect(@foo.to_comma(:detailed)).to eq(%w[content]) 163 | end 164 | 165 | it 'should return and array of header content, using the :default CSV description if none requested' do 166 | expect(@foo.to_comma_headers(:detailed)).to eq(%w[Content]) 167 | end 168 | 169 | it 'should return the CSV representation including header and content when called on an array' do 170 | expect(Array(@foo).to_comma(:detailed)).to eq("Content\ncontent\n") 171 | end 172 | 173 | it 'should raise an error if the requested description is not avaliable' do 174 | expect { @foo.to_comma(:bad) }.to raise_error(RuntimeError) 175 | expect { @foo.to_comma_headers(:bad) }.to raise_error(RuntimeError) 176 | expect { Array(@foo).to_comma(:bad) }.to raise_error(RuntimeError) 177 | end 178 | end 179 | 180 | describe 'with block' do # rubocop:disable Metrics/BlockLength 181 | before do 182 | class Foo 183 | attr_accessor :content, :created_at, :updated_at 184 | comma do 185 | content 186 | content('Truncated Content') { |i| i && i.length > 10 ? i[0..10] : '---' } 187 | created_at { |i| i&.to_formatted_s(:db) } 188 | updated_at { |i| i&.to_formatted_s(:db) } 189 | created_at 'Created Custom Label' do |i| i&.to_formatted_s(:short) end 190 | updated_at 'Updated at Custom Label' do |i| i&.to_formatted_s(:short) end 191 | end 192 | 193 | def initialize(content, created_at = Time.now, updated_at = Time.now) 194 | @content = content 195 | @created_at = created_at 196 | @updated_at = updated_at 197 | end 198 | end 199 | 200 | @time = Time.now 201 | @content = 'content ' * 5 202 | @foo = Foo.new @content, @time, @time 203 | end 204 | 205 | it 'should return yielded values by block' do 206 | _header, foo = Array(@foo).to_comma.split("\n") 207 | expected = [ 208 | @content, 209 | @content[0..10], 210 | @time.to_formatted_s(:db), 211 | @time.to_formatted_s(:db), 212 | @time.to_formatted_s(:short), 213 | @time.to_formatted_s(:short) 214 | ].join(',') 215 | expect(foo).to eq(expected) 216 | end 217 | 218 | it 'should return headers with custom labels from block' do 219 | header, _foo = Array(@foo).to_comma.split("\n") 220 | expected = [ 221 | 'Content', 222 | 'Truncated Content', 223 | 'Created at', 224 | 'Updated at', 225 | 'Created Custom Label', 226 | 'Updated at Custom Label' 227 | ].join(',') 228 | expect(header).to eq(expected) 229 | end 230 | 231 | it 'should put headers in place when forced' do 232 | header, _foo = Array(@foo).to_comma(write_headers: true).split("\n") 233 | expected = [ 234 | 'Content', 235 | 'Truncated Content', 236 | 'Created at', 237 | 'Updated at', 238 | 'Created Custom Label', 239 | 'Updated at Custom Label' 240 | ].join(',') 241 | expect(header).to eq(expected) 242 | end 243 | 244 | it 'should not write headers if specified' do 245 | header, _foo = Array(@foo).to_comma(write_headers: false).split("\n") 246 | expected = [ 247 | @content, 248 | @content[0..10], 249 | @time.to_formatted_s(:db), 250 | @time.to_formatted_s(:db), 251 | @time.to_formatted_s(:short), 252 | @time.to_formatted_s(:short) 253 | ].join(',') 254 | expect(header).to eq(expected) 255 | end 256 | end 257 | 258 | describe 'on an object with no comma declaration' do 259 | it 'should raise an error mentioning there is no comma description defined for that class' do 260 | expect { 'a string'.to_comma }.to raise_error('No comma format for class String defined for style default') 261 | expect { 'a string'.to_comma_headers } 262 | .to raise_error('No comma format for class String defined for style default') 263 | end 264 | end 265 | 266 | describe 'on objects using Single Table Inheritance' do 267 | before do 268 | class MySuperClass 269 | attr_accessor :content 270 | comma do; content end 271 | 272 | def initialize(content) 273 | @content = 'super-' + content 274 | end 275 | end 276 | 277 | class ChildClassComma < MySuperClass 278 | comma do; content end 279 | 280 | def initialize(content) 281 | @content = 'sub-' + content 282 | end 283 | end 284 | 285 | class ChildClassNoComma < MySuperClass 286 | end 287 | 288 | @childComma = ChildClassComma.new('content') 289 | @childNoComma = ChildClassNoComma.new('content') 290 | end 291 | 292 | it 'should return and array of data content, as defined in comma block in child class' do 293 | expect(@childComma.to_comma).to eq(%w[sub-content]) 294 | end 295 | 296 | it 'should return and array of data content, as defined in comma block in super class, if not present in child' do 297 | expect(@childNoComma.to_comma).to eq(%w[super-content]) 298 | end 299 | end 300 | end 301 | 302 | describe Comma, '__use__ keyword' do 303 | before(:all) do 304 | @obj = Class.new(Struct.new(:id, :title, :description)) do 305 | comma do 306 | title 307 | __use__ :description 308 | end 309 | 310 | comma :description do 311 | __use__ :static 312 | description 313 | end 314 | 315 | comma :static do 316 | __static_column__ do 'Foo, Inc.' end 317 | end 318 | end.new(1, 'Programming Ruby', 'The Pickaxe book') 319 | end 320 | 321 | subject { @obj.to_comma } 322 | its(:size) { should eq(3) } 323 | it { should eq(['Programming Ruby', 'Foo, Inc.', 'The Pickaxe book']) } 324 | end 325 | -------------------------------------------------------------------------------- /spec/comma/data_extractor_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | # comma do 6 | # name 'Title' 7 | # description 8 | # 9 | # isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13' 10 | # end 11 | 12 | describe Comma::DataExtractor do # rubocop:disable Metrics/BlockLength 13 | before do 14 | @isbn = Isbn.new('123123123', '321321321') 15 | @book = Book.new('Smalltalk-80', 'Language and Implementation', @isbn) 16 | 17 | @data = @book.to_comma 18 | end 19 | 20 | describe 'when no parameters are provided' do 21 | it 'should use the string value returned by sending the method name on the object' do 22 | expect(@data).to include('Language and Implementation') 23 | end 24 | end 25 | 26 | describe 'when given a string description as a parameter' do 27 | it 'should use the string value returned by sending the method name on the object' do 28 | expect(@data).to include('Smalltalk-80') 29 | end 30 | end 31 | 32 | describe 'when an hash is passed as a parameter' do 33 | describe 'with a string value' do 34 | it 'should use the string value, returned by sending the hash key to the object' do 35 | expect(@data).to include('123123123') 36 | expect(@data).to include('321321321') 37 | end 38 | 39 | it 'should not fail when an associated object is nil' do 40 | expect { Book.new('Smalltalk-80', 'Language and Implementation', nil).to_comma }.not_to raise_error 41 | end 42 | end 43 | end 44 | end 45 | 46 | describe Comma::DataExtractor, 'id attribute' do 47 | before do 48 | @data = Class.new(Struct.new(:id)) do 49 | comma do 50 | id 'ID' do |_id| '42' end 51 | end 52 | end.new(1).to_comma 53 | end 54 | 55 | it 'id attribute should yield block' do 56 | expect(@data).to include('42') 57 | end 58 | end 59 | 60 | describe Comma::DataExtractor, 'with static column method' do 61 | before do 62 | @data = Class.new(Struct.new(:id, :name)) do 63 | comma do 64 | __static_column__ 65 | __static_column__ 'STATIC' 66 | __static_column__ 'STATIC' do '' end 67 | __static_column__ 'STATIC', &:name 68 | end 69 | end.new(1, 'John Doe').to_comma 70 | end 71 | 72 | it 'should extract headers' do 73 | expect(@data).to eq([nil, nil, '', 'John Doe']) 74 | end 75 | end 76 | 77 | describe Comma::DataExtractor, 'nil value' do 78 | before do 79 | @data = Class.new(Struct.new(:id, :name)) do 80 | comma do 81 | name 82 | name 'Name' 83 | name 'Name' do |_name| nil end 84 | end 85 | end.new(1, nil).to_comma 86 | end 87 | 88 | it 'should extract nil' do 89 | expect(@data).to eq([nil, nil, nil]) 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /spec/comma/header_extractor_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | # comma do 6 | # name 'Title' 7 | # description 8 | # 9 | # isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13' 10 | # end 11 | 12 | describe Comma::HeaderExtractor do # rubocop:disable Metrics/BlockLength 13 | before do 14 | @isbn = Isbn.new('123123123', '321321321') 15 | @book = Book.new('Smalltalk-80', 'Language and Implementation', @isbn) 16 | 17 | @headers = @book.to_comma_headers 18 | end 19 | 20 | describe 'when no parameters are provided' do 21 | it 'should use the method name as the header name, humanized' do 22 | expect(@headers).to include('Description') 23 | end 24 | end 25 | 26 | describe 'when given a string description as a parameter' do 27 | it 'should use the string value, unmodified' do 28 | expect(@headers).to include('Title') 29 | end 30 | end 31 | 32 | describe 'when an hash is passed as a parameter' do 33 | describe 'with a string value' do 34 | it 'should use the string value, unmodified' do 35 | expect(@headers).to include('ISBN-10') 36 | end 37 | end 38 | 39 | describe 'with a non-string value' do 40 | it 'should use the non string value converted to a string, humanized' do 41 | expect(@headers).to include('Issuer') 42 | end 43 | end 44 | end 45 | end 46 | 47 | describe Comma::HeaderExtractor, 'with static column method' do 48 | before do 49 | @headers = Class.new(Struct.new(:id, :name)) do 50 | comma do 51 | __static_column__ 52 | __static_column__ 'STATIC' 53 | __static_column__ 'STATIC' do '' end 54 | end 55 | end.new(1, 'John Doe').to_comma_headers 56 | end 57 | 58 | it 'should extract headers' do 59 | expect(@headers).to eq(['', 'STATIC', 'STATIC']) 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/comma/rails/active_record_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | if defined? ActiveRecord 6 | 7 | describe Comma, 'generating CSV from an ActiveRecord object' do # rubocop:disable Metrics/BlockLength 8 | class Picture < ActiveRecord::Base 9 | belongs_to :imageable, polymorphic: true 10 | 11 | comma :pr_83 do 12 | imageable name: 'Picture' 13 | end 14 | end 15 | 16 | class Person < ActiveRecord::Base 17 | scope(:teenagers, -> { where(age: 13..19) }) 18 | 19 | comma do 20 | name 21 | age 22 | end 23 | 24 | has_one :job 25 | 26 | comma :issue_75 do 27 | job :title 28 | end 29 | 30 | has_many :pictures, as: :imageable 31 | end 32 | 33 | class Job < ActiveRecord::Base 34 | belongs_to :person 35 | 36 | comma do 37 | person_formatter name: 'Name' 38 | end 39 | 40 | def person_formatter 41 | @person_formatter ||= PersonFormatter.new(person) 42 | end 43 | end 44 | 45 | class PersonFormatter 46 | def initialize(persor) 47 | @person = persor 48 | end 49 | 50 | def name 51 | @person.name 52 | end 53 | end 54 | 55 | before(:all) do 56 | # Setup AR model in memory 57 | ActiveRecord::Base.connection.create_table :pictures, force: true do |table| 58 | table.column :name, :string 59 | table.column :imageable_id, :integer 60 | table.column :imageable_type, :string 61 | end 62 | 63 | ActiveRecord::Base.connection.create_table :people, force: true do |table| 64 | table.column :name, :string 65 | table.column :age, :integer 66 | end 67 | Person.reset_column_information 68 | 69 | ActiveRecord::Base.connection.create_table :jobs, force: true do |table| 70 | table.column :person_id, :integer 71 | table.column :title, :string 72 | end 73 | Job.reset_column_information 74 | 75 | @person = Person.new(age: 18, name: 'Junior') 76 | @person.build_job(title: 'Nice job') 77 | @person.save! 78 | Picture.create(name: 'photo.jpg', imageable_id: @person.id, imageable_type: 'Person') 79 | end 80 | 81 | describe '#to_comma on scopes' do 82 | 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 83 | expect(Person.teenagers.to_comma).to eq "Name,Age\nJunior,18\n" 84 | end 85 | 86 | it 'should find in batches' do 87 | scope = Person.teenagers 88 | expect(scope).to receive(:find_each).and_yield @person 89 | scope.to_comma 90 | end 91 | 92 | it 'should fall back to iterating with each when scope has limit clause' do 93 | scope = Person.limit(1) 94 | expect(scope).to receive(:each).and_yield @person 95 | scope.to_comma 96 | end 97 | 98 | it 'should fall back to iterating with each when scope has order clause' do 99 | scope = Person.order(:age) 100 | expect(scope).to receive(:each).and_yield @person 101 | scope.to_comma 102 | end 103 | end 104 | 105 | describe 'with custom value_humanizer' do 106 | before do 107 | Comma::HeaderExtractor.value_humanizer = 108 | lambda do |value, model_class| 109 | if model_class.respond_to?(:human_attribute_name) 110 | model_class.human_attribute_name(value) 111 | else 112 | value.is_a?(String) ? value : value.to_s.humanize 113 | end 114 | end 115 | 116 | I18n.config.backend.store_translations(:ja, activerecord: { attributes: { person: { age: '年齢', name: '名前' } } }) 117 | @original_locale = I18n.locale 118 | I18n.locale = :ja 119 | end 120 | 121 | after do 122 | I18n.locale = @original_locale 123 | 124 | Comma::HeaderExtractor.value_humanizer = 125 | Comma::HeaderExtractor::DEFAULT_VALUE_HUMANIZER 126 | end 127 | 128 | it 'should i18n-ize header values' do 129 | expect(Person.teenagers.to_comma).to match(/^名前,年齢/) 130 | end 131 | end 132 | 133 | describe 'github issue 75' do 134 | it 'should find association' do 135 | expect { Person.all.to_comma(:issue_75) }.not_to raise_error 136 | end 137 | end 138 | 139 | describe 'with accessor' do 140 | it 'should not raise exception' do 141 | expect(Job.all.to_comma).to eq("Name\nJunior\n") 142 | end 143 | end 144 | 145 | describe 'github pull-request 83' do 146 | it 'should not raise NameError' do 147 | expect { Picture.all.to_comma(:pr_83) }.not_to raise_error 148 | end 149 | end 150 | end 151 | 152 | describe Comma, 'generating CSV from an ActiveRecord object using Single Table Inheritance' do # rubocop:disable Metrics/BlockLength 153 | class Animal < ActiveRecord::Base 154 | comma do 155 | name 'Name' do |name| 156 | 'Super-' + name 157 | end 158 | end 159 | 160 | comma :with_type do 161 | name 162 | type 163 | end 164 | end 165 | 166 | class Dog < Animal 167 | comma do 168 | name 'Name' do |name| 169 | 'Dog-' + name 170 | end 171 | end 172 | end 173 | 174 | class Cat < Animal 175 | end 176 | 177 | before(:all) do 178 | # Setup AR model in memory 179 | ActiveRecord::Base.connection.create_table :animals, force: true do |table| 180 | table.column :name, :string 181 | table.column :type, :string 182 | end 183 | 184 | @dog = Dog.new(name: 'Rex') 185 | @dog.save! 186 | @cat = Cat.new(name: 'Kitty') 187 | @cat.save! 188 | end 189 | 190 | it 'should return and array of data content, as defined in comma block in child class' do 191 | expect(@dog.to_comma).to eq %w[Dog-Rex] 192 | end 193 | 194 | # FIXME: this one is failing - the comma block from Dog is executed instead of the one from the super class 195 | it 'should return and array of data content, as defined in comma block in super class, if not present in child' do 196 | expect(@cat.to_comma).to eq %w[Super-Kitty] 197 | end 198 | 199 | it 'should call definion in parent class' do 200 | expect { @dog.to_comma(:with_type) }.not_to raise_error 201 | end 202 | end 203 | end 204 | -------------------------------------------------------------------------------- /spec/comma/rails/data_mapper_collection_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | if defined? DataMapper 6 | 7 | describe Comma, 'generating CSV from an DataMapper object' do # rubocop:disable Metrics/BlockLength 8 | class Person 9 | include DataMapper::Resource 10 | 11 | property :id, Serial 12 | property :name, String 13 | property :age, Integer 14 | 15 | def self.teenagers 16 | all(:age.gte => 13) & all(:age.lte => 19) 17 | end 18 | 19 | comma do 20 | name 21 | age 22 | end 23 | end 24 | DataMapper.finalize 25 | 26 | before(:all) do 27 | DataMapper.setup(:default, 'sqlite::memory:') 28 | DataMapper.auto_migrate! 29 | end 30 | 31 | after(:all) do 32 | end 33 | 34 | describe 'case' do 35 | before do 36 | @person = Person.new(age: 18, name: 'Junior') 37 | @person.save 38 | end 39 | 40 | it 'should extend scope to add a #to_comma method which will return CSV content for objects within the scope' do 41 | Person.teenagers.to_comma.should == "Name,Age\nJunior,18\n" 42 | end 43 | 44 | it 'should find in batches' do 45 | Person.teenagers.to_comma 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/comma/rails/mongoid_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | if defined? Mongoid 6 | 7 | describe Comma, 'generating CSV from an Mongoid object' do 8 | class Person 9 | include Mongoid::Document 10 | 11 | field :name, type: String 12 | field :age, type: Integer 13 | 14 | scope :teenagers, between(age: 13..19) 15 | 16 | comma do 17 | name 18 | age 19 | end 20 | end 21 | 22 | after(:all) do 23 | Mongoid.purge! 24 | end 25 | 26 | describe 'case' do 27 | before do 28 | @person = Person.new(age: 18, name: 'Junior') 29 | @person.save 30 | end 31 | 32 | 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 33 | Person.teenagers.to_comma.should == "Name,Age\nJunior,18\n" 34 | end 35 | 36 | it 'should find in batches' do 37 | Person.teenagers.to_comma 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/controllers/users_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | if defined?(Rails) 6 | 7 | RSpec.describe UsersController, type: :controller do # rubocop:disable Metrics/BlockLength 8 | describe 'rails setup' do 9 | it 'should capture the CSV renderer provided by Rails' do 10 | mock_users = [mock_model(User), mock_model(User)] 11 | allow(User).to receive(:all).and_return(mock_users) 12 | 13 | expect(mock_users).to receive(:to_comma).once 14 | 15 | get :index, format: :csv 16 | end 17 | end 18 | 19 | describe 'controller' do # rubocop:disable Metrics/BlockLength 20 | before(:all) do 21 | @user_1 = User.create!(first_name: 'Fred', last_name: 'Flintstone') 22 | @user_2 = User.create!(first_name: 'Wilma', last_name: 'Flintstone') 23 | end 24 | 25 | it 'should not affect html requested' do 26 | get :index 27 | 28 | expect(response.status).to eq 200 29 | expect(response.media_type).to eq 'text/html' 30 | expect(response.body).to eq 'Users!' 31 | end 32 | 33 | it 'should return a csv when requested' do 34 | get :index, format: :csv 35 | 36 | expect(response.status).to eq 200 37 | expect(response.media_type).to eq 'text/csv' 38 | expect(response.header['Content-Disposition']).to include('filename="data.csv"') 39 | 40 | expected_content = <<-CSV.gsub(/^\s+/, '') 41 | First name,Last name,Name 42 | Fred,Flintstone,Fred Flintstone 43 | Wilma,Flintstone,Wilma Flintstone 44 | CSV 45 | 46 | expect(response.body).to eq expected_content 47 | end 48 | 49 | describe 'with comma options' do 50 | it 'should allow the style to be chosen from the renderer' do 51 | # Must be passed in same format (string/symbol) eg: 52 | # format.csv { render User.all, :style => :shortened } 53 | 54 | get :with_custom_style, format: :csv 55 | 56 | expected_content = <<-CSV.gsub(/^\s+/, '') 57 | First name,Last name 58 | Fred,Flintstone 59 | Wilma,Flintstone 60 | CSV 61 | 62 | expect(response.body).to eq expected_content 63 | end 64 | end 65 | 66 | describe 'with custom options' do # rubocop:disable Metrics/BlockLength 67 | def is_rails_4? 68 | Rails::VERSION::STRING =~ /^4.*/ 69 | end 70 | 71 | def get_(name, **args) 72 | if is_rails_4? && args[:params] 73 | args.merge!(args[:params]) 74 | args.delete(:params) 75 | end 76 | get name, **args 77 | end 78 | 79 | it 'should allow a filename to be set' do 80 | get_ :with_custom_options, format: :csv, params: { custom_options: { filename: 'my_custom_name' } } 81 | 82 | expect(response.status).to eq 200 83 | expect(response.media_type).to eq 'text/csv' 84 | expect(response.header['Content-Disposition']).to include('filename="my_custom_name.csv"') 85 | end 86 | 87 | it 'should allow a custom filename with spaces' do 88 | require 'shellwords' 89 | params = { custom_options: { filename: 'filename with a lot of spaces' } } 90 | get_ :with_custom_options, format: :csv, params: params 91 | 92 | expect(response.status).to eq 200 93 | expect(response.media_type).to eq 'text/csv' 94 | expect(response.header['Content-Disposition']).to include('filename="filename with a lot of spaces.csv"') 95 | 96 | filename_string = response.header['Content-Disposition'].split('=').last 97 | # shellsplit honors quoted strings 98 | expect(filename_string.shellsplit.length).to eq 1 99 | end 100 | 101 | it 'should allow a file extension to be set' do 102 | get_ :with_custom_options, format: :csv, params: { custom_options: { extension: :txt } } 103 | 104 | expect(response.status).to eq 200 105 | expect(response.media_type).to eq 'text/csv' 106 | expect(response.header['Content-Disposition']).to include('filename="data.txt"') 107 | end 108 | 109 | it 'should allow mime type to be set' do 110 | get_ :with_custom_options, format: :csv, params: { custom_options: { mime_type: 'text/plain' } } 111 | expect(response.status).to eq 200 112 | expect(response.media_type).to eq 'text/plain' 113 | end 114 | 115 | it 'should allow bom to be set' do 116 | get_ :with_custom_options, format: :csv, params: { custom_options: { with_bom: true } } 117 | 118 | expected_content = <<-CSV.gsub(/^\s+/, '') 119 | \xEF\xBB\xBFFirst name,Last name,Name 120 | Fred,Flintstone,Fred Flintstone 121 | Wilma,Flintstone,Wilma Flintstone 122 | CSV 123 | 124 | expect(response.body). to eq expected_content 125 | end 126 | 127 | describe 'headers' do 128 | it 'should allow toggling on' do 129 | get_ :with_custom_options, format: :csv, params: { custom_options: { write_headers: 'true' } } 130 | 131 | expect(response.status).to eq 200 132 | expect(response.media_type).to eq 'text/csv' 133 | 134 | expected_content = <<-CSV.gsub(/^\s+/, '') 135 | First name,Last name,Name 136 | Fred,Flintstone,Fred Flintstone 137 | Wilma,Flintstone,Wilma Flintstone 138 | CSV 139 | 140 | expect(response.body).to eq expected_content 141 | end 142 | 143 | it 'should allow toggling off' do 144 | get_ :with_custom_options, format: :csv, params: { custom_options: { write_headers: false } } 145 | 146 | expect(response.status).to eq 200 147 | expect(response.media_type).to eq 'text/csv' 148 | 149 | expected_content = <<-CSV.gsub(/^\s+/, '') 150 | Fred,Flintstone,Fred Flintstone 151 | Wilma,Flintstone,Wilma Flintstone 152 | CSV 153 | 154 | expect(response.body).to eq expected_content 155 | end 156 | end 157 | 158 | it 'should allow forcing of quotes' do 159 | get_ :with_custom_options, format: :csv, params: { custom_options: { force_quotes: true } } 160 | 161 | expect(response.status).to eq 200 162 | expect(response.media_type).to eq 'text/csv' 163 | 164 | expected_content = <<-CSV.gsub(/^\s+/, '') 165 | "First name","Last name","Name" 166 | "Fred","Flintstone","Fred Flintstone" 167 | "Wilma","Flintstone","Wilma Flintstone" 168 | CSV 169 | 170 | expect(response.body).to eq expected_content 171 | end 172 | 173 | it 'should allow combinations of options' do 174 | params = { 175 | custom_options: { 176 | write_headers: false, 177 | force_quotes: true, 178 | col_sep: '||', 179 | row_sep: "ENDOFLINE\n" 180 | } 181 | } 182 | get_ :with_custom_options, format: :csv, params: params 183 | 184 | expect(response.status).to eq 200 185 | expect(response.media_type).to eq 'text/csv' 186 | 187 | expected_content = <<-CSV.gsub(/^\s+/, '') 188 | "Fred"||"Flintstone"||"Fred Flintstone"ENDOFLINE 189 | "Wilma"||"Flintstone"||"Wilma Flintstone"ENDOFLINE 190 | CSV 191 | 192 | expect(response.body).to eq expected_content 193 | end 194 | end 195 | end 196 | end 197 | end 198 | -------------------------------------------------------------------------------- /spec/non_rails_app/ruby_classes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Book 4 | attr_accessor :name, :description, :isbn 5 | 6 | def initialize(name, description, isbn) 7 | @name = name 8 | @description = description 9 | @isbn = isbn 10 | end 11 | 12 | comma do 13 | name 'Title' 14 | description 15 | 16 | isbn authority: :issuer 17 | isbn number_10: 'ISBN-10' 18 | isbn number_13: 'ISBN-13' 19 | end 20 | 21 | comma :brief do 22 | name 23 | description 24 | end 25 | end 26 | 27 | class Isbn 28 | attr_accessor :number_10, :number_13 29 | 30 | def initialize(isbn_10, isbn_13) 31 | @number_10 = isbn_10 32 | @number_13 = isbn_13 33 | end 34 | 35 | def authority 36 | 'ISBN' 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/rails_app/active_record/config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActiveRecord::Base.configurations = { 'test' => { 'adapter' => 'sqlite3', 'database' => ':memory:' } } 4 | ActiveRecord::Base.establish_connection(:test) 5 | -------------------------------------------------------------------------------- /spec/rails_app/active_record/models.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Post < ActiveRecord::Base 4 | has_one :user 5 | 6 | comma do 7 | title 8 | description 9 | 10 | user :full_name 11 | end 12 | end 13 | 14 | class User < ActiveRecord::Base 15 | comma do 16 | first_name 17 | last_name 18 | full_name 'Name' 19 | end 20 | 21 | comma :shortened do 22 | first_name 23 | last_name 24 | end 25 | 26 | def full_name 27 | "#{first_name} #{last_name}".strip 28 | end 29 | end 30 | 31 | class CreateTables < ActiveRecord::Migration[4.2] 32 | def self.up 33 | create_table :users do |t| 34 | t.string :first_name 35 | t.string :last_name 36 | t.timestamps 37 | end 38 | 39 | create_table :posts do |t| 40 | t.references :user 41 | t.string :title 42 | t.string :description 43 | t.timestamps 44 | end 45 | end 46 | 47 | def self.down 48 | drop_table :posts 49 | drop_table :users 50 | end 51 | end 52 | ActiveRecord::Migration.verbose = false 53 | CreateTables.up 54 | -------------------------------------------------------------------------------- /spec/rails_app/data_mapper/config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | DataMapper.setup(:default, 'sqlite::memory:') 4 | -------------------------------------------------------------------------------- /spec/rails_app/mongoid/config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Mongoid.configure do |config| 4 | config.sessions = { 5 | default: { 6 | hosts: ['localhost:27017'], database: 'comma_test' 7 | } 8 | } 9 | end 10 | -------------------------------------------------------------------------------- /spec/rails_app/rails_app.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'action_controller/railtie' 4 | require 'action_view/railtie' 5 | 6 | # orm configs 7 | require 'rails_app/active_record/config' if defined?(ActiveRecord) 8 | 9 | app = CommaTestApp = Class.new(Rails::Application) 10 | app.config.secret_token = '6f6acf0443f74fd0aa8ff07a7c2fbe0a' 11 | app.config.session_store :cookie_store, key: '_rails_app_session' 12 | app.config.active_support.deprecation = :log 13 | app.config.eager_load = false 14 | app.config.root = File.dirname(__FILE__) 15 | Rails.backtrace_cleaner.remove_silencers! 16 | app.initialize! 17 | 18 | app.routes.draw do 19 | resources :users, only: [:index] 20 | get 'with_custom_options', to: 'users#with_custom_options' 21 | get 'with_custom_style', to: 'users#with_custom_style' 22 | root to: 'users#index' 23 | end 24 | 25 | # models 26 | require 'rails_app/active_record/models' if defined?(ActiveRecord) 27 | 28 | def is_rails_4? 29 | Rails::VERSION::STRING =~ /^4.*/ 30 | end 31 | 32 | if is_rails_4? 33 | def symbolize_param_keys(params) 34 | params.symbolize_keys 35 | end 36 | else 37 | def symbolize_param_keys(params) 38 | if params 39 | params.to_unsafe_h.symbolize_keys 40 | else 41 | {} 42 | end 43 | end 44 | end 45 | 46 | # controllers 47 | class ApplicationController < ActionController::Base; end 48 | class UsersController < ApplicationController 49 | def index 50 | respond_to do |format| 51 | format.html do 52 | if is_rails_4? 53 | render text: 'Users!' 54 | else 55 | render plain: 'Users!' 56 | end 57 | end 58 | format.csv { render csv: User.all } 59 | end 60 | end 61 | 62 | def with_custom_options 63 | render_options = { csv: User.all }.update(symbolize_param_keys(params[:custom_options])) 64 | 65 | respond_to do |format| 66 | format.csv { render render_options } 67 | end 68 | end 69 | 70 | def with_custom_style 71 | respond_to do |format| 72 | format.csv { render csv: User.all, style: :shortened } 73 | end 74 | end 75 | end 76 | 77 | # helpers 78 | Object.const_set(:ApplicationHelper, Module.new) 79 | -------------------------------------------------------------------------------- /spec/rails_app/tmp/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/comma-csv/comma/0f639466331e6cb5ca62e83ed7f2974c717264eb/spec/rails_app/tmp/.gitkeep -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rubygems' 4 | $LOAD_PATH.unshift(File.expand_path(File.join('..', '..', 'lib'), __FILE__)) 5 | 6 | require 'simplecov' 7 | require 'coveralls' 8 | SimpleCov.formatter = Coveralls::SimpleCov::Formatter 9 | if defined? Rails 10 | SimpleCov.start('rails') do 11 | add_filter %r{^/spec/comma/rails/data_mapper_collection_spec\.rb$} 12 | add_filter %r{^/spec/comma/rails/mongoid_spec\.rb$} 13 | end 14 | else 15 | SimpleCov.start do 16 | add_filter %r{^/spec/comma/rails/data_mapper_collection_spec\.rb} 17 | add_filter %r{^/spec/comma/rails/mongoid_spec\.rb} 18 | add_filter %r{^/spec/controllers/} 19 | end 20 | end 21 | 22 | require 'bundler/setup' 23 | Bundler.require 24 | 25 | require 'rspec/active_model/mocks' 26 | require 'rspec/its' 27 | 28 | begin 29 | require 'rails' 30 | rescue LoadError 31 | warn 'rails not loaded' 32 | end 33 | 34 | %w[data_mapper mongoid active_record].each do |orm| 35 | begin 36 | require orm 37 | break 38 | rescue LoadError 39 | warn "#{orm} not loaded" 40 | end 41 | end 42 | 43 | if defined? Rails 44 | require 'rails_app/rails_app' 45 | require 'rspec/rails' 46 | else 47 | require 'rails_app/data_mapper/config' if defined?(DataMapper) 48 | require 'rails_app/mongoid/config' if defined?(Mongoid) 49 | require 'rails_app/active_record/config' if defined?(ActiveRecord) 50 | end 51 | 52 | Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |file| require file } 53 | 54 | require File.expand_path('../spec/non_rails_app/ruby_classes', __dir__) 55 | --------------------------------------------------------------------------------