✨ Magic Support

GitHub Actions Workflow Status Code Climate maintainability Code Climate coverage

Magic Support is a collection of utility classes and standard library extensions that were found useful for my pet projects.

It’s inspired by Active Support.

Installation

TODO: Replace UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.

Install the gem and add to the application's Gemfile by executing:

bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG

If bundler is not being used to manage dependencies, install the gem by executing:

gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG

Core extensions

Loading

Magic Support is broken into small pieces so that only the desired extensions can be loaded. It also has some convenience entry points to load related extensions in one shot, even all of them.

Cherry-picking

For every single method defined as a core extension a note says where such a method is defined.

That means that you can require it like this:

require 'magic/core_ext/<module>/<extension>'

Magic Support has been carefully revised so that cherry-picking a file loads only strictly needed dependencies, if any.

Loading grouped core extensions

As a rule of thumb, extensions to SomeClass are available in one shot by loading magic/core_ext/some_class.

Thus, to load all extensions to Kernel:

require 'magic/core_ext/kernel'

Loading all core extensions

You may prefer just to load all core extensions, there is a file for that:

require 'magic/core_ext'

Extensions to all objects

#optional

Yields self to the block and returns the result of the block if it’s truthy, and self otherwise.

rand(6)                              # returns 0–5
  .optional { it + 1 if one_based? } # returns 1–6 if 1-based, or
                                     # the original 0–5 otherwise

It can be considered as a conditional #then.

Good usage for #optional is value piping in method chains with conditional processing:

@people = Person
  .optional { it.where created_at: (1.hour.ago...) if new? }
  .optional { anonymize it if gdpr? }
  .optional { try :decorate }

Defined in core_ext/kernel/optional.

Gems

Author

It holds authors info to be used primarily in gem specs.

Loading

Pre-install Magic Support if you plan to use Gem::Author in your gemspec.

gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG

Usage

  1. Inherit Gem::Author inside your gem and add the authors’ info.

    require 'rubygems/author'
    
    module MyLib
      class Author < Gem::Author
        new(
          name:   'Your Name',
          email:  '[email protected]',
          github: 'Your-GitHub-Username',
        )
      end
    end
    
  2. You can call some helper methods now.

    Gem::Specification.new do |spec|
      spec.name     = 'my_lib'
      spec.version  = MyLib::VERSION
      spec.authors  = MyLib::Author.names
      spec.email    = MyLib::Author.emails
      spec.homepage = "#{MyLib::Author.github_url}/#{spec.name}"
    end
    

RSpec

Method specs

Enables one to write specs for single methods.

[!WARNING] Planed for extraction into a separate gem.

Loading

Require it in spec_helper.rb:

require 'rspec/method'

Usage

Include a method reference into the description. The reference should start with either

  • . for class methods or
  • # for instance ones.
RSpec.describe MyClass do
  describe '.class_method' do
    its([arg1, arg2]) { is_expected.to be return_value }
  end

  describe '#instance_method' do
    its([arg1, arg2]) { is_expected.to be return_value }
  end
end

[!NOTE] Though rspec/its is not needed, it could come useful (see the article on method testing).

Within examples, subject is set to the corresponding Method instance. In cases when the method couldn’t be found (e.g., due to delegation via method_missing), it’s set to a Proc instance instead. Anyway, one may treat it as something callable.

A method name is exposed as a Symbol via method_name.

[!NOTE] subject.name may be undefined, use method_name instead.

Delegated methods

One can use it_behaves_like :delegated for delegated methods. This will ensure that calling the method calls the one of the same name on a delegate under the hood passing it the arguments.

RSpec.describe MyClass do
  describe '#method_with_arguments' do
    it_behaves_like :delegated, to: delegate, with: [arg1, arg2]
  end

  describe '#method_without_arguments' do
    it_behaves_like :delegated, to: delegate
  end
end
Module specs

It’s recommended to use class method notation, when writing specs for module functions.

RSpec.describe MyModule do
  describe '.module_function' do 
    # put the examples here
  end
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/Alexander-Senko/magic-support. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Magic::Support project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.