dry-core-0.4.9/0000755000175000017500000000000013617242752013205 5ustar utkarshutkarshdry-core-0.4.9/CONTRIBUTING.md0000644000175000017500000000305213617242752015436 0ustar utkarshutkarsh# Issue Guidelines ## Reporting bugs If you found a bug, report an issue and describe what's the expected behavior versus what actually happens. If the bug causes a crash, attach a full backtrace. If possible, a reproduction script showing the problem is highly appreciated. ## Reporting feature requests Report a feature request **only after discourseing it first on [discourse.dry-rb.org](https://discourse.dry-rb.org)** where it was accepted. Please provide a concise description of the feature, don't link to a discourseion thread, and instead summarize what was discourseed. ## Reporting questions, support requests, ideas, concerns etc. **PLEASE DON'T** - use [discourse.dry-rb.org](https://discourse.dry-rb.org) instead. # Pull Request Guidelines A Pull Request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc. Other requirements: 1) Do not open a pull request if you can't provide tests along with it. If you have problems writing tests, ask for help in the related issue. 2) Follow the style conventions of the surrounding code. In most cases, this is standard ruby style. 3) Add API documentation if it's a new feature 4) Update API documentation if it changes an existing feature 5) Bonus points for sending a PR to [github.com/dry-rb/dry-rb.org](github.com/dry-rb/dry-rb.org) which updates user documentation and guides # Asking for help If these guidelines aren't helpful, and you're stuck, please post a message on [discourse.dry-rb.org](https://discourse.dry-rb.org). dry-core-0.4.9/.travis.yml0000644000175000017500000000146413617242752015323 0ustar utkarshutkarshlanguage: ruby dist: trusty sudo: required cache: bundler bundler_args: --without benchmarks tools after_success: - '[ -d coverage ] && bundle exec codeclimate-test-reporter' before_install: - gem install bundler after_success: - '[ -d coverage ] && bundle exec codeclimate-test-reporter' rvm: - 2.4.6 - 2.5.5 - 2.6.3 - truffleruby env: global: - COVERAGE=true - JRUBY_OPTS='--dev -J-Xmx1024M' matrix: allow_failures: - rvm: truffleruby include: - rvm: jruby-9.2.7.0 jdk: openjdk8 notifications: email: false webhooks: urls: - https://webhooks.gitter.im/e/19098b4253a72c9796db on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: false # default: false dry-core-0.4.9/Rakefile0000644000175000017500000000022113617242752014645 0ustar utkarshutkarsh# frozen_string_literal: true require 'bundler/gem_tasks' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task default: :spec dry-core-0.4.9/LICENSE.txt0000644000175000017500000000207313617242752015032 0ustar utkarshutkarshThe MIT License (MIT) Copyright (c) 2016 Nikita Shilnikov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dry-core-0.4.9/CHANGELOG.md0000644000175000017500000001161613617242752015023 0ustar utkarshutkarsh# v0.4.9 ### Added - `Undefined.coalesce` takes a variable number of arguments and returns the first non-`Undefined` value (flash-gordon) ```ruby Undefined.coalesce(Undefined, Undefined, :foo) # => :foo ``` ### Fixed - `Undefined.{dup,clone}` returns `Undefined` back, `Undefined` is a singleton (flash-gordon) [Compare v0.4.8...v0.4.9](https://github.com/dry-rb/dry-core/compare/v0.4.8...v0.4.9) # v0.4.8 2019-06-23 ### Added - `Undefined.map` for mapping non-undefined values (flash-gordon): ```ruby something = 1 Undefined.map(something) { |v| v + 1 } # => 2 something = Undefined Undefined.map(something) { |v| v + 1 } # => Undefined ``` [Compare v0.4.7...v0.4.8](https://github.com/dry-rb/dry-core/compare/v0.4.7...v0.4.8) # v0.4.7 2018-06-25 ### Fixed - Fix default logger for deprecations, it now uses `$stderr` by default, as it should (flash-gordon) [Compare v0.4.6...v0.4.7](https://github.com/dry-rb/dry-core/compare/v0.4.6...v0.4.7) # v0.4.6 2018-05-15 ### Changed - Trigger constant autoloading in the class builder (radar) [Compare v0.4.5...v0.4.6](https://github.com/dry-rb/dry-core/compare/v0.4.5...v0.4.6) # v0.4.5 2018-03-14 ### Added - `Dry::Core::Memoizable`, which provides a `memoize` macro for memoizing results of instance methods (timriley) [Compare v0.4.4...v0.4.5](https://github.com/dry-rb/dry-core/compare/v0.4.4...v0.4.5) # v0.4.4 2018-02-10 ### Added - `deprecate_constant` overrides `Module#deprecate_constant` and issues a labeled message on accessing a deprecated constant (flash-gordon) - `Undefined.default` which accepts two arguments and returns the first if it's not `Undefined`; otherwise, returns the second one or yields a block (flash-gordon) [Compare v0.4.3...v0.4.4](https://github.com/dry-rb/dry-core/compare/v0.4.3...v0.4.4) # v0.4.3 2018-02-03 ### Added - `Dry::Core::DescendantsTracker` which is a maintained version of the [`descendants_tracker`](https://github.com/dkubb/descendants_tracker) gem (flash-gordon) [Compare v0.4.2...v0.4.3](https://github.com/dry-rb/dry-core/compare/v0.4.2...0.4.3) # v0.4.2 2017-12-16 ### Fixed - Class attributes now support private setters/getters (flash-gordon) [Compare v0.4.1...v0.4.2](https://github.com/dry-rb/dry-core/compare/v0.4.1...v0.4.2) # v0.4.1 2017-11-04 ### Changed - Improved error message on invalid attribute value (GustavoCaso) [Compare v0.4.0...v0.4.1](https://github.com/dry-rb/dry-core/compare/v0.4.0...v0.4.1) # v0.4.0 2017-11-02 ### Added - Added the `:type` option to class attributes, you can now restrict attribute values with a type. You can either use plain ruby types (`Integer`, `String`, etc) or `dry-types` (GustavoCaso) ```ruby class Foo extend Dry::Core::ClassAttributes defines :ruby_attr, type: Integer defines :dry_attr, type: Dry::Types['strict.int'] end ``` [Compare v0.3.4...v0.4.0](https://github.com/dry-rb/dry-core/compare/v0.3.4...v0.4.0) # v0.3.4 2017-09-29 ### Fixed - `Deprecations` output is set to `$stderr` by default now (solnic) [Compare v0.3.3...v0.3.4](https://github.com/dry-rb/dry-core/compare/v0.3.3...v0.3.4) # v0.3.3 2017-08-31 ### Fixed - The Deprecations module now shows the right caller line (flash-gordon) [Compare v0.3.2...v0.3.3](https://github.com/dry-rb/dry-core/compare/v0.3.2...v0.3.3) # v0.3.2 2017-08-31 ### Added - Accept an existing logger object in `Dry::Core::Deprecations.set_logger!` (flash-gordon) [Compare v0.3.1...v0.3.2](https://github.com/dry-rb/dry-core/compare/v0.3.1...v0.3.2) # v0.3.1 2017-05-27 ### Added - Support for building classes within an existing namespace (flash-gordon) [Compare v0.3.0...v0.3.1](https://github.com/dry-rb/dry-core/compare/v0.3.0...v0.3.1) # v0.3.0 2017-05-05 ### Changed - Class attributes are initialized _before_ running the `inherited` hook. It's slightly more convenient behavior and it's very unlikely anyone will be affected by this, but technically this is a breaking change (flash-gordon) [Compare v0.2.4...v0.3.0](https://github.com/dry-rb/dry-core/compare/v0.2.4...v0.3.0) # v0.2.4 2017-01-26 ### Fixed - Do not require deprecated method to be defined (flash-gordon) [Compare v0.2.3...v0.2.4](https://github.com/dry-rb/dry-core/compare/v0.2.3...v0.2.4) # v0.2.3 2016-12-30 ### Fixed - Fix warnings on using uninitialized class attributes (flash-gordon) [Compare v0.2.2...v0.2.3](https://github.com/dry-rb/dry-core/compare/v0.2.2...v0.2.3) # v0.2.2 2016-12-30 ### Added - `ClassAttributes` which provides `defines` method for defining get-or-set methods (flash-gordon) [Compare v0.2.1...v0.2.2](https://github.com/dry-rb/dry-core/compare/v0.2.1...v0.2.2) # v0.2.1 2016-11-18 ### Added - `Constants` are now available in nested scopes (flash-gordon) [Compare v0.2.0...v0.2.1](https://github.com/dry-rb/dry-core/compare/v0.2.0...v0.2.1) # v0.2.0 2016-11-01 [Compare v0.1.0...v0.2.0](https://github.com/dry-rb/dry-core/compare/v0.1.0...v0.2.0) # v0.1.0 2016-09-17 Initial release dry-core-0.4.9/.codeclimate.yml0000644000175000017500000000036213617242752016260 0ustar utkarshutkarshversion: "2" prepare: fetch: - url: "https://raw.githubusercontent.com/dry-rb/devtools/master/.rubocop.yml" path: ".rubocop.yml" exclude_patterns: - "benchmarks/" - "examples/" - "spec/" plugins: rubocop: enabled: true dry-core-0.4.9/.gitignore0000644000175000017500000000016613617242752015200 0ustar utkarshutkarsh/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ spec/examples.txt .rubocop.yml dry-core-0.4.9/Gemfile0000644000175000017500000000064013617242752014500 0ustar utkarshutkarshsource 'https://rubygems.org' gemspec group :test do if RUBY_VERSION >= '2.4' gem 'activesupport' else gem 'activesupport', '~> 4.2' end gem 'inflecto', '~> 0.0', '>= 0.0.2' gem 'codeclimate-test-reporter', require: false gem 'simplecov', require: false gem 'dry-types', '~> 1.0' gem 'dry-inflector' end group :tools do gem 'pry-byebug', platform: :mri gem 'pry', platform: :jruby end dry-core-0.4.9/dry-core.gemspec0000644000175000017500000000266513617242752016307 0ustar utkarshutkarsh# frozen_string_literal: true lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'dry/core/version' Gem::Specification.new do |spec| spec.name = 'dry-core' spec.version = Dry::Core::VERSION spec.authors = ['Nikita Shilnikov'] spec.email = ['fg@flashgordon.ru'] spec.summary = 'A toolset of small support modules used throughout the dry-rb ecosystem.' spec.description = spec.summary spec.homepage = 'https://github.com/dry-rb/dry-core' spec.license = 'MIT' # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' # to allow pushing to a single host or delete this section to allow pushing to any host. if spec.respond_to?(:metadata) spec.metadata['allowed_push_host'] = 'https://rubygems.org' else raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' end spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|bin)/}) } spec.bindir = 'exe' spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] spec.required_ruby_version = '>= 2.1.0' spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0' spec.add_development_dependency 'bundler' spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency 'rspec', '~> 3.0' end dry-core-0.4.9/README.md0000644000175000017500000000434513617242752014472 0ustar utkarshutkarsh[gitter]: https://gitter.im/dry-rb/chat [gem]: https://rubygems.org/gems/dry-core [travis]: https://travis-ci.org/dry-rb/dry-core [code_climate]: https://codeclimate.com/github/dry-rb/dry-core [inch]: http://inch-ci.org/github/dry-rb/dry-core [chat]: https://dry-rb.zulipchat.com # dry-core [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat] [![Gem Version](https://img.shields.io/gem/v/dry-core.svg)][gem] [![Build Status](https://img.shields.io/travis/dry-rb/dry-core.svg)][travis] [![Code Climate](https://api.codeclimate.com/v1/badges/eebb0e969814744231e4/maintainability)][code_climate] [![Test Coverage](https://api.codeclimate.com/v1/badges/eebb0e969814744231e4/test_coverage)][code_climate] [![API Documentation Coverage](http://inch-ci.org/github/dry-rb/dry-core.svg)][inch] ![No monkey-patches](https://img.shields.io/badge/monkey--patches-0-brightgreen.svg) A collection of small modules used in the dry-rb ecosystem. ## Links * [User docs](https://dry-rb.org/gems/dry-core) * [API docs](http://rubydoc.info/gems/dry-core) ## Supported Ruby versions This library officially supports following Ruby versions: * MRI >= `2.4` * jruby >= `9.2` It **should** work on MRI `2.3.x` too, but there's no official support for this version. ## Installation Add this line to your application's Gemfile: ```ruby gem 'dry-core' ``` And then execute: $ bundle Or install it yourself as: $ gem install dry-core ## 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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/dry-rb/dry-core. ## License The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). dry-core-0.4.9/.rspec0000644000175000017500000000007213617242752014321 0ustar utkarshutkarsh--format progress --color --require ./spec/spec_helper.rb dry-core-0.4.9/.inch.yml0000644000175000017500000000010713617242752014725 0ustar utkarshutkarshfiles: excluded: - lib/dry/core.rb - lib/dry/core/version.rb dry-core-0.4.9/lib/0000755000175000017500000000000013617242752013753 5ustar utkarshutkarshdry-core-0.4.9/lib/dry-core.rb0000644000175000017500000000006213617242752016022 0ustar utkarshutkarsh# frozen_string_literal: true require 'dry/core' dry-core-0.4.9/lib/dry/0000755000175000017500000000000013617242752014551 5ustar utkarshutkarshdry-core-0.4.9/lib/dry/core.rb0000644000175000017500000000016413617242752016027 0ustar utkarshutkarsh# frozen_string_literal: true require 'dry/core/version' # :nodoc: module Dry # :nodoc: module Core end end dry-core-0.4.9/lib/dry/core/0000755000175000017500000000000013617242752015501 5ustar utkarshutkarshdry-core-0.4.9/lib/dry/core/version.rb0000644000175000017500000000013713617242752017514 0ustar utkarshutkarsh# frozen_string_literal: true module Dry module Core VERSION = '0.4.9'.freeze end end dry-core-0.4.9/lib/dry/core/descendants_tracker.rb0000644000175000017500000000277613617242752022050 0ustar utkarshutkarsh# frozen_string_literal: true require 'concurrent/array' module Dry module Core # An implementation of descendants tracker, heavily inspired # by the descendants_tracker gem. # # @example # # class Base # extend Dry::Core::DescendantsTracker # end # # class A < Base # end # # class B < Base # end # # class C < A # end # # Base.descendants # => [C, B, A] # A.descendants # => [C] # B.descendants # => [] # module DescendantsTracker class << self # @api private def setup(target) target.instance_variable_set(:@descendants, Concurrent::Array.new) end private # @api private def extended(base) super DescendantsTracker.setup(base) end end # Return the descendants of this class # # @example # descendants = Parent.descendants # # @return [Array] # # @api public attr_reader :descendants protected # @api private def add_descendant(descendant) ancestor = superclass if ancestor.respond_to?(:add_descendant, true) ancestor.add_descendant(descendant) end descendants.unshift(descendant) end private # @api private def inherited(descendant) super DescendantsTracker.setup(descendant) add_descendant(descendant) end end end end dry-core-0.4.9/lib/dry/core/constants.rb0000644000175000017500000000502213617242752020041 0ustar utkarshutkarsh# frozen_string_literal: true require 'set' module Dry module Core # A list of constants you can use to avoid memory allocations or identity checks. # # @example Just include this module to your class or module # class Foo # include Dry::Core::Constants # def call(value = EMPTY_ARRAY) # value.map(&:to_s) # end # end # # @api public module Constants # An empty array EMPTY_ARRAY = [].freeze # An empty hash EMPTY_HASH = {}.freeze # An empty list of options EMPTY_OPTS = {}.freeze # An empty set EMPTY_SET = Set.new.freeze # An empty string EMPTY_STRING = ''.freeze # A special value you can use as a default to know if no arguments # were passed to you method # # @example # def method(value = Undefined) # if value == Undefined # puts 'no args' # else # puts value # end # end Undefined = Object.new.tap do |undefined| # @api private Self = -> { Undefined } # @api public def undefined.to_s 'Undefined' end # @api public def undefined.inspect 'Undefined' end # Pick a value, if the first argument is not Undefined, return it back, # otherwise return the second arg or yield the block. # # @example # def method(val = Undefined) # 1 + Undefined.default(val, 2) # end # def undefined.default(x, y = self) if equal?(x) if equal?(y) yield else y end else x end end # Map a non-undefined value # # @example # def add_five(val = Undefined) # Undefined.map(val) { |x| x + 5 } # end # def undefined.map(value) if equal?(value) self else yield(value) end end # @api public def undefined.dup self end # @api public def undefined.clone self end # @api public def undefined.coalesce(*args) args.find(Self) { |x| !equal?(x) } end end.freeze def self.included(base) super constants.each do |const_name| base.const_set(const_name, const_get(const_name)) end end end end end dry-core-0.4.9/lib/dry/core/memoizable.rb0000644000175000017500000000307413617242752020156 0ustar utkarshutkarsh# frozen_string_literal: true module Dry module Core module Memoizable MEMOIZED_HASH = {}.freeze module ClassInterface def memoize(*names) prepend(Memoizer.new(self, names)) end def new(*) obj = super obj.instance_variable_set(:'@__memoized__', MEMOIZED_HASH.dup) obj end end def self.included(klass) super klass.extend(ClassInterface) end attr_reader :__memoized__ # @api private class Memoizer < Module attr_reader :klass attr_reader :names # @api private def initialize(klass, names) @names = names @klass = klass define_memoizable_names! end private # @api private def define_memoizable_names! names.each do |name| meth = klass.instance_method(name) if meth.parameters.size > 0 define_method(name) do |*args| name_with_args = :"#{name}_#{args.hash}" if __memoized__.key?(name_with_args) __memoized__[name_with_args] else __memoized__[name_with_args] = super(*args) end end else define_method(name) do if __memoized__.key?(name) __memoized__[name] else __memoized__[name] = super() end end end end end end end end end dry-core-0.4.9/lib/dry/core/inflector.rb0000644000175000017500000000735313617242752020023 0ustar utkarshutkarsh# frozen_string_literal: true module Dry module Core # Helper module providing thin interface around an inflection backend. module Inflector # List of supported backends BACKENDS = { activesupport: [ 'active_support/inflector', proc { ::ActiveSupport::Inflector } ], dry_inflector: [ 'dry/inflector', proc { Dry::Inflector.new } ], inflecto: [ 'inflecto', proc { ::Inflecto } ] }.freeze # Try to activate a backend # # @api private def self.realize_backend(path, backend_factory) require path rescue LoadError nil else backend_factory.call end # Set up first available backend # # @api private def self.detect_backend BACKENDS.inject(nil) do |backend, (_, (path, factory))| backend || realize_backend(path, factory) end || raise(LoadError, "No inflector library could be found: "\ "please install either the `inflecto` or `activesupport` gem.") end # Set preferred backend # # @param [Symbol] name backend name (:activesupport or :inflecto) def self.select_backend(name = nil) if name && !BACKENDS.key?(name) raise NameError, "Invalid inflector library selection: '#{name}'" end @inflector = name ? realize_backend(*BACKENDS[name]) : detect_backend end # Inflector accessor. Lazily initializes a backend # # @api private def self.inflector defined?(@inflector) ? @inflector : select_backend end # Transform string to camel case # # @example # Dry::Core::Inflector.camelize('foo_bar') # => 'FooBar' # # @param [String] input input string # @return Transformed string def self.camelize(input) inflector.camelize(input) end # Transform string to snake case # # @example # Dry::Core::Inflector.underscore('FooBar') # => 'foo_bar' # # @param [String] input input string # @return Transformed string def self.underscore(input) inflector.underscore(input) end # Get a singlular form of a word # # @example # Dry::Core::Inflector.singularize('chars') # => 'char' # # @param [String] input input string # @return Transformed string def self.singularize(input) inflector.singularize(input) end # Get a plural form of a word # # @example # Dry::Core::Inflector.pluralize('string') # => 'strings' # # @param [String] input input string # @return Transformed string def self.pluralize(input) inflector.pluralize(input) end # Remove namespaces from a constant name # # @example # Dry::Core::Inflector.demodulize('Deeply::Nested::Name') # => 'Name' # # @param [String] input input string # @return Unnested constant name def self.demodulize(input) inflector.demodulize(input) end # Get a constant value by its name # # @example # Dry::Core::Inflector.constantize('Foo::Bar') # => Foo::Bar # # @param [String] input input constant name # @return Constant value def self.constantize(input) inflector.constantize(input) end # Transform a file path to a constant name # # @example # Dry::Core::Inflector.classify('foo/bar') # => 'Foo::Bar' # # @param [String] input input string # @return Constant name def self.classify(input) inflector.classify(input) end end end end dry-core-0.4.9/lib/dry/core/errors.rb0000644000175000017500000000042613617242752017344 0ustar utkarshutkarsh# frozen_string_literal: true module Dry module Core class InvalidClassAttributeValue < StandardError def initialize(name, value) super( "Value #{value.inspect} is invalid for class attribute #{name.inspect}" ) end end end end dry-core-0.4.9/lib/dry/core/cache.rb0000644000175000017500000000347013617242752017075 0ustar utkarshutkarsh# frozen_string_literal: true require 'concurrent/map' module Dry module Core # Allows you to cache call results that are solely determined by arguments. # # @example # require 'dry/core/cache' # # class Foo # extend Dry::Core::Cache # # def heavy_computation(arg1, arg2) # fetch_or_store(arg1, arg2) { arg1 ^ arg2 } # end # end # # @api public module Cache # @api private def self.extended(klass) super klass.include(Methods) klass.instance_variable_set(:@__cache__, Concurrent::Map.new) end # @api private def inherited(klass) super klass.instance_variable_set(:@__cache__, cache) end # @api private def cache @__cache__ end # Caches a result of the block evaluation # # @param [Array] args List of hashable objects # @yield An arbitrary block # # @note beware Proc instance hashes are not equal, i.e. -> { 1 }.hash != -> { 1 }.hash, # this means you shouldn't pass Procs in args unless you're sure # they are always the same instances, otherwise you introduce a memory leak # # @return [Object] block's return value (cached for subsequent calls with the same argument values) def fetch_or_store(*args, &block) cache.fetch_or_store(args.hash, &block) end # Instance methods module Methods # Delegates call to the class-level method # # @param [Array] args List of hashable objects # @yield An arbitrary block # # @return [Object] block's return value def fetch_or_store(*args, &block) self.class.fetch_or_store(*args, &block) end end end end end dry-core-0.4.9/lib/dry/core/class_builder.rb0000644000175000017500000000476713617242752020657 0ustar utkarshutkarsh# frozen_string_literal: true module Dry module Core # Class for generating more classes class ClassBuilder ParentClassMismatch = Class.new(TypeError) attr_reader :name attr_reader :parent attr_reader :namespace def initialize(name:, parent: nil, namespace: nil) @name = name @namespace = namespace @parent = parent || Object end # Generate a class based on options # # @example Create anonymous class # builder = Dry::Core::ClassBuilder.new(name: 'MyClass') # # klass = builder.call # klass.name # => "MyClass" # # @example Create named class # builder = Dry::Core::ClassBuilder.new(name: 'User', namespace: Entities) # # klass = builder.call # klass.name # => "Entities::User" # klass.superclass.name # => "Entities::User" # Entities::User # => "Entities::User" # klass.superclass == Entities::User # => true # # @return [Class] def call klass = if namespace create_named else create_anonymous end yield(klass) if block_given? klass end private # @api private def create_anonymous klass = Class.new(parent) name = self.name klass.singleton_class.class_eval do define_method(:name) { name } alias_method :inspect, :name alias_method :to_s, :name end klass end # @api private def create_named name = self.name base = create_base(namespace, name, parent) klass = Class.new(base) namespace.module_eval do remove_const(name) const_set(name, klass) const_get(name).name if RUBY_VERSION < '2.4' remove_const(name) const_set(name, base) end klass end # @api private def create_base(namespace, name, parent) begin namespace.const_get(name) rescue NameError end if namespace.const_defined?(name, false) existing = namespace.const_get(name) unless existing <= parent raise ParentClassMismatch, "#{ existing.name } must be a subclass of #{ parent.name }" end existing else klass = Class.new(parent || Object) namespace.const_set(name, klass) klass end end end end end dry-core-0.4.9/lib/dry/core/class_attributes.rb0000644000175000017500000000360313617242752021403 0ustar utkarshutkarsh# frozen_string_literal: true require 'dry/core/constants' require 'dry/core/errors' module Dry module Core # Internal support module for class-level settings # # @api public module ClassAttributes include Constants # Specify what attributes a class will use # # @example # class ExtraClass # extend Dry::Core::ClassAttributes # # defines :hello # # hello 'world' # end # # @example with inheritance and type checking # # class MyClass # extend Dry::Core::ClassAttributes # # defines :one, :two, type: Integer # # one 1 # two 2 # end # # class OtherClass < MyClass # two 3 # end # # MyClass.one # => 1 # MyClass.two # => 2 # # OtherClass.one # => 1 # OtherClass.two # => 3 # # @example with dry-types # # class Foo # extend Dry::Core::ClassAttributes # # defines :one, :two, type: Dry::Types['strict.int'] # end # def defines(*args, type: Object) mod = Module.new do args.each do |name| define_method(name) do |value = Undefined| ivar = "@#{name}" if value == Undefined if instance_variable_defined?(ivar) instance_variable_get(ivar) else nil end else raise InvalidClassAttributeValue.new(name, value) unless type === value instance_variable_set(ivar, value) end end end define_method(:inherited) do |klass| args.each { |name| klass.send(name, send(name)) } super(klass) end end extend(mod) end end end end dry-core-0.4.9/lib/dry/core/deprecations.rb0000644000175000017500000001464513617242752020520 0ustar utkarshutkarsh# frozen_string_literal: true require 'logger' module Dry module Core # An extension for issueing warnings on using deprecated methods. # # @example # # class Foo # def self.old_class_api; end # def self.new_class_api; end # # deprecate_class_method :old_class_api, :new_class_api # # def old_api; end # def new_api; end # # deprecate_method :old_api, :new_api, "old_api is no-no" # end # # @example You also can use this module for your custom messages # # Dry::Core::Deprecations.announce("Foo", "use bar instead") # Dry::Core::Deprecations.warn("Baz is going to be removed soon") # # @api public module Deprecations STACK = -> { caller.find { |l| l !~ %r{(lib/dry/core)|(gems)} } } class << self # Prints a warning # # @param [String] msg Warning string def warn(msg, tag: nil) tagged = "[#{tag || 'deprecated'}] #{msg.gsub(/^\s+/, '')}" logger.warn(tagged) end # Wraps arguments with a standard message format and prints a warning # # @param [Object] name what is deprecated # @param [String] msg additional message usually containing upgrade instructions def announce(name, msg, tag: nil) warn(deprecation_message(name, msg), tag: tag) end # @api private def deprecation_message(name, msg) <<-MSG #{ name } is deprecated and will be removed in the next major version #{ msg } MSG end # @api private def deprecated_name_message(old, new = nil, msg = nil) if new deprecation_message(old, <<-MSG) Please use #{new} instead. #{msg} MSG else deprecation_message(old, msg) end end # Returns the logger used for printing warnings. # You can provide your own with .set_logger! # # @param [IO] output output stream # # @return [Logger] def logger(output = $stderr) if defined?(@logger) @logger else set_logger!(output) end end # Sets a custom logger. This is a global setting. # # @overload set_logger!(output) # @param [IO] output Stream for messages # # @overload set_logger! # Stream messages to stdout # # @overload set_logger!(logger) # @param [#warn] logger # # @api public def set_logger!(output = $stderr) if output.respond_to?(:warn) @logger = output else @logger = Logger.new(output).tap do |logger| logger.formatter = proc { |_, _, _, msg| "#{ msg }\n" } end end end def [](tag) Tagged.new(tag) end end # @api private class Tagged < Module def initialize(tag) @tag = tag end def extended(base) base.extend Interface base.deprecation_tag @tag end end module Interface # Sets/gets deprecation tag # # @option [String,Symbol] tag tag def deprecation_tag(tag = nil) if defined?(@deprecation_tag) @deprecation_tag else @deprecation_tag = tag end end # Issue a tagged warning message # # @param [String] msg warning message def warn(msg) Deprecations.warn(msg, tag: deprecation_tag) end # Mark instance method as deprecated # # @param [Symbol] old_name deprecated method # @param [Symbol] new_name replacement (not required) # @option [String] message optional deprecation message def deprecate(old_name, new_name = nil, message: nil) full_msg = Deprecations.deprecated_name_message( "#{self.name}##{old_name}", new_name ? "#{self.name}##{new_name}" : nil, message ) mod = self if new_name undef_method old_name if method_defined?(old_name) define_method(old_name) do |*args, &block| mod.warn("#{ full_msg }\n#{ STACK.() }") __send__(new_name, *args, &block) end else aliased_name = :"#{old_name}_without_deprecation" alias_method aliased_name, old_name private aliased_name undef_method old_name define_method(old_name) do |*args, &block| mod.warn("#{ full_msg }\n#{ STACK.() }") __send__(aliased_name, *args, &block) end end end # Mark class-level method as deprecated # # @param [Symbol] old_name deprecated method # @param [Symbol] new_name replacement (not required) # @option [String] message optional deprecation message def deprecate_class_method(old_name, new_name = nil, message: nil) full_msg = Deprecations.deprecated_name_message( "#{self.name}.#{old_name}", new_name ? "#{self.name}.#{new_name}" : nil, message ) meth = new_name ? method(new_name) : method(old_name) singleton_class.instance_exec do undef_method old_name if method_defined?(old_name) define_method(old_name) do |*args, &block| warn("#{ full_msg }\n#{ STACK.() }") meth.call(*args, &block) end end end # Mark a constant as deprecated # @param [Symbol] constant_name constant name to be deprecated # @option [String] message optional deprecation message def deprecate_constant(constant_name, message: nil) value = const_get(constant_name) remove_const(constant_name) full_msg = Deprecations.deprecated_name_message( "#{self.name}::#{constant_name}", message ) mod = Module.new do define_method(:const_missing) do |missing| if missing == constant_name warn("#{ full_msg }\n#{ STACK.() }") value else super(missing) end end end extend(mod) end end end end end dry-core-0.4.9/lib/dry/core/extensions.rb0000644000175000017500000000326113617242752020227 0ustar utkarshutkarsh# frozen_string_literal: true require 'set' module Dry module Core # Define extensions that can be later enabled by the user. # # @example # # class Foo # extend Dry::Core::Extensions # # register_extension(:bar) do # def bar; :bar end # end # end # # Foo.new.bar # => NoMethodError # Foo.load_extensions(:bar) # Foo.new.bar # => :bar # module Extensions # @api private def self.extended(obj) super obj.instance_variable_set(:@__available_extensions__, {}) obj.instance_variable_set(:@__loaded_extensions__, Set.new) end # Register an extension # # @param [Symbol] name extension name # @yield extension block. This block guaranteed not to be called more than once def register_extension(name, &block) @__available_extensions__[name] = block end # Whether an extension is available # # @param [Symbol] name extension name # @return [Boolean] Extension availability def available_extension?(name) @__available_extensions__.key?(name) end # Enables specified extensions. Already enabled extensions remain untouched # # @param [Array] extensions list of extension names def load_extensions(*extensions) extensions.each do |ext| block = @__available_extensions__.fetch(ext) do raise ArgumentError, "Unknown extension: #{ext.inspect}" end unless @__loaded_extensions__.include?(ext) block.call @__loaded_extensions__ << ext end end end end end end