anyway_config 0.5.1.rc1 → 0.5.1
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 +4 -4
- data/.gitignore +1 -3
- data/.hound.yml +12 -0
- data/.rubocop.yml +8 -10
- data/.travis.yml +3 -12
- data/Gemfile +1 -3
- data/LICENSE.txt +1 -1
- data/README.md +45 -79
- data/Rakefile +3 -7
- data/anyway_config.gemspec +5 -13
- data/lib/anyway.rb +0 -2
- data/lib/anyway/config.rb +5 -16
- data/lib/anyway/env.rb +15 -16
- data/lib/anyway/ext/class.rb +1 -3
- data/lib/anyway/ext/deep_dup.rb +2 -14
- data/lib/anyway/ext/hash.rb +1 -3
- data/lib/anyway/rails/config.rb +2 -3
- data/lib/anyway/version.rb +1 -3
- data/spec/config_spec.rb +13 -24
- data/spec/config_spec_norails.rb +7 -28
- data/spec/dummy.yml +0 -0
- data/spec/dummy/config/cool.yml +1 -1
- data/spec/dummy/config/secrets.yml +1 -0
- data/spec/env_spec.rb +13 -17
- data/spec/spec_helper.rb +2 -13
- data/spec/spec_norails_helper.rb +2 -13
- data/spec/support/cool_config.rb +0 -2
- data/spec/support/test_config.rb +2 -4
- metadata +9 -28
- data/CHANGELOG.md +0 -21
- data/config/cool.yml +0 -5
- data/gemfiles/railsmaster.gemfile +0 -6
- data/lib/anyway_config.rb +0 -3
- data/spec/ext/deep_dup_spec.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 809092685f06ffac464aa29746797c1fec47f899
|
4
|
+
data.tar.gz: 37855a5237e91c0a0958ce89c7cd4772af85689a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c818eacf91c5364efe0df64de0ee36b8dacaab2ed7df2594265fac8bdc29f02d5f0a72fa82131f46324332c7a84d170e260cba6fb91cea378b70e91963132b2e
|
7
|
+
data.tar.gz: eb0806e2ad07292ef47a363fd212c5b41f16b3f51dbf4d7a4957e51438351eef4c4c9bd06ede20042395ddd13bbb04edd1dfe578652212c2f1adf839a346071b
|
data/.gitignore
CHANGED
data/.hound.yml
ADDED
data/.rubocop.yml
CHANGED
@@ -1,19 +1,15 @@
|
|
1
1
|
AllCops:
|
2
|
+
# Include gemspec and Rakefile
|
2
3
|
Include:
|
3
4
|
- 'lib/**/*.rb'
|
4
5
|
- 'lib/**/*.rake'
|
5
6
|
- 'spec/**/*.rb'
|
6
|
-
- 'Gemfile'
|
7
|
-
- 'Rakefile'
|
8
7
|
Exclude:
|
9
8
|
- 'bin/**/*'
|
10
9
|
- 'spec/dummy/**/*'
|
11
|
-
|
12
|
-
- 'gemfiles/vendor/**/*'
|
13
|
-
- 'vendor/**/*'
|
10
|
+
RunRailsCops: true
|
14
11
|
DisplayCopNames: true
|
15
12
|
StyleGuideCopsOnly: false
|
16
|
-
TargetRubyVersion: 2.3
|
17
13
|
|
18
14
|
Style/Documentation:
|
19
15
|
Exclude:
|
@@ -30,9 +26,11 @@ Metrics/MethodLength:
|
|
30
26
|
Exclude:
|
31
27
|
- 'spec/**/*.rb'
|
32
28
|
|
33
|
-
Metrics/BlockLength:
|
34
|
-
Exclude:
|
35
|
-
- 'spec/**/*.rb'
|
36
|
-
|
37
29
|
Metrics/LineLength:
|
38
30
|
Max: 100
|
31
|
+
|
32
|
+
Rails/Date:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
Rails/TimeZone:
|
36
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
language: ruby
|
2
2
|
cache: bundler
|
3
3
|
|
4
|
-
dist: trusty
|
5
|
-
sudo: false
|
6
|
-
|
7
4
|
notifications:
|
8
5
|
email: false
|
9
6
|
|
@@ -13,14 +10,8 @@ before_install:
|
|
13
10
|
|
14
11
|
matrix:
|
15
12
|
include:
|
16
|
-
- rvm: ruby-head
|
17
|
-
gemfile: gemfiles/railsmaster.gemfile
|
18
|
-
- rvm: 2.4.1
|
19
|
-
gemfile: gemfiles/rails5.gemfile
|
20
13
|
- rvm: 2.3.1
|
21
14
|
gemfile: gemfiles/rails42.gemfile
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
- rvm: ruby-head
|
26
|
-
gemfile: gemfiles/railsmaster.gemfile
|
15
|
+
|
16
|
+
- rvm: 2.4.0
|
17
|
+
gemfile: gemfiles/rails5.gemfile
|
data/Gemfile
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
source 'https://rubygems.org'
|
4
2
|
|
5
3
|
# Specify your gem's dependencies in anyway_config.gemspec
|
@@ -9,7 +7,7 @@ gemspec
|
|
9
7
|
local_gemfile = "#{File.dirname(__FILE__)}/Gemfile.local"
|
10
8
|
|
11
9
|
if File.exist?(local_gemfile)
|
12
|
-
eval(File.read(local_gemfile)) # rubocop:disable
|
10
|
+
eval(File.read(local_gemfile)) # rubocop:disable Lint/Eval
|
13
11
|
else
|
14
12
|
gem 'rails', '~> 5.0'
|
15
13
|
end
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -2,45 +2,34 @@
|
|
2
2
|
|
3
3
|
# Anyway Config
|
4
4
|
|
5
|
-
Rails/Ruby plugin/application configuration
|
5
|
+
Rails/Ruby plugin/application configuration using any source: YAML, _secrets_, environment.
|
6
6
|
|
7
|
-
Apps using AnywayConfig:
|
8
7
|
|
8
|
+
Apps using Anyway Config:
|
9
9
|
- [Influxer](https://github.com/palkan/influxer)
|
10
|
+
- [AnyCable](https://github.com/anycable/anycable).
|
10
11
|
|
11
|
-
|
12
|
+
## Using with Gem
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
## Installation
|
16
|
-
|
17
|
-
1) Adding to a gem:
|
14
|
+
Configure your gemspec:
|
18
15
|
|
19
16
|
```ruby
|
20
|
-
|
21
|
-
Gem::Specification.new do |spec|
|
17
|
+
Gem::Specification.new do |s|
|
22
18
|
...
|
23
|
-
|
19
|
+
s.add_dependancy 'anyway_config', "~>0.5"
|
24
20
|
...
|
25
21
|
end
|
26
22
|
```
|
27
23
|
|
28
|
-
|
24
|
+
And then execute:
|
29
25
|
|
30
|
-
|
31
|
-
# Gemfile
|
32
|
-
gem "anyway_config", "~> 1.0"
|
33
|
-
```
|
26
|
+
$ bundle
|
34
27
|
|
35
|
-
|
28
|
+
Or install it yourself as:
|
36
29
|
|
37
|
-
|
38
|
-
$ gem install anyway_config
|
39
|
-
```
|
30
|
+
$ gem install anyway_config
|
40
31
|
|
41
|
-
|
42
|
-
|
43
|
-
### Pre-defined configuration
|
32
|
+
### Usage
|
44
33
|
|
45
34
|
Create configuration class:
|
46
35
|
|
@@ -60,21 +49,11 @@ end
|
|
60
49
|
attr_config :user, :password, host: 'localhost'
|
61
50
|
```
|
62
51
|
|
63
|
-
|
52
|
+
Your config will be filled up with values from `RAILS_ROOT/config/my_cool_gem.yml`, `Rails.application.secrets.my_cool_gem` (if using Rails) and `ENV['MYCOOLGEM_*']`.
|
64
53
|
|
65
|
-
|
66
|
-
module MyCoolGem
|
67
|
-
def self.config
|
68
|
-
@config ||= Config.new
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
MyCoolGem.config.user #=> 'root'
|
73
|
-
```
|
74
|
-
|
75
|
-
#### Customize name
|
54
|
+
### Customize name
|
76
55
|
|
77
|
-
|
56
|
+
If you want to load config params from, for example, "cool.yml" (secrets, env), just add one line:
|
78
57
|
|
79
58
|
```ruby
|
80
59
|
module MyCoolGem
|
@@ -85,41 +64,30 @@ module MyCoolGem
|
|
85
64
|
end
|
86
65
|
```
|
87
66
|
|
88
|
-
###
|
89
|
-
|
90
|
-
You can also create configuration objects without pre-defined schema (just like `Rails.application.config_for` but more [powerful](#railsapplicationconfig_for-vs-anywayconfigfor)):
|
91
|
-
|
92
|
-
```ruby
|
93
|
-
# load data from config/my_app.yml, secrets.my_app (if using Rails), ENV["MYAPP_*"]
|
94
|
-
config = Anyway::Config.for(:my_app)
|
95
|
-
```
|
96
|
-
|
97
|
-
### Using with Rails
|
98
|
-
|
99
|
-
Your config will be filled up with values from the following sources (ordered by priority from low to high):
|
100
|
-
|
101
|
-
- `RAILS_ROOT/config/my_cool_gem.yml` (for the current `RAILS_ENV`, supports `ERB`)
|
67
|
+
### Config clear and reload
|
102
68
|
|
103
|
-
|
69
|
+
You can use `clear` and `reload` functions on your config (which do exactly what they state).
|
104
70
|
|
105
|
-
- `ENV['MYCOOLGEM_*']`.
|
106
71
|
|
107
|
-
|
72
|
+
## Using with Rails app
|
108
73
|
|
109
|
-
|
110
|
-
through special environment variable – 'MYGEM_CONF' – containing the path to the YAML file.
|
74
|
+
In your Gemfile
|
111
75
|
|
112
|
-
|
76
|
+
```ruby
|
77
|
+
require 'anyway_config', "~>0.5", require: 'anyway'
|
78
|
+
```
|
113
79
|
|
80
|
+
In your code
|
114
81
|
|
115
|
-
|
82
|
+
```ruby
|
116
83
|
|
117
|
-
|
84
|
+
config = Anyway::Config.for(:my_app) # load data from config/my_app.yml, secrets.my_app, ENV["MYAPP_*"]
|
118
85
|
|
86
|
+
```
|
119
87
|
|
120
88
|
## `Rails.application.config_for` vs `Anyway::Config.for`
|
121
89
|
|
122
|
-
Rails 4.2
|
90
|
+
Rails 4.2 introduces new feature: `Rails.application.config_for`. It looks very similar to
|
123
91
|
`Anyway::Config.for`, but there are some differences:
|
124
92
|
|
125
93
|
| Feature | Rails | Anyway |
|
@@ -128,33 +96,26 @@ Rails 4.2 introduced new feature: `Rails.application.config_for`. It looks very
|
|
128
96
|
| load data from `secrets` | no | yes |
|
129
97
|
| load data from environment | no | yes |
|
130
98
|
| return Hash with indifferent access | no | yes |
|
131
|
-
| support ERB within `config/app.yml` | yes |
|
99
|
+
| support ERB within `config/app.yml` | yes | no |
|
132
100
|
| raise errors if file doesn't exist | yes | no |
|
133
101
|
|
134
|
-
|
135
|
-
|
136
|
-
But the main advantage of Anyway::Config is that it can be used [without Rails](#using-with-ruby)!)
|
102
|
+
But the main advantage of Anyway::Config is that it can be used [without Rails](#using-without-rails)!
|
137
103
|
|
138
104
|
## How to set env vars
|
139
105
|
|
140
|
-
Environmental variables for your config should start with your config name, uppercased and underscore-free.
|
141
|
-
|
142
|
-
For example, if your module is called "MyCoolGem" then the env var "MYCOOLGEM_PASSWORD" is used as `config.password`.
|
106
|
+
Environmental variables for your config should start with your module name (or config name if any), uppercased and underscore-free.
|
143
107
|
|
144
|
-
|
108
|
+
For example, if your module is called "MyCoolGem" then your env var "MYCOOLGEM_PASSWORD" is used as `config.password`.
|
145
109
|
|
110
|
+
Environment variables are type-casted (case-insensitive).
|
146
111
|
Examples:
|
112
|
+
- "True", "T" and "yes" to `true`;
|
113
|
+
- "False", "f" and "no" to `false`;
|
114
|
+
- "nil" and "null" to `nil` (do you really need it?);
|
115
|
+
- "123" to 123 and "3.14" to 3.14.
|
147
116
|
|
148
|
-
|
149
|
-
|
150
|
-
- `"False"`, `"f"` and `"no"` to `false`;
|
151
|
-
|
152
|
-
- `"nil"` and `"null"` to `nil` (do you really need it?);
|
153
|
-
|
154
|
-
- `"123"` to 123 and `"3.14"` to 3.14.
|
155
|
-
|
156
|
-
*Anyway Config* supports nested (_hashed_) env variables. Just separate keys with double-underscore.
|
157
|
-
For example, "MYCOOLGEM_OPTIONS__VERBOSE" is parsed as `config.options.verbose`.
|
117
|
+
*Anyway Config* supports nested (_hashed_) environmental variables. Just separate keys with double-underscore.
|
118
|
+
For example, "MYCOOLGEM_OPTIONS__VERBOSE" is transformed to `config.options.verbose`.
|
158
119
|
|
159
120
|
Array values are also supported:
|
160
121
|
|
@@ -163,16 +124,21 @@ Array values are also supported:
|
|
163
124
|
config.ids #=> [1,2,3]
|
164
125
|
```
|
165
126
|
|
166
|
-
If you want to provide
|
127
|
+
If you want to provide text-like env variable which contain commas then wrap it into quotes:
|
167
128
|
|
168
129
|
```ruby
|
169
130
|
MYCOOLGEM="Nif-Nif, Naf-Naf and Nouf-Nouf"
|
170
131
|
```
|
171
132
|
|
133
|
+
## Using without Rails
|
134
|
+
|
135
|
+
AnywayConfig can be used without Rails too.
|
136
|
+
Environmental variables remain the same. To load config from YAML add special environment variable 'MYGEM_CONF' containing path to config. But you cannot use one file for different environments (unless you do it yourself).
|
137
|
+
|
172
138
|
## Contributing
|
173
139
|
|
174
140
|
1. Fork it
|
175
141
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
176
142
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
177
143
|
4. Push to the branch (`git push origin my-new-feature`)
|
178
|
-
5. Create a new Pull Request
|
144
|
+
5. Create a new Pull Request
|
data/Rakefile
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require "bundler/gem_tasks"
|
4
|
-
require 'rspec/core/rake_task'
|
5
|
-
require "rubocop/rake_task"
|
6
2
|
|
7
|
-
|
3
|
+
require 'rspec/core/rake_task'
|
8
4
|
|
9
5
|
task(:spec).clear
|
10
6
|
desc "Run specs with Rails app"
|
@@ -19,5 +15,5 @@ RSpec::Core::RakeTask.new("spec:norails") do |task|
|
|
19
15
|
task.verbose = false
|
20
16
|
end
|
21
17
|
|
22
|
-
desc "Run the all specs
|
23
|
-
task default: %w
|
18
|
+
desc "Run the all specs"
|
19
|
+
task default: %w(spec:norails spec)
|
data/anyway_config.gemspec
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
2
|
lib = File.expand_path('../lib', __FILE__)
|
5
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
4
|
require 'anyway/version'
|
@@ -8,23 +6,17 @@ require 'anyway/version'
|
|
8
6
|
Gem::Specification.new do |s|
|
9
7
|
s.name = "anyway_config"
|
10
8
|
s.version = Anyway::VERSION
|
11
|
-
s.authors = ["
|
9
|
+
s.authors = ["Vlad Dem"]
|
12
10
|
s.email = ["dementiev.vm@gmail.com"]
|
13
11
|
s.homepage = "http://github.com/palkan/anyway_config"
|
14
|
-
s.summary = "Configuration
|
15
|
-
s.description =
|
16
|
-
|
17
|
-
|
18
|
-
Allows you to easily follow the twevle factor application principles (https://12factor.net/config).
|
19
|
-
}
|
20
|
-
|
21
|
-
s.license = "MIT"
|
12
|
+
s.summary = "Configuration for Ruby plugins and applications"
|
13
|
+
s.description = "Configuration for Ruby plugins and applications"
|
14
|
+
s.license = "MIT"
|
22
15
|
|
23
|
-
s.files = `git ls-files`.split(
|
16
|
+
s.files = `git ls-files`.split($/)
|
24
17
|
s.require_paths = ["lib"]
|
25
18
|
s.required_ruby_version = '>= 2'
|
26
19
|
|
27
20
|
s.add_development_dependency "simplecov", ">= 0.3.8"
|
28
21
|
s.add_development_dependency "rspec", "~> 3.5.0"
|
29
|
-
s.add_development_dependency "rubocop", "~> 0.49"
|
30
22
|
end
|
data/lib/anyway.rb
CHANGED
data/lib/anyway/config.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'anyway/ext/class'
|
4
2
|
require 'anyway/ext/deep_dup'
|
5
3
|
require 'anyway/ext/hash'
|
6
4
|
|
7
|
-
module Anyway
|
5
|
+
module Anyway
|
8
6
|
using Anyway::Ext::Class
|
9
7
|
using Anyway::Ext::DeepDup
|
10
8
|
using Anyway::Ext::Hash
|
@@ -75,16 +73,16 @@ module Anyway # :nodoc:
|
|
75
73
|
end
|
76
74
|
|
77
75
|
def load_from_file(config)
|
78
|
-
config_path = Anyway.env.
|
79
|
-
"./config/#{config_name}.yml"
|
76
|
+
config_path = (Anyway.env.send(config_name) || {}).delete('conf')
|
80
77
|
if config_path && File.file?(config_path)
|
81
|
-
|
78
|
+
require 'yaml'
|
79
|
+
config.deep_merge!(YAML.load_file(config_path) || {})
|
82
80
|
end
|
83
81
|
config
|
84
82
|
end
|
85
83
|
|
86
84
|
def load_from_env(config)
|
87
|
-
config.deep_merge!(Anyway.env.
|
85
|
+
config.deep_merge!(Anyway.env.send(config_name) || {})
|
88
86
|
config
|
89
87
|
end
|
90
88
|
|
@@ -93,14 +91,5 @@ module Anyway # :nodoc:
|
|
93
91
|
def set_value(key, val)
|
94
92
|
send("#{key}=", val) if respond_to?(key)
|
95
93
|
end
|
96
|
-
|
97
|
-
def parse_yml(path)
|
98
|
-
require 'yaml'
|
99
|
-
if defined?(ERB)
|
100
|
-
YAML.safe_load(ERB.new(File.read(path)).result)
|
101
|
-
else
|
102
|
-
YAML.load_file(path)
|
103
|
-
end
|
104
|
-
end
|
105
94
|
end
|
106
95
|
end
|
data/lib/anyway/env.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Anyway
|
4
2
|
# Parses environment variables and provides
|
5
3
|
# method-like access
|
@@ -11,29 +9,34 @@ module Anyway
|
|
11
9
|
|
12
10
|
def initialize
|
13
11
|
@data = {}
|
12
|
+
load
|
13
|
+
end
|
14
|
+
|
15
|
+
def reload
|
16
|
+
clear
|
17
|
+
load
|
18
|
+
self
|
14
19
|
end
|
15
20
|
|
16
21
|
def clear
|
17
22
|
@data.clear
|
23
|
+
self
|
18
24
|
end
|
19
25
|
|
20
|
-
def
|
21
|
-
|
26
|
+
def method_missing(method_name, *args, &_block)
|
27
|
+
method_name = method_name.to_s.gsub(/\_/, '')
|
28
|
+
return @data[method_name] if args.empty? && @data.key?(method_name)
|
22
29
|
end
|
23
30
|
|
24
31
|
private
|
25
32
|
|
26
|
-
def
|
27
|
-
config_env_name = config_name.to_s.delete("_")
|
28
|
-
config_env_name.upcase!
|
29
|
-
data = {}
|
33
|
+
def load
|
30
34
|
ENV.each_pair do |key, val|
|
31
|
-
if
|
32
|
-
|
33
|
-
set_by_path(data, path, serialize_val(val))
|
35
|
+
if config_key?(key)
|
36
|
+
mod, path = extract_module_path(key)
|
37
|
+
set_by_path(get_hash(@data, mod), path, serialize_val(val))
|
34
38
|
end
|
35
39
|
end
|
36
|
-
data
|
37
40
|
end
|
38
41
|
|
39
42
|
def config_key?(key)
|
@@ -58,8 +61,6 @@ module Anyway
|
|
58
61
|
(from[name] ||= {})
|
59
62
|
end
|
60
63
|
|
61
|
-
# rubocop:disable Metrics/MethodLength
|
62
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
63
64
|
def serialize_val(value)
|
64
65
|
case value
|
65
66
|
when ARRAY_RXP
|
@@ -80,7 +81,5 @@ module Anyway
|
|
80
81
|
value
|
81
82
|
end
|
82
83
|
end
|
83
|
-
# rubocop:enable Metrics/MethodLength
|
84
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
85
84
|
end
|
86
85
|
end
|
data/lib/anyway/ext/class.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Anyway
|
4
2
|
module Ext
|
5
3
|
# Extend String through refinements
|
@@ -8,7 +6,7 @@ module Anyway
|
|
8
6
|
def underscore_name
|
9
7
|
return unless name
|
10
8
|
word = name[/^(\w+)/]
|
11
|
-
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,
|
9
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
12
10
|
word.downcase!
|
13
11
|
word
|
14
12
|
end
|
data/lib/anyway/ext/deep_dup.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Anyway
|
4
2
|
module Ext
|
5
3
|
# Extend Object through refinements
|
@@ -8,11 +6,7 @@ module Anyway
|
|
8
6
|
# Based on ActiveSupport http://api.rubyonrails.org/classes/Hash.html#method-i-deep_dup
|
9
7
|
def deep_dup
|
10
8
|
each_with_object(dup) do |(key, value), hash|
|
11
|
-
hash[key] =
|
12
|
-
value.deep_dup
|
13
|
-
else
|
14
|
-
value
|
15
|
-
end
|
9
|
+
hash[key] = value.respond_to?(:deep_dup) ? value.deep_dup : value
|
16
10
|
end
|
17
11
|
end
|
18
12
|
end
|
@@ -20,13 +14,7 @@ module Anyway
|
|
20
14
|
refine ::Array do
|
21
15
|
# From ActiveSupport http://api.rubyonrails.org/classes/Array.html#method-i-deep_dup
|
22
16
|
def deep_dup
|
23
|
-
map
|
24
|
-
if value.is_a?(::Hash) || value.is_a?(::Array)
|
25
|
-
value.deep_dup
|
26
|
-
else
|
27
|
-
value
|
28
|
-
end
|
29
|
-
end
|
17
|
+
map { |el| el.respond_to?(:deep_dup) ? el.deep_dup : el }
|
30
18
|
end
|
31
19
|
end
|
32
20
|
end
|
data/lib/anyway/ext/hash.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Anyway
|
4
2
|
module Ext
|
5
3
|
# Extend Hash through refinements
|
@@ -22,7 +20,7 @@ module Anyway
|
|
22
20
|
end
|
23
21
|
|
24
22
|
def stringify_keys!
|
25
|
-
keys.each do |key|
|
23
|
+
self.keys.each do |key|
|
26
24
|
value = delete(key)
|
27
25
|
value.stringify_keys! if value.is_a?(::Hash)
|
28
26
|
self[key.to_s] = value
|
data/lib/anyway/rails/config.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Anyway
|
4
2
|
class Config # :nodoc:
|
5
3
|
class << self
|
@@ -19,7 +17,8 @@ module Anyway
|
|
19
17
|
def load_from_file(config)
|
20
18
|
config_path = Rails.root.join("config", "#{@config_name}.yml")
|
21
19
|
if File.file? config_path
|
22
|
-
|
20
|
+
require 'yaml'
|
21
|
+
config.deep_merge!(YAML.load_file(config_path)[Rails.env] || {})
|
23
22
|
end
|
24
23
|
config
|
25
24
|
end
|
data/lib/anyway/version.rb
CHANGED
data/spec/config_spec.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'spec_helper'
|
4
2
|
|
5
3
|
describe Anyway::Config do
|
@@ -7,10 +5,6 @@ describe Anyway::Config do
|
|
7
5
|
let(:test_conf) { Anyway::TestConfig.new }
|
8
6
|
|
9
7
|
describe "config with name" do
|
10
|
-
before(:each) do
|
11
|
-
ENV.delete_if { |var| var =~ /^(cool|anyway)_/i }
|
12
|
-
end
|
13
|
-
|
14
8
|
specify { expect(CoolConfig.config_name).to eq "cool" }
|
15
9
|
|
16
10
|
describe "defaults" do
|
@@ -27,20 +21,21 @@ describe Anyway::Config do
|
|
27
21
|
end
|
28
22
|
|
29
23
|
describe "load from files" do
|
30
|
-
it "set
|
24
|
+
it "should set defauls" do
|
31
25
|
expect(conf.port).to eq 8080
|
32
26
|
end
|
33
27
|
|
34
|
-
it "load config from YAML" do
|
28
|
+
it "should load config from YAML" do
|
35
29
|
expect(conf.host).to eq "test.host"
|
36
30
|
end
|
37
31
|
|
38
32
|
if Rails.application.respond_to?(:secrets)
|
39
|
-
it "load config from secrets" do
|
33
|
+
it "should load config from secrets" do
|
40
34
|
expect(conf.user[:name]).to eq "test"
|
35
|
+
expect(conf.user[:password]).to eq "test"
|
41
36
|
end
|
42
37
|
else
|
43
|
-
it "load config from file if no secrets" do
|
38
|
+
it "should load config from file if no secrets" do
|
44
39
|
expect(conf.user[:name]).to eq "root"
|
45
40
|
expect(conf.user[:password]).to eq "root"
|
46
41
|
end
|
@@ -48,18 +43,14 @@ describe Anyway::Config do
|
|
48
43
|
end
|
49
44
|
|
50
45
|
describe "load from env" do
|
51
|
-
|
46
|
+
after(:each) { Anyway.env.clear }
|
47
|
+
it "should work" do
|
52
48
|
ENV['COOL_PORT'] = '80'
|
53
49
|
ENV['COOL_USER__NAME'] = 'john'
|
54
|
-
Anyway.env.
|
50
|
+
Anyway.env.reload
|
55
51
|
expect(conf.port).to eq 80
|
56
52
|
expect(conf.user[:name]).to eq 'john'
|
57
53
|
end
|
58
|
-
|
59
|
-
it "handle ENV in YML thru ERB" do
|
60
|
-
ENV['ANYWAY_SECRET_PASSWORD'] = 'my_pass'
|
61
|
-
expect(conf.user[:password]).to eq 'my_pass'
|
62
|
-
end
|
63
54
|
end
|
64
55
|
|
65
56
|
describe "clear" do
|
@@ -75,11 +66,12 @@ describe Anyway::Config do
|
|
75
66
|
end
|
76
67
|
|
77
68
|
describe "reload" do
|
69
|
+
after(:each) { Anyway.env.clear }
|
78
70
|
it do
|
79
71
|
expect(conf.port).to eq 8080
|
80
72
|
ENV['COOL_PORT'] = '80'
|
81
73
|
ENV['COOL_USER__NAME'] = 'john'
|
82
|
-
Anyway.env.
|
74
|
+
Anyway.env.reload
|
83
75
|
conf.reload
|
84
76
|
expect(conf.port).to eq 80
|
85
77
|
expect(conf.user[:name]).to eq 'john'
|
@@ -88,14 +80,11 @@ describe Anyway::Config do
|
|
88
80
|
end
|
89
81
|
|
90
82
|
describe "config for name" do
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
it "load data by config name", :aggregate_failures do
|
83
|
+
after(:each) { Anyway.env.clear }
|
84
|
+
it "should load data by config name", :aggregate_failures do
|
96
85
|
ENV['MYAPP_TEST'] = '1'
|
97
86
|
ENV['MYAPP_NAME'] = 'my_app'
|
98
|
-
Anyway.env.
|
87
|
+
Anyway.env.reload
|
99
88
|
data = Anyway::Config.for(:my_app)
|
100
89
|
expect(data[:test]).to eq 1
|
101
90
|
expect(data[:name]).to eq 'my_app'
|
data/spec/config_spec_norails.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'spec_norails_helper'
|
4
2
|
|
5
3
|
describe Anyway::Config do
|
6
4
|
let(:conf) { Anyway::TestConfig.new }
|
7
5
|
|
8
6
|
describe "config without Rails" do
|
9
|
-
|
7
|
+
after(:each) do
|
8
|
+
Anyway.env.clear
|
10
9
|
ENV.delete_if { |var| var =~ /^anyway_/i }
|
11
10
|
end
|
12
11
|
|
@@ -26,27 +25,26 @@ describe Anyway::Config do
|
|
26
25
|
ENV['ANYWAY_LOG__FORMAT__COLOR'] = 't'
|
27
26
|
ENV['ANYWAY_LOG_LEVELS'] = 'debug,warning,info'
|
28
27
|
|
29
|
-
Anyway.env.
|
28
|
+
Anyway.env.reload
|
30
29
|
expect(conf.api['key']).to eq "test1"
|
31
30
|
expect(conf.api['endpoint']).to eq "localhost"
|
32
31
|
expect(conf.test).to eq "test"
|
33
32
|
expect(conf.log['format']['color']).to eq true
|
34
|
-
expect(conf.log_levels).to eq(%w
|
33
|
+
expect(conf.log_levels).to eq(%w(debug warning info))
|
35
34
|
end
|
36
35
|
|
37
36
|
it "reloads config", :aggregate_failures do
|
38
|
-
ENV['ANYWAY_CONF'] = File.join(File.dirname(__FILE__), "anyway.yml")
|
39
|
-
|
40
37
|
expect(conf.api['key']).to eq ""
|
41
|
-
expect(conf.api['endpoint']).to
|
38
|
+
expect(conf.api['endpoint']).to be_nil
|
42
39
|
expect(conf.test).to be_nil
|
43
40
|
expect(conf.log['format']['color']).to eq false
|
44
41
|
|
42
|
+
ENV['ANYWAY_CONF'] = File.join(File.dirname(__FILE__), "anyway.yml")
|
45
43
|
ENV['ANYWAY_API__KEY'] = 'test1'
|
46
44
|
ENV['ANYWAY_API__SSL'] = 'yes'
|
47
45
|
ENV['ANYWAY_TEST'] = 'test'
|
48
46
|
ENV['ANYWAY_LOG__FORMAT__COLOR'] = 't'
|
49
|
-
Anyway.env.
|
47
|
+
Anyway.env.reload
|
50
48
|
|
51
49
|
conf.reload
|
52
50
|
expect(conf.api['key']).to eq "test1"
|
@@ -63,24 +61,5 @@ describe Anyway::Config do
|
|
63
61
|
|
64
62
|
specify { expect(conf.config_name).to be_nil }
|
65
63
|
end
|
66
|
-
|
67
|
-
context "loading from default path" do
|
68
|
-
let(:conf) { CoolConfig.new }
|
69
|
-
|
70
|
-
before(:each) do
|
71
|
-
ENV.delete_if { |var| var =~ /^cool_/i }
|
72
|
-
end
|
73
|
-
|
74
|
-
it "loads from ./config", :aggregate_failures do
|
75
|
-
expect(conf.user).to eq("name" => "root", "password" => "root")
|
76
|
-
expect(conf.host).to eq "test.host"
|
77
|
-
expect(conf.port).to eq 9292
|
78
|
-
end
|
79
|
-
|
80
|
-
it "handle ENV in YML thru ERB" do
|
81
|
-
ENV["ANYWAY_COOL_PORT"] = "1957"
|
82
|
-
expect(conf.port).to eq 1957
|
83
|
-
end
|
84
|
-
end
|
85
64
|
end
|
86
65
|
end
|
data/spec/dummy.yml
ADDED
File without changes
|
data/spec/dummy/config/cool.yml
CHANGED
data/spec/env_spec.rb
CHANGED
@@ -1,36 +1,32 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'spec_helper'
|
4
2
|
|
5
3
|
describe Anyway::Env do
|
6
|
-
let(:env) { Anyway.env }
|
4
|
+
let(:env) { Anyway.env.reload }
|
7
5
|
|
8
|
-
it "
|
6
|
+
it "should load simple key/values by module", :aggregate_failures do
|
9
7
|
ENV['TESTO_KEY'] = 'a'
|
10
8
|
ENV['MYTEST_KEY'] = 'b'
|
11
|
-
expect(env.
|
12
|
-
expect(env.
|
9
|
+
expect(env.testo['key']).to eq 'a'
|
10
|
+
expect(env.my_test['key']).to eq 'b'
|
13
11
|
end
|
14
12
|
|
15
|
-
it "
|
13
|
+
it "should load hash values", :aggregate_failures do
|
16
14
|
ENV['TESTO_DATA__ID'] = '1'
|
17
15
|
ENV['TESTO_DATA__META__NAME'] = 'meta'
|
18
16
|
ENV['TESTO_DATA__META__VAL'] = 'true'
|
19
|
-
|
20
|
-
expect(
|
21
|
-
expect(
|
22
|
-
expect(testo_config['data']['meta']['val']).to be_truthy
|
17
|
+
expect(env.testo['data']['id']).to eq 1
|
18
|
+
expect(env.testo['data']['meta']['name']).to eq 'meta'
|
19
|
+
expect(env.testo['data']['meta']['val']).to be_truthy
|
23
20
|
end
|
24
21
|
|
25
|
-
it "
|
22
|
+
it "should load array values", :aggregate_failures do
|
26
23
|
ENV['TESTO_DATA__IDS'] = '1,2, 3'
|
27
24
|
ENV['TESTO_DATA__META__NAMES'] = 'meta, kotleta'
|
28
25
|
ENV['TESTO_DATA__META__SIZE'] = '2'
|
29
26
|
ENV['TESTO_DATA__TEXT'] = '"C\'mon, everybody"'
|
30
|
-
|
31
|
-
expect(
|
32
|
-
expect(
|
33
|
-
expect(
|
34
|
-
expect(testo_config['data']['text']).to eq "C'mon, everybody"
|
27
|
+
expect(env.testo['data']['ids']).to include(1, 2, 3)
|
28
|
+
expect(env.testo['data']['meta']['names']).to include('meta', 'kotleta')
|
29
|
+
expect(env.testo['data']['meta']['size']).to eq 2
|
30
|
+
expect(env.testo['data']['text']).to eq "C'mon, everybody"
|
35
31
|
end
|
36
32
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
2
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
3
|
|
6
4
|
begin
|
7
5
|
require "pry-byebug"
|
8
|
-
rescue LoadError
|
6
|
+
rescue LoadError
|
9
7
|
end
|
10
8
|
|
11
9
|
ENV["RAILS_ENV"] = 'test'
|
12
10
|
|
13
11
|
require File.expand_path("../dummy/config/environment", __FILE__)
|
14
|
-
require '
|
12
|
+
require 'anyway'
|
15
13
|
|
16
14
|
Rails.application.eager_load!
|
17
15
|
|
@@ -19,13 +17,4 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
|
19
17
|
|
20
18
|
RSpec.configure do |config|
|
21
19
|
config.mock_with :rspec
|
22
|
-
|
23
|
-
config.example_status_persistence_file_path = "tmp/rspec_examples.txt"
|
24
|
-
config.filter_run :focus
|
25
|
-
config.run_all_when_everything_filtered = true
|
26
|
-
|
27
|
-
config.order = :random
|
28
|
-
Kernel.srand config.seed
|
29
|
-
|
30
|
-
config.before(:each) { Anyway.env.clear }
|
31
20
|
end
|
data/spec/spec_norails_helper.rb
CHANGED
@@ -1,26 +1,15 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
2
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
3
|
|
6
4
|
begin
|
7
5
|
require "pry-byebug"
|
8
|
-
rescue LoadError
|
6
|
+
rescue LoadError
|
9
7
|
end
|
10
8
|
|
11
|
-
require '
|
12
|
-
require 'erb'
|
9
|
+
require 'anyway'
|
13
10
|
|
14
11
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
15
12
|
|
16
13
|
RSpec.configure do |config|
|
17
14
|
config.mock_with :rspec
|
18
|
-
|
19
|
-
config.filter_run :focus
|
20
|
-
config.run_all_when_everything_filtered = true
|
21
|
-
|
22
|
-
config.order = :random
|
23
|
-
Kernel.srand config.seed
|
24
|
-
|
25
|
-
config.before(:each) { Anyway.env.clear }
|
26
15
|
end
|
data/spec/support/cool_config.rb
CHANGED
data/spec/support/test_config.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Anyway
|
4
2
|
class TestConfig < Anyway::Config # :nodoc:
|
5
|
-
attr_config :test,
|
3
|
+
attr_config :test,
|
6
4
|
api: { key: '' },
|
7
5
|
log: {
|
8
6
|
format: {
|
@@ -11,6 +9,6 @@ module Anyway
|
|
11
9
|
},
|
12
10
|
level: :info
|
13
11
|
},
|
14
|
-
log_levels:
|
12
|
+
log_levels: [:info, :fatal]
|
15
13
|
end
|
16
14
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: anyway_config
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.1
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Vlad Dem
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-01-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: simplecov
|
@@ -38,23 +38,7 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 3.5.0
|
41
|
-
|
42
|
-
name: rubocop
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.49'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0.49'
|
55
|
-
description: "\n Configuration DSL for Ruby libraries and applications.\n\n Allows
|
56
|
-
you to easily follow the twevle factor application principles (https://12factor.net/config).\n
|
57
|
-
\ "
|
41
|
+
description: Configuration for Ruby plugins and applications
|
58
42
|
email:
|
59
43
|
- dementiev.vm@gmail.com
|
60
44
|
executables: []
|
@@ -62,18 +46,16 @@ extensions: []
|
|
62
46
|
extra_rdoc_files: []
|
63
47
|
files:
|
64
48
|
- ".gitignore"
|
49
|
+
- ".hound.yml"
|
65
50
|
- ".rubocop.yml"
|
66
51
|
- ".travis.yml"
|
67
|
-
- CHANGELOG.md
|
68
52
|
- Gemfile
|
69
53
|
- LICENSE.txt
|
70
54
|
- README.md
|
71
55
|
- Rakefile
|
72
56
|
- anyway_config.gemspec
|
73
|
-
- config/cool.yml
|
74
57
|
- gemfiles/rails42.gemfile
|
75
58
|
- gemfiles/rails5.gemfile
|
76
|
-
- gemfiles/railsmaster.gemfile
|
77
59
|
- lib/anyway.rb
|
78
60
|
- lib/anyway/config.rb
|
79
61
|
- lib/anyway/env.rb
|
@@ -82,10 +64,10 @@ files:
|
|
82
64
|
- lib/anyway/ext/hash.rb
|
83
65
|
- lib/anyway/rails/config.rb
|
84
66
|
- lib/anyway/version.rb
|
85
|
-
- lib/anyway_config.rb
|
86
67
|
- spec/anyway.yml
|
87
68
|
- spec/config_spec.rb
|
88
69
|
- spec/config_spec_norails.rb
|
70
|
+
- spec/dummy.yml
|
89
71
|
- spec/dummy/config.ru
|
90
72
|
- spec/dummy/config/application.rb
|
91
73
|
- spec/dummy/config/boot.rb
|
@@ -96,7 +78,6 @@ files:
|
|
96
78
|
- spec/dummy/config/routes.rb
|
97
79
|
- spec/dummy/config/secrets.yml
|
98
80
|
- spec/env_spec.rb
|
99
|
-
- spec/ext/deep_dup_spec.rb
|
100
81
|
- spec/spec_helper.rb
|
101
82
|
- spec/spec_norails_helper.rb
|
102
83
|
- spec/support/cool_config.rb
|
@@ -116,13 +97,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
116
97
|
version: '2'
|
117
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
99
|
requirements:
|
119
|
-
- - "
|
100
|
+
- - ">="
|
120
101
|
- !ruby/object:Gem::Version
|
121
|
-
version:
|
102
|
+
version: '0'
|
122
103
|
requirements: []
|
123
104
|
rubyforge_project:
|
124
105
|
rubygems_version: 2.6.4
|
125
106
|
signing_key:
|
126
107
|
specification_version: 4
|
127
|
-
summary: Configuration
|
108
|
+
summary: Configuration for Ruby plugins and applications
|
128
109
|
test_files: []
|
data/CHANGELOG.md
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
# Change log
|
2
|
-
|
3
|
-
## 1.0.0 (2017-06-20)
|
4
|
-
|
5
|
-
- Lazy load and parse ENV configurtaion (https://github.com/palkan/anyway_config/commit/5fe407c75fefec8994ca201ea7b4691b5ddd96e5). ([@palkan][])
|
6
|
-
|
7
|
-
- Add support for ERB in YML configuration (https://github.com/palkan/anyway_config/commit/8d8a47dbda6858a43ff509aaa4cddf4f938dd660). ([@palkan][])
|
8
|
-
|
9
|
-
## 0.5.0 (2017-01-20)
|
10
|
-
|
11
|
-
- Drop `active_support` dependency. ([@palkan][])
|
12
|
-
|
13
|
-
Use custom refinements instead of requiring `active_support`.
|
14
|
-
|
15
|
-
No we're dependency-free!
|
16
|
-
|
17
|
-
## 0.1.0 (2015-01-20)
|
18
|
-
|
19
|
-
Initial version.
|
20
|
-
|
21
|
-
[@palkan]: https://github.com/palkan
|
data/config/cool.yml
DELETED
data/lib/anyway_config.rb
DELETED
data/spec/ext/deep_dup_spec.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Anyway::Ext::DeepDup do
|
6
|
-
using Anyway::Ext::DeepDup
|
7
|
-
|
8
|
-
it "duplicates nested arrays and hashes", :aggregate_failures do
|
9
|
-
source = {
|
10
|
-
a: 1,
|
11
|
-
b: 'hello',
|
12
|
-
c: {
|
13
|
-
id: 1,
|
14
|
-
list: [1, 2, { name: 'John' }]
|
15
|
-
},
|
16
|
-
d: [{ id: 1 }, { id: 2 }]
|
17
|
-
}
|
18
|
-
|
19
|
-
dup = source.deep_dup
|
20
|
-
|
21
|
-
expect(dup[:a]).to eq 1
|
22
|
-
expect(dup[:b]).to eq 'hello'
|
23
|
-
expect(dup[:c]).to eq(
|
24
|
-
id: 1,
|
25
|
-
list: [1, 2, { name: 'John' }]
|
26
|
-
)
|
27
|
-
expect(dup[:d]).to eq(
|
28
|
-
[{ id: 1 }, { id: 2 }]
|
29
|
-
)
|
30
|
-
|
31
|
-
expect(dup[:c]).not_to be_equal(source[:c])
|
32
|
-
expect(dup[:c][:list]).not_to be_equal(source[:c][:list])
|
33
|
-
expect(dup[:c][:list].last).not_to be_equal(source[:c][:list].last)
|
34
|
-
|
35
|
-
expect(dup[:d].first).not_to be_equal(source[:d].first)
|
36
|
-
expect(dup[:d].last).not_to be_equal(source[:d].last)
|
37
|
-
end
|
38
|
-
end
|