Project

countless

0.0
No release in over a year
This gem includes reusable code statistics / annotations helpers / Rake tasks.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.4
~> 2.3
~> 1.2
~> 3.12
~> 1.28
>= 0.22
>= 0.9.28

Runtime

 Project Readme

Countless

Continuous Integration Gem Version Test Coverage Test Ratio API docs

This is a reusable and widely configurable collection of Rake tasks and utilities for code statistics and annotations. The Rake task names and outputs are based on the Rails tasks. For code statistics (lines of code, comments) the cloc utility is used, which is battle-proven, popular and good maintained. A bundled version of it is shipped with the gem package.

  • Installation
  • Requirements
  • Usage
    • Addtional Configuration
  • Development
  • Code of Conduct
  • Contributing
  • Releasing

Installation

Add this line to your gemspec/Gemfile:

# Within a gem/library use:
spec.add_runtime_dependency 'countless'

# In an application use:
gem 'countless'

And then execute:

$ bundle

Requirements

  • Ruby (>=2.5, tested on CRuby/MRI only, may work with other implementations as well)
  • Perl (>= 5.10, for the cloc utility)

Usage

You can configure the Countless gem in serveral ways, but the most common usecase is to install its Rake tasks and configure it in order to work properly. Here comes a self descriptive example (within a Rakefile):

# Add the annotations and statistics tasks
require 'countless/rake_tasks'

Afterwards the following Rake tasks are available to you:

  • stats: Report code statistics (KLOCs, etc) (run via bundle exec rake stats)
    +------------------+-------+-----+----------+---------+---------+-----+-------+
    | Name             | Lines | LOC | Comments | Classes | Methods | M/C | LOC/M |
    +------------------+-------+-----+----------+---------+---------+-----+-------+
    | Extensions       |    83 |  40 |       33 |       0 |       4 |   0 |    10 |
    | Top-levels       |   934 | 503 |      331 |       5 |      36 |   7 |    13 |
    | Extensions specs |    36 |  28 |        1 |       0 |       4 |   0 |     7 |
    | Top-levels specs |   323 | 260 |        4 |       0 |      39 |   0 |     6 |
    +------------------+-------+-----+----------+---------+---------+-----+-------+
    | Total            |  1376 | 831 |      369 |       5 |      83 |  16 |    10 |
    +------------------+-------+-----+----------+---------+---------+-----+-------+
      Code LOC: 543     Test LOC: 288     Code to Test Ratio: 1:0.5
    
  • notes: Enumerate all annotations (run via bundle exec rake notes)
    Rakefile:
      * [ 7] This is just for testing purposes
    
    lib/countless/rake_tasks.rb:
      * [ 3] This is just for testing purposes here. Keep it exactly like that.
    
    spec/fixtures/files/test/test_spec.rb:
      * [29] Do something
    
    • notes:optimize, notes:fixme, notes:todo, notes:testme, notes:deprecateme (by default, see config.annotation_tags, to configure more defaults)
    • notes:custom: Show notes for custom annotation (run via bundle exec rake notes:custom ANNOTATION='NOTE')

Addtional Configuration

