better_settings 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +137 -0
- data/lib/better_settings.rb +110 -0
- data/lib/better_settings/version.rb +5 -0
- data/spec/better_settings/better_settings_spec.rb +126 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/settings.rb +25 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 838170a01ea177f40b02a01a7e7308c6b1cf6528
|
4
|
+
data.tar.gz: 94ddb23c277b6ae2ba9c9eb12cfab77635163d87
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0241b28bfe8bdc7d143c21dbf7526b88142bae2e958c4a9c85a8369ff14c390383a3db14abff98122fc7e2593384d2b0ca3b52fc6a3471700476f9eba173c161
|
7
|
+
data.tar.gz: 254487a597d7e78ca6569d2df82634c48551dfa2715a49533d29bfcd11ade1472df41eebf5cc5d7661fa3a1ba5dfd6610759e21663dc3b8da2ffa03e3aa68f41
|
data/README.md
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
BetterSettings [](https://rubygems.org/gems/better_settings) [](https://travis-ci.org/ElMassimo/better_settings) [](https://coveralls.io/github/ElMassimo/better_settings?branch=master) [](http://inch-ci.org/github/ElMassimo/better_settings) [](https://github.com/ElMassimo/better_settings/blob/master/LICENSE.txt)
|
2
|
+
=======================================
|
3
|
+
|
4
|
+
A robust settings library that can read YML files and provide an immutable object allowing to access settings through method calls. Can be used in __any Ruby app__, __not just Rails__.
|
5
|
+
|
6
|
+
### Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'better_settings'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install better_settings
|
21
|
+
|
22
|
+
### Usage
|
23
|
+
|
24
|
+
#### 1. Define a class
|
25
|
+
|
26
|
+
Instead of defining a Settings constant for you, that task is left to you. Simply create a class in your application
|
27
|
+
that looks like:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# app/models/settings.rb
|
31
|
+
class Settings < BetterSettings
|
32
|
+
source Rails.root.join('config', 'application.yml'), namespace: Rails.env
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
#### 2. Create your settings
|
37
|
+
|
38
|
+
Notice above we specified an absolute path to our settings file called `application.yml`. This is just a typical YAML file.
|
39
|
+
Also notice above that we specified a namespace for our environment. A namespace is just an optional string that corresponds to a key in the YAML file.
|
40
|
+
|
41
|
+
Using a namespace allows us to change our configuration depending on our environment:
|
42
|
+
|
43
|
+
```yaml
|
44
|
+
# config/application.yml
|
45
|
+
defaults: &defaults
|
46
|
+
port: 80
|
47
|
+
mailer:
|
48
|
+
root: www.example.com
|
49
|
+
dynamic: <%= "Did you know you can use ERB inside the YML file? Env is #{ Rails.env }." %>
|
50
|
+
|
51
|
+
development:
|
52
|
+
<<: *defaults
|
53
|
+
port: 3000
|
54
|
+
|
55
|
+
test:
|
56
|
+
<<: *defaults
|
57
|
+
|
58
|
+
production:
|
59
|
+
<<: *defaults
|
60
|
+
```
|
61
|
+
|
62
|
+
#### 3. Access your settings
|
63
|
+
|
64
|
+
>> Rails.env
|
65
|
+
=> "development"
|
66
|
+
|
67
|
+
>> Settings.mailer
|
68
|
+
=> "#<Settings ... >"
|
69
|
+
|
70
|
+
>> Settings.mailer.root
|
71
|
+
=> "www.example.com
|
72
|
+
|
73
|
+
>> Settings.port
|
74
|
+
=> 3000
|
75
|
+
|
76
|
+
>> Settings.dynamic
|
77
|
+
=> "Did you know you can use ERB inside the YML file? Env is development."
|
78
|
+
|
79
|
+
You can use these settings anywhere, for example in a model:
|
80
|
+
|
81
|
+
class Post < ActiveRecord::Base
|
82
|
+
self.per_page = Settings.pagination.posts_per_page
|
83
|
+
end
|
84
|
+
|
85
|
+
### Advanced Setup ⚙
|
86
|
+
Name it `Settings`, name it `Config`, name it whatever you want. Add as many or as few as you like, read from as many files as necessary (nested keys will be merged).
|
87
|
+
|
88
|
+
We usually read a few optional files for the `development` and `test` environment, which allows each developer to override some settings in their own local environment (we git ignore `development.yml` and `test.yml`).
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# app/models/settings.rb
|
92
|
+
class Settings < BetterSettings
|
93
|
+
source Rails.root.join('config', 'application.yml'), namespace: Rails.env
|
94
|
+
source Rails.root.join('config', 'development.yml'), namespace: Rails.env, optional: true if Rails.env.development?
|
95
|
+
source Rails.root.join('config', 'test.yml'), namespace: Rails.env, optional: true if Rails.env.test?
|
96
|
+
end
|
97
|
+
```
|
98
|
+
Our `application.yml` looks like this:
|
99
|
+
```yaml
|
100
|
+
# application.yml
|
101
|
+
defaults: &defaults
|
102
|
+
auto_logout: false
|
103
|
+
secret_key_base: 'fake_secret_key_base'
|
104
|
+
|
105
|
+
server_defaults: &server_defaults
|
106
|
+
<<: *defaults
|
107
|
+
auto_logout: true
|
108
|
+
secret_key: <%= ENV['SECRET_KEY'] %>
|
109
|
+
|
110
|
+
development:
|
111
|
+
<<: *defaults
|
112
|
+
host: 'localhost'
|
113
|
+
|
114
|
+
test:
|
115
|
+
<<: *defaults
|
116
|
+
host: '127.0.0.1'
|
117
|
+
|
118
|
+
staging:
|
119
|
+
<<: *server_defaults
|
120
|
+
host: 'staging.example.com'
|
121
|
+
|
122
|
+
production:
|
123
|
+
<<: *server_defaults
|
124
|
+
host: 'example.com'
|
125
|
+
```
|
126
|
+
A developer might want to override some settings by defining a `development.yml` such as:
|
127
|
+
```yaml
|
128
|
+
development:
|
129
|
+
auto_logout: true
|
130
|
+
````
|
131
|
+
The main advantage is that those changes won't be tracked by source control :smiley:
|
132
|
+
|
133
|
+
## Opinionated Design
|
134
|
+
After using [settingslogic](https://github.com/settingslogic/settingslogic) for a long time, we learned some lessons, which are distilled in the following decisions:
|
135
|
+
- __Immutability:__ Once created settings can't be modified.
|
136
|
+
- __No Optional Setings:__ Any optional setting can be modeled in a safer way, this library doesn't allow them.
|
137
|
+
- __Not Tied to a Source File:__ Useful to create multiple environment-specific files.
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'erb'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'forwardable'
|
7
|
+
|
8
|
+
# Public: Rewrite of BetterSettings to enforce fail-fast and immutability, and
|
9
|
+
# avoid extending a core class like Hash which can be problematic.
|
10
|
+
class BetterSettings
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
VALID_SETTING_NAME = /^\w+$/
|
14
|
+
RESERVED_METHODS = %w[
|
15
|
+
settings
|
16
|
+
root_settings
|
17
|
+
]
|
18
|
+
|
19
|
+
attr_reader :settings
|
20
|
+
def_delegators :settings, :to_h, :to_hash
|
21
|
+
|
22
|
+
# Public: Initializes a new settings object from a Hash or compatible object.
|
23
|
+
def initialize(hash, parent:)
|
24
|
+
@settings = hash.to_h.freeze
|
25
|
+
@parent = parent
|
26
|
+
|
27
|
+
# Create a getter method for each setting.
|
28
|
+
@settings.each { |key, value| create_accessor(key, value) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Internal: Returns a new Better Settings instance that combines the settings.
|
32
|
+
def merge(other_settings)
|
33
|
+
self.class.new(deep_merge(@settings, other_settings.to_h), parent: @parent)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Internal: Display explicit errors for typos and missing settings.
|
37
|
+
# rubocop:disable Style/MethodMissing
|
38
|
+
def method_missing(name, *)
|
39
|
+
raise MissingSetting, "Missing setting '#{ name }' in #{ @parent }"
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Internal: Wrap nested hashes as settings to allow accessing keys as methods.
|
45
|
+
def auto_wrap(key, value)
|
46
|
+
case value
|
47
|
+
when Hash then self.class.new(value, parent: "'#{ key }' section in #{ @parent }")
|
48
|
+
when Array then value.map { |item| auto_wrap(key, item) }.freeze
|
49
|
+
else value.freeze
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Internal: Defines a getter for the specified setting.
|
54
|
+
def create_accessor(key, value)
|
55
|
+
raise InvalidSettingKey if !key.is_a?(String) || key !~ VALID_SETTING_NAME || RESERVED_METHODS.include?(key)
|
56
|
+
instance_variable_set("@#{ key }", auto_wrap(key, value))
|
57
|
+
singleton_class.send(:attr_reader, key)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Internal: Recursively merges two hashes (in case ActiveSupport is not available).
|
61
|
+
def deep_merge(this_hash, other_hash)
|
62
|
+
this_hash.merge(other_hash) do |key, this_val, other_val|
|
63
|
+
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
64
|
+
deep_merge(this_val, other_val)
|
65
|
+
else
|
66
|
+
other_val
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class MissingSetting < StandardError; end
|
72
|
+
class InvalidSettingKey < StandardError; end
|
73
|
+
|
74
|
+
class << self
|
75
|
+
extend Forwardable
|
76
|
+
def_delegators :root_settings, :to_h, :to_hash, :method_missing
|
77
|
+
|
78
|
+
# Public: Loads a file as settings (merges it with any previously loaded settings).
|
79
|
+
def source(file_name, namespace: false, optional: false)
|
80
|
+
return if !File.exist?(file_name) && optional
|
81
|
+
|
82
|
+
# Load the specified yaml file and instantiate a Settings object.
|
83
|
+
settings = new(yaml_to_hash(file_name), parent: file_name)
|
84
|
+
|
85
|
+
# Take one of the settings keys if one is specified.
|
86
|
+
settings = settings.public_send(namespace) if namespace
|
87
|
+
|
88
|
+
# Merge settings if a source had previously been specified.
|
89
|
+
@root_settings = @root_settings ? @root_settings.merge(settings) : settings
|
90
|
+
|
91
|
+
# Allow to call any settings methods directly on the class.
|
92
|
+
singleton_class.extend(Forwardable)
|
93
|
+
singleton_class.def_delegators :root_settings, *@root_settings.settings.keys
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# Internal: Methods called at the class level are delegated to this instance.
|
99
|
+
def root_settings
|
100
|
+
raise ArgumentError, '`source` must be specified for the settings' unless defined?(@root_settings)
|
101
|
+
@root_settings
|
102
|
+
end
|
103
|
+
|
104
|
+
# Internal: Parses a yml file that can optionally use ERB templating.
|
105
|
+
def yaml_to_hash(file_name)
|
106
|
+
return {} if (content = open(file_name).read).empty?
|
107
|
+
YAML.load(ERB.new(content).result).to_hash
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/settings'
|
3
|
+
|
4
|
+
describe BetterSettings do
|
5
|
+
def new_settings(value)
|
6
|
+
Settings.new(value, parent: 'new_settings')
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should access settings' do
|
10
|
+
expect(Settings.setting2).to eq 5
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should access nested settings' do
|
14
|
+
expect(Settings.setting1.setting1_child).to eq 'saweet'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should access settings in nested arrays' do
|
18
|
+
expect(Settings.array.first.name).to eq 'first'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should access deep nested settings' do
|
22
|
+
expect(Settings.setting1.deep.another).to eq 'my value'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should access extra deep nested settings' do
|
26
|
+
expect(Settings.setting1.deep.child.value).to eq 2
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should enable erb' do
|
30
|
+
expect(Settings.setting3).to eq 25
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should namespace settings' do
|
34
|
+
expect(DevSettings.language.haskell.paradigm).to eq 'functional'
|
35
|
+
expect(DevSettings.language.smalltalk.paradigm).to eq 'object-oriented'
|
36
|
+
expect(DevSettings.environment).to eq 'development'
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should distinguish nested keys' do
|
40
|
+
expect(Settings.language.haskell.paradigm).to eq 'functional'
|
41
|
+
expect(Settings.language.smalltalk.paradigm).to eq 'object oriented'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should not override global methods' do
|
45
|
+
expect(Settings.global).to eq 'GLOBAL'
|
46
|
+
expect(Settings.custom).to eq 'CUSTOM'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should raise a helpful error message' do
|
50
|
+
expect {
|
51
|
+
Settings.missing
|
52
|
+
}.to raise_error(BetterSettings::MissingSetting, /Missing setting 'missing' in/)
|
53
|
+
expect {
|
54
|
+
Settings.language.missing
|
55
|
+
}.to raise_error(BetterSettings::MissingSetting, /Missing setting 'missing' in 'language' section/)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should raise an error on a nil source argument' do
|
59
|
+
expect { NoSource.foo.bar }.to raise_error(ArgumentError, '`source` must be specified for the settings')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should support instance usage as well' do
|
63
|
+
expect(new_settings(Settings.setting1).setting1_child).to eq 'saweet'
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should handle invalid name settings' do
|
67
|
+
expect {
|
68
|
+
new_settings('some-dash-setting#' => 'dashtastic')
|
69
|
+
}.to raise_error(BetterSettings::InvalidSettingKey)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should handle settings with nil value' do
|
73
|
+
expect(Settings.nil).to eq nil
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should handle settings with false value' do
|
77
|
+
expect(Settings.false).to eq false
|
78
|
+
end
|
79
|
+
|
80
|
+
# If .name is called on BetterSettings itself, handle appropriately
|
81
|
+
# by delegating to Hash
|
82
|
+
it 'should have the parent class always respond with Module.name' do
|
83
|
+
expect(BetterSettings.name).to eq 'BetterSettings'
|
84
|
+
end
|
85
|
+
|
86
|
+
# If .name is not a property, delegate to superclass
|
87
|
+
it 'should respond with Module.name' do
|
88
|
+
expect(DevSettings.name).to eq 'DevSettings'
|
89
|
+
end
|
90
|
+
|
91
|
+
# If .name is a property, respond with that instead of delegating to superclass
|
92
|
+
it 'should allow a name setting to be overriden' do
|
93
|
+
expect(Settings.name).to eq 'test'
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'to_h' do
|
97
|
+
it 'should handle empty file' do
|
98
|
+
expect(NoSettings.to_h).to be_empty
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should be similar to the internal representation' do
|
102
|
+
expect(settings = Settings.send(:root_settings)).to be_is_a(Settings)
|
103
|
+
expect(hash = settings.send(:settings)).to be_is_a(Hash)
|
104
|
+
expect(Settings.to_h).to eq hash
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should not mutate the original when getting a copy' do
|
108
|
+
result = Settings.language.to_h.merge('haskell' => 'awesome')
|
109
|
+
expect(result.class).to eq Hash
|
110
|
+
expect(result).to eq(
|
111
|
+
'haskell' => 'awesome',
|
112
|
+
'smalltalk' => { 'paradigm' => 'object oriented' },
|
113
|
+
)
|
114
|
+
expect(Settings.language.haskell.paradigm).to eq('functional')
|
115
|
+
expect(Settings.language).not_to eq Settings.language.merge('paradigm' => 'functional')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#to_hash' do
|
120
|
+
it 'should return a new instance of a Hash object' do
|
121
|
+
expect(Settings.to_hash).to be_kind_of(Hash)
|
122
|
+
expect(Settings.to_hash.class.name).to eq 'Hash'
|
123
|
+
expect(Settings.to_hash.object_id).not_to eq Settings.object_id
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class Settings < BetterSettings
|
2
|
+
source "#{ File.dirname(__FILE__) }/settings.yml"
|
3
|
+
source "#{File.dirname(__FILE__)}/settings_empty.yml"
|
4
|
+
|
5
|
+
def self.custom
|
6
|
+
'CUSTOM'
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.global
|
10
|
+
'GLOBAL'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class DevSettings < BetterSettings
|
15
|
+
source "#{ File.dirname(__FILE__) }/settings.yml", namespace: :development
|
16
|
+
source "#{ File.dirname(__FILE__) }/dev.yml", namespace: 'development'
|
17
|
+
end
|
18
|
+
|
19
|
+
class NoSettings < BetterSettings
|
20
|
+
source "#{File.dirname(__FILE__)}/settings_empty.yml", optional: true
|
21
|
+
source "#{File.dirname(__FILE__)}/settings_none.yml", optional: true
|
22
|
+
end
|
23
|
+
|
24
|
+
class NoSource < BetterSettings
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: better_settings
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Máximo Mussini
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-12-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: coveralls
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry-byebug
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-given
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
description: Settings solution for Rails applications that can read YAML files (ERB-enabled)
|
70
|
+
and allows to access using method calls.
|
71
|
+
email:
|
72
|
+
- maximomussini@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files:
|
76
|
+
- README.md
|
77
|
+
files:
|
78
|
+
- README.md
|
79
|
+
- lib/better_settings.rb
|
80
|
+
- lib/better_settings/version.rb
|
81
|
+
- spec/better_settings/better_settings_spec.rb
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
- spec/support/settings.rb
|
84
|
+
homepage: https://github.com/ElMassimo/better_settings
|
85
|
+
licenses:
|
86
|
+
- MIT
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.2'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.6.11
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: 'Settings for Rails applications: simple, immutable, better.'
|
108
|
+
test_files:
|
109
|
+
- spec/better_settings/better_settings_spec.rb
|
110
|
+
- spec/spec_helper.rb
|
111
|
+
- spec/support/settings.rb
|