dry-configurable-0.9.0/0000755000175000017500000000000013617272250014705 5ustar utkarshutkarshdry-configurable-0.9.0/CODE_OF_CONDUCT.md0000644000175000017500000000266113617272250017511 0ustar utkarshutkarsh# Contributor Code of Conduct As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.4.0, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct) dry-configurable-0.9.0/CONTRIBUTING.md0000644000175000017500000000312413617272250017136 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 discussing 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 discussion thread, and instead summarize what was discussed. ## Reporting questions, support requests, ideas, concerns etc. **PLEASE DON'T** - use [discourse.dry-rb.org](http://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) or join [our chat](https://dry-rb.zulipchat.com). dry-configurable-0.9.0/.rubocop.yml0000644000175000017500000000241413617272250017160 0ustar utkarshutkarsh# this file is managed by dry-rb/devtools project AllCops: TargetRubyVersion: 2.4 Style/EachWithObject: Enabled: false Style/StringLiterals: Enabled: true EnforcedStyle: single_quotes Style/Alias: Enabled: false Style/LambdaCall: Enabled: false Style/StabbyLambdaParentheses: Enabled: false Style/FormatString: Enabled: false Style/Documentation: Enabled: false Layout/SpaceInLambdaLiteral: Enabled: false Layout/MultilineMethodCallIndentation: Enabled: true EnforcedStyle: indented Metrics/LineLength: Max: 100 Metrics/MethodLength: Max: 22 Metrics/ClassLength: Max: 150 Metrics/AbcSize: Max: 20 Metrics/BlockLength: Enabled: false Metrics/CyclomaticComplexity: Enabled: true Max: 10 Lint/BooleanSymbol: Enabled: false Style/AccessModifierDeclarations: Enabled: false Style/BlockDelimiters: Enabled: false Layout/IndentFirstArrayElement: EnforcedStyle: consistent Style/ClassAndModuleChildren: Exclude: - "spec/**/*_spec.rb" Lint/HandleExceptions: Exclude: - "spec/spec_helper.rb" Naming/FileName: Exclude: - "lib/dry-*.rb" Style/SymbolArray: Exclude: - "spec/**/*_spec.rb" Style/ConditionalAssignment: Enabled: false Naming/MethodName: Enabled: false Style/AsciiComments: Enabled: false dry-configurable-0.9.0/Rakefile0000644000175000017500000000040113617272250016345 0ustar utkarshutkarsh#!/usr/bin/env rake require 'bundler/gem_tasks' $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) require 'rspec/core' require 'rspec/core/rake_task' task default: :spec desc 'Run all specs in spec directory' RSpec::Core::RakeTask.new(:spec) dry-configurable-0.9.0/CHANGELOG.md0000644000175000017500000000643713617272250016530 0ustar utkarshutkarsh## 0.9.0 - 2019-11-06 ## Fixed - Support for reserved names in settings. Some Kernel methods (`public_send` and `class` specifically) are not available if you use access settings via method call. Same for methods of the `Config` class. You can still access them with `[]` and `[]=`. Ruby keywords are fully supported. Invalid names containing special symbols (including `!` and `?`) are rejected. Note that these changes don't affect the `reader` option, if you define a setting named `:class` and pass `reader: true` ... well ... (flash-gordon) - Settings can be redefined in subclasses without a warning about overriding exsting methods (flash-gordon) - Fix warnings about using keyword arguments in 2.7 (koic) [Compare v0.8.3...0.9.0](https://github.com/dry-rb/dry-configurable/compare/v0.8.3...0.9.0) ## 0.8.3 - 2019-05-29 ## Fixed - `Configurable#dup` and `Configurable#clone` make a copy of instance-level config so that it doesn't get mutated/shared across instances (flash-gordon) [Compare v0.8.2...v0.8.3](https://github.com/dry-rb/dry-configurable/compare/v0.8.2...v0.8.3) ## 0.8.2 - 2019-02-25 ## Fixed - Test interface support for modules ([Neznauy](https://github.com/Neznauy)) [Compare v0.8.1...v0.8.2](https://github.com/dry-rb/dry-configurable/compare/v0.8.1...v0.8.2) ## 0.8.1 - 2019-02-06 ## Fixed - `.configure` doesn't require a block, this makes the behavior consistent with the previous versions ([flash-gordon](https://github.com/flash-gordon)) [Compare v0.8.0...v0.8.1](https://github.com/dry-rb/dry-configurable/compare/v0.8.0...v0.8.1) ## 0.8.0 - 2019-02-05 ## Fixed - A number of bugs related to inheriting settings from parent class were fixed. Ideally, new behavior will be :100: predictable but if you observe any anomaly, please report ([flash-gordon](https://github.com/flash-gordon)) ## Added - Support for instance-level configuration landed. For usage, `include` the module instead of extending ([flash-gordon](https://github.com/flash-gordon)) ```ruby class App include Dry::Configurable setting :database end production = App.new production.config.database = ENV['DATABASE_URL'] production.finalize! development = App.new development.config.database = 'jdbc:sqlite:memory' development.finalize! ``` - Config values can be set from a hash with `.update`. Nested settings are supported ([flash-gordon](https://github.com/flash-gordon)) ```ruby class App extend Dry::Configurable setting :db do setting :host setting :port end config.update(YAML.load(File.read("config.yml"))) end ``` ## Changed - [BREAKING] Minimal supported Ruby version is set to 2.3 ([flash-gordon](https://github.com/flash-gordon)) [Compare v0.7.0...v0.8.0](https://github.com/dry-rb/dry-configurable/compare/v0.7.0...v0.8.0) ## 0.7.0 - 2017-04-25 ## Added - Introduce `Configurable.finalize!` which freezes config and its dependencies ([qcam](https://github.com/qcam)) ## Fixed - Allow for boolean false as default setting value ([yuszuv](https://github.com/yuszuv)) - Convert nested configs to nested hashes with `Config#to_h` ([saverio-kantox](https://github.com/saverio-kantox)) - Disallow modification on frozen config ([qcam](https://github.com/qcam)) [Compare v0.6.2...v0.7.0](https://github.com/dry-rb/dry-configurable/compare/v0.6.2...v0.7.0) dry-configurable-0.9.0/.codeclimate.yml0000644000175000017500000000025013617272250017754 0ustar utkarshutkarsh# this file is managed by dry-rb/devtools project version: "2" exclude_patterns: - "benchmarks/" - "examples/" - "spec/" plugins: rubocop: enabled: true dry-configurable-0.9.0/.gitignore0000644000175000017500000000013213617272250016671 0ustar utkarshutkarsh.DS_Store coverage /.bundle vendor/bundle tmp/ .idea/ Gemfile.lock spec/examples.txt pkg/ dry-configurable-0.9.0/Gemfile0000644000175000017500000000057213617272250016204 0ustar utkarshutkarshsource 'https://rubygems.org' gemspec group :test do platforms :mri do gem 'codeclimate-test-reporter', require: false gem 'simplecov', require: false end gem 'warning' end group :tools do gem 'guard' gem 'guard-rspec' gem 'listen', '3.0.6' gem 'pry-byebug', platform: :mri gem "ossy", git: "https://github.com/solnic/ossy.git", branch: "master" end dry-configurable-0.9.0/README.md0000644000175000017500000000237513617272250016173 0ustar utkarshutkarsh[gitter]: https://gitter.im/dry-rb/chat [gem]: https://rubygems.org/gems/dry-configurable [travis]: https://travis-ci.org/dry-rb/dry-configurable [inch]: http://inch-ci.org/github/dry-rb/dry-configurable [chat]: https://dry-rb.zulipchat.com # dry-configurable [![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-configurable.svg)][gem] [![Build Status](https://img.shields.io/travis/dry-rb/dry-configurable.svg)][travis] [![Maintainability](https://api.codeclimate.com/v1/badges/25311e81391498d6b7c8/maintainability)](https://codeclimate.com/github/dry-rb/dry-configurable/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/25311e81391498d6b7c8/test_coverage)](https://codeclimate.com/github/dry-rb/dry-configurable/test_coverage) [![API Documentation Coverage](http://inch-ci.org/github/dry-rb/dry-configurable.svg)][inch] ## Installation Add this line to your application's Gemfile: ```ruby gem 'dry-configurable' ``` And then execute: ```sh $ bundle ``` Or install it yourself as: ```sh $ gem install dry-configurable ``` ## Links * [Documentation](http://dry-rb.org/gems/dry-configurable) ## License See `LICENSE` file. dry-configurable-0.9.0/.rspec0000644000175000017500000000005613617272250016023 0ustar utkarshutkarsh--color --require spec_helper --order random dry-configurable-0.9.0/docsite/0000755000175000017500000000000013617272250016337 5ustar utkarshutkarshdry-configurable-0.9.0/docsite/source/0000755000175000017500000000000013617272250017637 5ustar utkarshutkarshdry-configurable-0.9.0/docsite/source/index.html.md0000644000175000017500000000304413617272250022234 0ustar utkarshutkarsh--- title: Introduction & Usage description: Thread-safe configuration mixin layout: gem-single order: 7 type: gem name: dry-configurable sections: - testing --- ### Introduction `dry-configurable` is a simple mixin to add thread-safe configuration behaviour to your classes. There are many libraries that make use of configuration, and each seemed to have their own implementation with a similar or duplicate interface, so we thought it was strange that this behaviour had not already been encapsulated into a reusable gem, hence `dry-configurable` was born. ### Usage `dry-configurable` is extremely simple to use, just extend the mixin and use the `setting` macro to add configuration options: ```ruby class App extend Dry::Configurable # Pass a block for nested configuration (works to any depth) setting :database do # Can pass a default value setting :dsn, 'sqlite:memory' end # Defaults to nil if no default value is given setting :adapter # Pre-process values setting(:path, 'test') { |value| Pathname(value) } # Passing the reader option as true will create attr_reader method for the class setting :pool, 5, reader: true # Passing the reader attributes works with nested configuration setting :uploader, reader: true do setting :bucket, 'dev' end end App.config.database.dsn # => "sqlite:memory" App.config.database.dsn = 'jdbc:sqlite:memory' App.config.database.dsn # => "jdbc:sqlite:memory" App.config.adapter # => nil App.config.path # => # App.pool # => 5 App.uploader.bucket # => 'dev' ``` dry-configurable-0.9.0/docsite/source/testing.html.md0000644000175000017500000000074013617272250022602 0ustar utkarshutkarsh--- title: Testing layout: gem-single name: dry-configurable --- ### How to reset the config to its original state on testing environment update `spec_helper.rb` : ```ruby require "dry/configurable/test_interface" # this is your module/class that extended by Dry::Configurable module AwesomeModule enable_test_interface end ``` and on spec file (`xxx_spec.rb`) : ```ruby before(:all) { AwesomeModule.reset_config } # or before(:each) { AwesomeModule.reset_config } ``` dry-configurable-0.9.0/LICENSE0000644000175000017500000000207313617272250015714 0ustar utkarshutkarshThe MIT License (MIT) Copyright (c) 2015-2019 dry-rb team 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-configurable-0.9.0/.github/0000755000175000017500000000000013617272250016245 5ustar utkarshutkarshdry-configurable-0.9.0/.github/workflows/0000755000175000017500000000000013617272250020302 5ustar utkarshutkarshdry-configurable-0.9.0/.github/workflows/docsite.yml0000644000175000017500000000152013617272250022455 0ustar utkarshutkarsh# this file is managed by dry-rb/devtools project name: docsite on: push: paths: - docsite/** - .github/workflows/docsite.yml branches: - master - release-** tags: jobs: update-docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Set up Ruby uses: actions/setup-ruby@v1 with: ruby-version: "2.6.x" - name: Install dependencies run: | gem install bundler bundle install --jobs 4 --retry 3 --without benchmarks sql - name: Symlink ossy run: mkdir -p bin && ln -sf "$(bundle show ossy)/bin/ossy" bin/ossy - name: Trigger dry-rb.org deploy env: GITHUB_LOGIN: dry-bot GITHUB_TOKEN: ${{ secrets.GH_PAT }} run: bin/ossy github workflow dry-rb/dry-rb.org ci dry-configurable-0.9.0/.github/workflows/ci.yml0000644000175000017500000000402613617272250021422 0ustar utkarshutkarshname: ci on: push: paths: - .github/workflows/ci.yml - lib/** - spec/** jobs: tests-mri: runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby: ["2.6.x", "2.5.x", "2.4.x"] include: - ruby: "2.6.x" coverage: "true" steps: - uses: actions/checkout@v1 - name: Set up Ruby uses: actions/setup-ruby@v1 with: ruby-version: ${{matrix.ruby}} - name: Download test reporter if: "matrix.coverage == 'true'" run: | mkdir -p tmp/ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./tmp/cc-test-reporter chmod +x ./tmp/cc-test-reporter ./tmp/cc-test-reporter before-build - name: Run all tests env: COVERAGE: ${{matrix.coverage}} run: | gem install bundler bundle install --jobs 4 --retry 3 --without tools docs bundle exec rake - name: Send coverage results if: "matrix.coverage == 'true'" env: CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}} GIT_COMMIT_SHA: ${{github.sha}} GIT_BRANCH: ${{github.ref}} GIT_COMMITTED_AT: ${{github.event.head_commit.timestamp}} run: | GIT_BRANCH=`ruby -e "puts ENV['GITHUB_REF'].split('/', 3).last"` \ GIT_COMMITTED_AT=`ruby -r time -e "puts Time.iso8601(ENV['GIT_COMMITTED_AT']).to_i"` \ ./tmp/cc-test-reporter after-build tests-others: runs-on: ubuntu-latest strategy: fail-fast: false matrix: image: ["jruby:9.2.8", "ruby:rc"] container: image: ${{matrix.image}} steps: - uses: actions/checkout@v1 - name: Install git run: | apt-get update apt-get install -y --no-install-recommends git - name: Run all tests run: | gem install bundler bundle install --jobs 4 --retry 3 --without tools docs bundle exec rspec dry-configurable-0.9.0/.github/workflows/sync_configs.yml0000644000175000017500000000152713617272250023516 0ustar utkarshutkarsh# this file is managed by dry-rb/devtools project name: sync_configs on: repository_dispatch: jobs: sync-configs: runs-on: ubuntu-latest if: github.event.action == 'sync_configs' steps: - uses: actions/checkout@v1 - name: Update configuration files from devtools env: GITHUB_LOGIN: dry-bot GITHUB_TOKEN: ${{ secrets.GH_PAT }} run: | git clone https://github.com/dry-rb/devtools.git tmp/devtools rsync -av tmp/devtools/shared/ . git config --local user.email "dry-bot@dry-rb.org" git config --local user.name "dry-bot" git add -A git commit -m "[devtools] config sync" || echo "nothing changed" - name: Push changes uses: ad-m/github-push-action@master with: github_token: ${{ secrets.GH_PAT }} dry-configurable-0.9.0/.github/ISSUE_TEMPLATE/0000755000175000017500000000000013617272250020430 5ustar utkarshutkarshdry-configurable-0.9.0/.github/ISSUE_TEMPLATE/---bug-report.md0000644000175000017500000000161413617272250023251 0ustar utkarshutkarsh--- name: "\U0001F41B Bug report" about: See CONTRIBUTING.md for more information title: '' labels: bug assignees: '' --- **Before you submit this: WE ONLY ACCEPT BUG REPORTS AND FEATURE REQUESTS** For more information see [our contribution guidelines](https://github.com/rom-rb/rom/blob/master/CONTRIBUTING.md) **Before you report** :warning: If you have a problem related to a schema, please **report it under [dry-schema issues](https://github.com/dry-rb/dry-schema/issues/new?assignees=&labels=bug&template=---bug-report.md&title=)** instead. **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Provide detailed steps to reproduce, an executable script would be best. **Expected behavior** A clear and concise description of what you expected to happen. **Your environment** - Affects my production application: **YES/NO** - Ruby version: ... - OS: ... dry-configurable-0.9.0/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md0000644000175000017500000000022613617272250030666 0ustar utkarshutkarsh--- name: "⚠️ Please don't ask for support via issues" about: See CONTRIBUTING.md for more information title: '' labels: '' assignees: '' --- dry-configurable-0.9.0/.github/ISSUE_TEMPLATE/---feature-request.md0000644000175000017500000000055613617272250024310 0ustar utkarshutkarsh--- name: "\U0001F6E0 Feature request" about: See CONTRIBUTING.md for more information title: '' labels: feature assignees: '' --- Summary of what the feature is supposed to do. ## Examples Code examples showing how the feature could be used. ## Resources Additional information, like a link to the discussion forum thread where the feature was discussed etc. dry-configurable-0.9.0/rakelib/0000755000175000017500000000000013617272250016316 5ustar utkarshutkarshdry-configurable-0.9.0/rakelib/rubocop.rake0000644000175000017500000000055713617272250020642 0ustar utkarshutkarshbegin require 'rubocop/rake_task' Rake::Task[:default].enhance [:rubocop] RuboCop::RakeTask.new do |task| task.options << '--display-cop-names' end namespace :rubocop do desc 'Generate a configuration file acting as a TODO list.' task :auto_gen_config do exec 'bundle exec rubocop --auto-gen-config' end end rescue LoadError end dry-configurable-0.9.0/dry-configurable.gemspec0000644000175000017500000000230613617272250021507 0ustar utkarshutkarshrequire File.expand_path('../lib/dry/configurable/version', __FILE__) Gem::Specification.new do |spec| spec.name = 'dry-configurable' spec.version = Dry::Configurable::VERSION spec.authors = ['Andy Holland'] spec.email = ['andyholland1991@aol.com'] spec.summary = 'A mixin to add configuration functionality to your classes' spec.homepage = 'https://github.com/dry-rb/dry-configurable' spec.license = 'MIT' spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|bin)/}) } spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.metadata = { 'source_code_uri' => 'https://github.com/dry-rb/dry-configurable', 'changelog_uri' => 'https://github.com/dry-rb/dry-configurable/blob/master/CHANGELOG.md' } spec.required_ruby_version = ">= 2.4.0" spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0' spec.add_runtime_dependency 'dry-core', '~> 0.4', '>= 0.4.7' spec.add_development_dependency 'bundler' spec.add_development_dependency 'rake' spec.add_development_dependency 'rspec' end dry-configurable-0.9.0/lib/0000755000175000017500000000000013617272250015453 5ustar utkarshutkarshdry-configurable-0.9.0/lib/dry-configurable.rb0000644000175000017500000000003313617272250021230 0ustar utkarshutkarshrequire 'dry/configurable' dry-configurable-0.9.0/lib/dry/0000755000175000017500000000000013617272250016251 5ustar utkarshutkarshdry-configurable-0.9.0/lib/dry/configurable.rb0000644000175000017500000001030013617272250021230 0ustar utkarshutkarshrequire 'dry/core/constants' require 'dry/configurable/settings' require 'dry/configurable/error' require 'dry/configurable/version' # A collection of micro-libraries, each intended to encapsulate # a common task in Ruby module Dry # A simple configuration mixin # # @example class-level configuration # # class App # extend Dry::Configurable # # setting :database do # setting :dsn, 'sqlite:memory' # end # end # # App.config.database.dsn = 'jdbc:sqlite:memory' # App.config.database.dsn # # => "jdbc:sqlite:memory" # # @example instance-level configuration # # class App # include Dry::Configurable # # setting :database # end # # production = App.new # production.config.database = ENV['DATABASE_URL'] # production.finalize! # # development = App.new # development.config.database = 'jdbc:sqlite:memory' # development.finalize! # # @api public module Configurable include Dry::Core::Constants module ClassMethods # @private def self.extended(base) base.instance_exec do @settings = Settings.new end end # Add a setting to the configuration # # @param [Mixed] key # The accessor key for the configuration value # @param [Mixed] default # The default config value # # @yield # If a block is given, it will be evaluated in the context of # a new configuration class, and bound as the default value # # @return [Dry::Configurable::Config] # # @api public def setting(key, value = Undefined, options = Undefined, &block) raise_already_defined_config(key) if _settings.config_defined? setting = _settings.add(key, value, options, &block) if setting.reader? readers = singleton_class < Configurable ? singleton_class : self readers.send(:define_method, setting.name) { config[setting.name] } end end # Return an array of setting names # # @return [Set] # # @api public def settings _settings.names end # @private no, really... def _settings @settings end private # @private def raise_already_defined_config(key) raise AlreadyDefinedConfig, "Cannot add setting +#{key}+, #{self} is already configured" end # @private def inherited(subclass) parent = self subclass.instance_exec do @settings = parent._settings.dup end if singleton_class < Configurable parent_config = @config subclass.instance_exec do @config = _settings.create_config @config.define!(parent_config.to_h) if parent_config.defined? end end super end end class << self # @private def extended(base) base.extend(ClassMethods) base.class_eval do @config = _settings.create_config end end # @private def included(base) base.extend(ClassMethods) end end # @private def initialize(*) @config = self.class._settings.create_config super end # Return configuration # # @return [Dry::Configurable::Config] # # @api public def config return @config if @config.defined? @config.define! end # Return configuration # # @yield [Dry::Configuration::Config] # # @return [Dry::Configurable::Config] # # @api public def configure raise FrozenConfig, 'Cannot modify frozen config' if frozen? yield(config) if block_given? self end # Finalize and freeze configuration # # @return [Dry::Configurable::Config] # # @api public def finalize! freeze config.finalize! end # @api public def dup super.tap do |copy| copy.instance_variable_set(:@config, config.dup) end end # @api public def clone if frozen? super else super.tap do |copy| copy.instance_variable_set(:@config, config.dup) end end end end end dry-configurable-0.9.0/lib/dry/configurable/0000755000175000017500000000000013617272250020711 5ustar utkarshutkarshdry-configurable-0.9.0/lib/dry/configurable/version.rb0000755000175000017500000000013213617272250022722 0ustar utkarshutkarshmodule Dry module Configurable # @api public VERSION = '0.9.0'.freeze end end dry-configurable-0.9.0/lib/dry/configurable/test_interface.rb0000644000175000017500000000120113617272250024227 0ustar utkarshutkarshmodule Dry module Configurable # Methods meant to be used in a testing scenario module TestInterface # Resets configuration to default values # # @return [Dry::Configurable::Config] # # @api public def reset_config @config = if self.is_a?(Module) _settings.create_config else self.class._settings.create_config end end end # Mixes in test interface into the configurable module # # @api public def enable_test_interface extend Dry::Configurable::TestInterface end end end dry-configurable-0.9.0/lib/dry/configurable/error.rb0000644000175000017500000000024613617272250022371 0ustar utkarshutkarshmodule Dry module Configurable Error = Class.new(::StandardError) AlreadyDefinedConfig = ::Class.new(Error) FrozenConfig = ::Class.new(Error) end end dry-configurable-0.9.0/lib/dry/configurable/setting.rb0000644000175000017500000000155613617272250022722 0ustar utkarshutkarshmodule Dry module Configurable # This class represents a setting and is used internally. # # @private class Setting VALID_NAME = /\A[a-z_]\w*\z/i attr_reader :name attr_reader :options attr_reader :processor def initialize(name, value, processor, options = EMPTY_HASH) unless VALID_NAME =~ name.to_s raise ArgumentError, "+#{name}+ is not a valid setting name" end @name = name.to_sym @value = value @processor = processor @options = options end def value Undefined.default(@value, nil) end def undefined? Undefined.equal?(@value) end def reader? options[:reader] end def node? Settings === @value end def reserved? options[:reserved] end end end end dry-configurable-0.9.0/lib/dry/configurable/settings.rb0000644000175000017500000000471513617272250023105 0ustar utkarshutkarshrequire 'set' require 'concurrent/array' require 'dry/configurable/settings/argument_parser' require 'dry/configurable/setting' require 'dry/configurable/config' module Dry module Configurable # A collection of settings. This is not part of the public API. # # @private class Settings Parser = ArgumentParser.new.freeze class DSL def self.call(&block) new.instance_exec do instance_exec(&block) @settings end end def initialize @settings = Settings.new end def setting(*args, &block) @settings.add(*args, &block) end end # Capture nested config definition # # @return [Dry::Configurable::Setting] def self.capture(&block) DSL.(&block) end attr_reader :settings attr_reader :config_class attr_reader :index private :index def initialize(settings = ::Concurrent::Array.new) @settings = settings @config_class = Config[self] @index = settings.map { |s| [s.name, s] }.to_h yield(self) if block_given? end def add(key, value = Undefined, options = Undefined, &block) extended = singleton_class < Configurable raise_already_defined_config(key) if extended && configured? *args, opts = Parser.(value, options, block) Setting.new(key, *args, { **opts, reserved: reserved?(key) }).tap do |s| settings.delete_if { |e| e.name.eql?(s.name) } settings << s index[s.name] = s @names = nil end end def each settings.each { |s| yield(s) } end def names @names ||= index.keys.to_set end def [](name) index[name] end def empty? settings.empty? end def name?(name) index.key?(name) end def dup Settings.new(settings.dup) end def freeze settings.freeze super end def create_config config_class.new end def config_defined? config_class.config_defined? end def reserved?(name) reserved_names.include?(name) end def reserved_names @reserved_names ||= [ config_class.instance_methods(false), config_class.superclass.instance_methods(false), %i(class public_send) ].reduce(:+) end end end end dry-configurable-0.9.0/lib/dry/configurable/settings/0000755000175000017500000000000013617272250022551 5ustar utkarshutkarshdry-configurable-0.9.0/lib/dry/configurable/settings/argument_parser.rb0000644000175000017500000000265413617272250026303 0ustar utkarshutkarshmodule Dry # Argument parser # # Passing and array or arguments, it will decide which one are arguments # and which one are options. # # We have a limitation if setting the value without options, as a hash # having the same key as one of the valid options, will parse the value # as options. In this case, all unknown options will be reject with an exception. # # @example # p = Dry::Configurable::ArgumentParser.new.('db:sqlite', reader: true) # # p[0] # => 'db:sqlite' # p[1] # => ArgumentParser::DEFAULT_PROCESSOR # p[2] # => { reader: true } module Configurable class Settings # @private class ArgumentParser DEFAULT_PROCESSOR = ->(v) { v } # @private def call(val, opts, block) if block && block.parameters.empty? raise ArgumentError unless Undefined.equal?(opts) processor = DEFAULT_PROCESSOR value, options = Settings.capture(&block), val else processor = block || DEFAULT_PROCESSOR if Undefined.equal?(opts) && val.is_a?(Hash) && val.key?(:reader) value, options = Undefined, val else value, options = val, opts end end [value, processor, options(**Undefined.default(options, EMPTY_HASH))] end def options(reader: false) { reader: reader } end end end end end dry-configurable-0.9.0/lib/dry/configurable/config.rb0000644000175000017500000001015713617272250022507 0ustar utkarshutkarshrequire 'concurrent/hash' module Dry module Configurable # @private class Config class << self # @private def [](settings) ::Class.new(Config) do @settings = settings singleton_class.send(:attr_reader, :settings) @lock = ::Mutex.new @config_defined = false end end # @private def define_accessors! @lock.synchronize do break if config_defined? settings.each do |setting| next if setting.reserved? define_method(setting.name) do @config[setting.name] end define_method("#{setting.name}=") do |value| raise FrozenConfig, 'Cannot modify frozen config' if frozen? @config[setting.name] = setting.processor.(value) end end @config_defined = true end end # @private def config_defined? @config_defined end end def initialize @config = ::Concurrent::Hash.new @lock = ::Mutex.new @defined = false end def defined? @defined end # @private def define!(parent_config = EMPTY_HASH) @lock.synchronize do break if self.defined? self.class.define_accessors! set_values!(parent_config) @defined = true end self end # @private def finalize! define! @config.freeze freeze end # Serialize config to a Hash # # @return [Hash] # # @api public def to_h @config.each_with_object({}) do |(key, value), hash| case value when Config hash[key] = value.to_h else hash[key] = value end end end alias to_hash to_h # Get config value by a key # # @param [String,Symbol] name # # @return Config value def [](name) setting = self.class.settings[name.to_sym] if setting.nil? raise_unknown_setting_error(name) elsif setting.reserved? @config[setting.name] else public_send(name) end end # Set config value. # Note that finalized configs cannot be changed. # # @param [String,Symbol] name # @param [Object] value def []=(name, value) setting = self.class.settings[name.to_sym] if setting.nil? raise_unknown_setting_error(name) elsif setting.reserved? @config[setting.name] = setting.processor.(value) else public_send("#{name}=", value) end end # Whether config has a key # # @param [Symbol] key # @return [Bool] def key?(name) self.class.settings.name?(name) end # Recursively update values from a hash # # @param [Hash] values to set # @return [Config] def update(values) values.each do |key, value| if self[key].is_a?(Config) self[key].update(value) else self[key] = value end end self end def dup if self.defined? self.class.new.define!(to_h) else self.class.new end end private # @private def set_values!(parent) self.class.settings.each do |setting| if parent.key?(setting.name) && !setting.node? @config[setting.name] = parent[setting.name] elsif setting.undefined? @config[setting.name] = nil elsif setting.node? value = setting.value.create_config value.define!(parent.fetch(setting.name, EMPTY_HASH)) self[setting.name] = value else self[setting.name] = setting.value end end end # @private def raise_unknown_setting_error(name) ::Kernel.raise ArgumentError, "+#{name}+ is not a setting name" end end end end