# All the configured values here represent the Gem defaults.
Countless.configure do |config|
  # The base/root path of the project to work on. This path is used as a #
  # prefix to all relative path/file configurations. By default we check for a
  # Rake invokation (Rakefile location), a Rails invokation (project root) or
  # fallback the the current working directory of the process.
  config.base_path = Dir.pwd

  # The path to the cloc (https://github.com/AlDanial/cloc) utility. The gem
  # comes with a bundled version of the utility, ready to be used. But you
  # can also change the used binary path in order to use a different version
  # which you manually provisioned.
  config.cloc_path = File.expand_path('../../bin/cloc', __dir__)

  # We allow to configure additional file extensions to consider for
  # statistics calculation. They will be included in the default list. This
  # way you can easily extend the list.
  config.additional_stats_file_extensions = []

  # All the file extensions to consider for statistics calculation
  config.stats_file_extensions = %w[
    rb js jsx ts tsx css scss coffee rake erb haml h c cpp rs
  ] + config.additional_stats_file_extensions

  # We allow to configure additional application object types. They will be
  # included in the default list. This way you can easily extend the list.
  config.additional_stats_app_object_types = []

  # Configure the application (in the root +app+ directory) object types,
  # they will be added as regular directories as well as their testing
  # counter parts (minitest/RSpec)
  config.stats_app_object_types = %w[
    channels consumers controllers dashboards decorators fields helpers jobs
    mailboxes mailers models policies serializers services uploaders
    validators value_objects views
  ] + config.additional_stats_app_object_types

  # We allow to configure additional statistics directories. They will be
  # included in the default list. This way you can easily extend the list.
  config.additional_stats_directories = []

  # A list of custom base directories in an application / gem
  config.stats_base_directories = [
    { name: 'JavaScripts', dir: 'app/assets/javascripts' },
    { name: 'Stylesheets', dir: 'app/assets/stylesheets' },
    { name: 'JavaScript', dir: 'app/javascript' },
    { name: 'API', dir: 'app/api' },
    { name: 'API tests', dir: 'test/api', test: true },
    { name: 'API specs', dir: 'spec/api', test: true },
    { name: 'APIs', dir: 'app/apis' },
    { name: 'API tests', dir: 'test/apis', test: true },
    { name: 'API specs', dir: 'spec/apis', test: true },
    { name: 'Libraries', dir: 'app/lib' },
    { name: 'Library tests', dir: 'test/lib', test: true },
    { name: 'Library specs', dir: 'spec/lib', test: true },
    { name: 'Libraries', dir: 'lib' },
    { name: 'Library tests', dir: 'test/lib', test: true },
    { name: 'Library specs', dir: 'spec/lib', test: true }
  ] + config.additional_stats_directories

  # We allow to configure additional detailed statistics patterns. They will
  # be included in the default list. This way you can easily extend the list.
  config.additional_detailed_stats_patterns = {}

  # All the detailed statistics (class/method and tests/examples) patterns
  # which will be used for parsing the source files to gather the metrics
  config.detailed_stats_patterns = {
    ruby: {
      extensions: %w[rb rake],
      class: /^\s*class\s+[_A-Z]/, # regular Ruby classes
      method: Regexp.union(
        [
          /^\s*def\s+[_a-z]/, # regular Ruby methods
          /^\s*def test_/, # minitest
          /^\s*x?it(\s+|\()['"_a-z]/ # RSpec
        ]
      )
    },
    javascript: {
      extensions: %w[js jsx ts tsx],
      class: /^\s*class\s+[_A-Z]/,
      method: Regexp.union(
        [
          /function(\s+[_a-zA-Z][\da-zA-Z]*)?\s*\(/, # regular method
          /^\s*x?it(\s+|\()['"_a-z]/, # jsspec, jasmine, jest
          /^\s*test(\s+|\()['"_a-z]/, # jest
          /^\s*QUnit.test(\s+|\()['"_a-z]/ # qunit
        ]
      )
    },
    coffee: {
      extensions: %w[coffee],
      class: /^\s*class\s+[_A-Z]/,
      method: /[-=]>/
    },
    rust: {
      extensions: %(rs),
      class: /^\s*struct\s+[_A-Z]/,
      method: Regexp.union(
        [
          /^\s*fn\s+[_a-z]/, # regular Rust methods
          /#\[test\]/ # methods with test config
        ]
      )
    },
    c_cpp: {
      extensions: %(h c cpp),
      class: /^\s*(struct|class)\s+[_a-z]/i,
      method: /^\s*\w.* \w.*\(.*\)\s*{/m
    }
  }.deep_merge(config.additional_detailed_stats_patterns)

  # We allow to configure additional annotation directories. They will be
  # included in the default list. This way you can easily extend the list.
  config.additional_annotations_directories = []

  # Configure the directories which should be checked for annotations
  config.annotations_directories = %w[
    app config db src lib test tests spec doc docs
  ] + config.additional_annotations_directories

  # We allow to configure additional annotation files/patterns. They will be
  # included in the default list. This way you can easily extend the list.
  config.additional_annotations_files = []

  # Configure the files/patterns which should be checked for annotations
  config.annotations_files = %w[
    Appraisals CHANGELOG.md CODE_OF_CONDUCT.md config.ru docker-compose.yml
    Dockerfile Envfile Gemfile *.gemspec Makefile Rakefile README.md
  ] + config.additional_annotations_files

  # We allow to configure additional annotation tags. They will be included
  # in the default list. This way you can easily extend the list.
  config.additional_annotation_tags = []

  # Configure the annotation tags which will be search
  config.annotation_tags = %w[
    OPTIMIZE FIXME TODO TESTME DEPRECATEME
  ] + config.additional_annotation_tags

  # We allow to configure additional annotation patterns. They will be
  # included in the default list. This way you can easily extend the list.
  config.additional_annotation_patterns = {}

  # Configure all known file extensions of annotations files
  config.annotation_patterns = {
    hashtag: {
      files: %w[Appraisals Dockerfile Envfile Gemfile Rakefile
                Makefile Appraisals],
      extensions: %w[builder md ru rb rake yml yaml ruby gemspec toml],
      regex: ->(tag) { /#\s*(#{tag}):?\s*(.*)$/ }
    },
    double_slash: {
      extensions: %w[css js jsx ts tsx rust c h],
      regex: ->(tag) { %r{//\s*(#{tag}):?\s*(.*)$} }
    },
    erb: {
      extensions: %w[erb],
      regex: ->(tag) { /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/ }
    },
    haml: {
      extensions: %w[haml],
      regex: ->(tag) { /-#\s*(#{tag}):?\s*(.*)$/ }
    }
  }.deep_merge(config.additional_annotation_patterns)
end

Development

After checking out the repo, run make install to install dependencies. Then, run make test to run the tests. You can also run make shell-irb for an interactive prompt that will allow you to experiment.

Code of Conduct

Everyone interacting in the project codebase, issue tracker, chat rooms and mailing lists is expected to follow the code of conduct.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/hausgold/countless. Make sure that every pull request adds a bullet point to the changelog file with a reference to the actual pull request.

Releasing

The release process of this Gem is fully automated. You just need to open the Github Actions Release Workflow and trigger a new run via the Run workflow button. Insert the new version number (check the changelog first for the latest release) and you're done.