inquisitive 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2c18376379e842ce96e09a55327f32e626a25c04
4
+ data.tar.gz: 745ebacc0f952e67809dfc5a0fc9c2aa0a6b0ac3
5
+ SHA512:
6
+ metadata.gz: 40d8af8890c71242210733158d282e228a855d75882861a7a6d35ff2ca048abc267f47459613be4343c74833be9d16d4f888c83e5b8cff541b667d00e73e986c
7
+ data.tar.gz: 1c0f9745d5dbe15d2f9a98caea7d487db33e5276d32725819c14cadc7f2114adbb1d7de353af49e71df6ba1704103067492d8453e9b3cb224128fa959f4f672f
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in inquisitive.gemspec
4
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2013 Chris Keele
2
+ ------------------------------
3
+
4
+ MIT License
5
+ ===========
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,261 @@
1
+ Inquisitive
2
+ ===========
3
+
4
+ Predicate methods for those curious about their datastructures.
5
+
6
+ Inquisitive allows you to interrogate objects about their contents with friendly, readable method chains. It's the logical conclusion of ActiveSupport's `StringInquirer`.
7
+
8
+ This library is extracted from several projects where I found myself building on the `Rails.env.production?` pattern: wrapping information from the `ENV` variable into more descriptive and flexible methods accessible from my main namespace. `Inquisitive::Environment` contains helper methods to further this end.
9
+
10
+ For all intents and purposes Inquisitive has no dependencies, doesn't pollute the global constant namespace with anything but `Inquisitive`, and doesn't touch any core classes. It uses ActiveSupport's `HashWithIndifferentAccess`, but will bootstrap itself with a minimal version extracted from ActiveSupport 4.0 if it cannot be found.
11
+
12
+ It also leans on `method_missing`, `dup`, and wrapper objects, so if your application is too inquisitive you might find it grinding to a halt. It's recommended to only use it to switch on a few core runtime environment variables. Don't serialize ActiveRecord attributes into it, is what I'm saying here.
13
+
14
+ Usage
15
+ -----
16
+
17
+ ### String
18
+
19
+ `Inquisitive::String` tests equality:
20
+
21
+ ```ruby
22
+ environment = Inquisitive::String.new 'development'
23
+ #=> "development"
24
+ environment.development?
25
+ #=> true
26
+ environment.not.development?
27
+ #=> false
28
+ ```
29
+
30
+ ### Array
31
+
32
+ `Inquisitive::Array` tests inclusion:
33
+
34
+ ```ruby
35
+ supported_databases = Inquisitive::Array.new %w[mysql postgres sqlite]
36
+ #=> ["mysql", "postgres", "sqlite"]
37
+ supported_databases.postgres?
38
+ #=> true
39
+ supported_databases.sql_server?
40
+ #=> false
41
+ supported_databases.exclude.sql_server?
42
+ #=> true
43
+ ```
44
+
45
+ ### Hash
46
+
47
+ `Inquisitive::Hash` provides struct-like access to its values, wrapped in other inquisitive objects:
48
+
49
+ ```ruby
50
+ stubbed = Inquisitive::Hash.new(
51
+ authentication: true,
52
+ in: 'development',
53
+ services: %w[database api],
54
+ ignorable: { junk: [ "" ] }
55
+ )
56
+ #=> {"authentication"=>true,
57
+ #=> "in"=>"development",
58
+ #=> "services"=>["database", "api"],
59
+ #=> "ignorable"=>{"junk"=>[""]}}
60
+
61
+ stubbed.authentication?
62
+ #=> true
63
+ stubbed.registration?
64
+ #=> false
65
+ stubbed.services?
66
+ #=> true
67
+ stubbed.ignorable?
68
+ #=> false
69
+
70
+ stubbed.in.development?
71
+ #=> true
72
+ stubbed.in.production?
73
+ #=> false
74
+ stubbed.services.database?
75
+ #=> true
76
+ stubbed.services.sidekiq?
77
+ #=> false
78
+ ```
79
+
80
+ `Inquisitive::Hash` also allows negation with the `no` method:
81
+
82
+ ```ruby
83
+ config = Inquisitive::Hash.new(database: 'postgres')
84
+ #=> {"database"=>"postgres"}
85
+
86
+ config.database?
87
+ #=> true
88
+ config.no.database?
89
+ #=> false
90
+ config.api?
91
+ #=> false
92
+ config.no.api?
93
+ #=> true
94
+ ```
95
+
96
+ ### Inquisitive Environment
97
+
98
+ `Inquisitive::Environment` can be used in your modules and classes to more easily interrogate the `ENV` variable:
99
+
100
+ #### Strings
101
+
102
+ ```ruby
103
+ ENV['ENVIRONMENT'] = "development"
104
+ class MyGame
105
+ extend Inquisitive::Environment
106
+ inquires_about 'ENVIRONMENT'
107
+ end
108
+
109
+ MyGame.environment
110
+ #=> "development"
111
+ MyGame.environment.development?
112
+ #=> true
113
+ MyGame.environment.production?
114
+ #=> false
115
+ ```
116
+
117
+ #### Arrays
118
+
119
+ ```ruby
120
+ ENV['SUPPORTED_DATABASES'] = "mysql,postgres,sqlite"
121
+ class MyGame
122
+ extend Inquisitive::Environment
123
+ inquires_about 'SUPPORTED_DATABASES'
124
+ end
125
+
126
+ MyGame.supported_databases
127
+ #=> ["mysql", "postgres", "sqlite"]
128
+ MyGame.supported_databases.sqlite?
129
+ #=> true
130
+ MyGame.supported_databases.sql_server?
131
+ #=> false
132
+ ```
133
+
134
+ #### Hashes
135
+
136
+ ```ruby
137
+ ENV['STUB_AUTHENTICATION'] = 'true'
138
+ ENV['STUB_IN'] = "development"
139
+ ENV['STUB_SERVICES'] = "database,api"
140
+ class MyGame
141
+ extend Inquisitive::Environment
142
+ inquires_about 'STUB'
143
+ end
144
+
145
+ MyGame.stub.authentication?
146
+ #=> true
147
+ MyGame.stub.registration?
148
+ #=> false
149
+ MyGame.stub.in.development?
150
+ #=> true
151
+ MyGame.stub.in.production?
152
+ #=> false
153
+ MyGame.stub.services.exclude.sidekiq?
154
+ #=> true
155
+ MyGame.stub.services.sidekiq?
156
+ #=> false
157
+ ```
158
+
159
+ #### Naming
160
+
161
+ You can name your environment inquirers with `:with`:
162
+
163
+ ```ruby
164
+ ENV['ENVIRONMENT'] = "development"
165
+ class MyGame
166
+ extend Inquisitive::Environment
167
+ inquires_about 'ENVIRONMENT', with: :env
168
+ end
169
+
170
+ MyGame.env
171
+ #=> "development"
172
+ MyGame.env.development?
173
+ #=> true
174
+ MyGame.env.production?
175
+ #=> false
176
+ ```
177
+
178
+ #### Presence
179
+
180
+ Environment inquirers can have explicit presence checks, circumventing a common pitfall when reasoning about environment variables. Borrowing from the example above:
181
+
182
+ ```ruby
183
+ ENV['STUB_AUTHENTICATION'] = 'false'
184
+ class MyGame
185
+ extend Inquisitive::Environment
186
+ inquires_about 'STUB'
187
+ end
188
+
189
+ MyGame.stub.authentication
190
+ #=> "false"
191
+ MyGame.stub.authentication?
192
+ #=> true
193
+ MyGame.stub.authentication.true?
194
+ #=> false
195
+ ```
196
+
197
+ It's common to use the presence of environment variables as runtime booleans. This is frequently done by setting the environment variable to the string `"true"` when you want it to be true, and not at all otherwise. As demonstrated, this pattern can lead to ambiguity when the string is other values.
198
+
199
+ By default such variables will be parsed as an `Inquisitive::String`, so predicate methods will return true whatever their contents, as long as they exist. You can bind the predicate method tighter to an explicit value if you prefer:
200
+
201
+ ```ruby
202
+ ENV['STUB_AUTHENTICATION'] = 'false'
203
+ ENV['STUB_REGISTRATION'] = 'true'
204
+ class MyGame
205
+ extend Inquisitive::Environment
206
+ inquires_about 'STUB_AUTHENTICATION', present_if: 'true'
207
+ inquires_about 'STUB_REGISTRATION', present_if: 'true'
208
+ end
209
+
210
+ MyGame.stub_authentication
211
+ #=> "false"
212
+ MyGame.stub_authentication?
213
+ #=> false
214
+
215
+ MyGame.stub_registration
216
+ #=> "true"
217
+ MyGame.stub_registration?
218
+ #=> true
219
+ ```
220
+
221
+ This only works on top-level inquirers, so there's no way to get our nested `MyGame.stubbed.authentication?` to behave as expected (currently).
222
+
223
+ The `present_if` check uses `===` under the covers for maximum expressiveness, so you can also use it to match against regexs and other constructs.
224
+
225
+ #### Inquiry mode
226
+
227
+ Environment inquirers have three configurable modes, defaulting to `:dynamic`:
228
+
229
+ ```ruby
230
+ class MyGame
231
+ extend Inquisitive::Environment
232
+ inquires_about 'STUB', mode: %i[dynamic cached static].sample
233
+ end
234
+ ```
235
+
236
+ - **Dynamic**
237
+
238
+ Environment inquiries parse `ENV` on every invocation.
239
+
240
+ Use if you're manipulating the environment in between invocations, so `Inquisitive` can pick up on new values, detect changes between string or array notation, and discover new keys for hash notation.
241
+
242
+ - **Cached**
243
+
244
+ Environment inquiries check `ENV` on their first invocation, and re-use the response in future invocations.
245
+
246
+ Use if you're loading the module with environment inquiry methods before you've finished preparing your environment.
247
+
248
+ - **Static**
249
+
250
+ Environment inquiries use the contents of `ENV` at the moment `inquires_about` was invoked.
251
+
252
+ Use if your application is well-behaved and doesn't go mucking around with the environment at runtime.
253
+
254
+ Contributing
255
+ ------------
256
+
257
+ 1. Fork it
258
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
259
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
260
+ 4. Push to the branch (`git push origin my-new-feature`)
261
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.test_files = Dir['test/**/*_test.rb']
7
+ end
8
+ task default: :test
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "inquisitive"
7
+ spec.version = "1.0.0"
8
+ spec.authors = ["Chris Keele"]
9
+ spec.email = ["dev@chriskeele.com"]
10
+ spec.description = "Predicate methods for those curious about their datastructures."
11
+ spec.summary = "Predicate methods for those curious about their datastructures."
12
+ spec.homepage = "https://github.com/rawsugar/inquisitive"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.test_files = spec.files.grep(%r{^(test)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_development_dependency "bundler", ">= 1.3"
20
+ spec.add_development_dependency "rake", ">= 10.0"
21
+ spec.add_development_dependency "minitest", ">= 5.0"
22
+ spec.add_development_dependency "simplecov", ">= 0.7"
23
+ end
@@ -0,0 +1,48 @@
1
+ module Inquisitive
2
+
3
+ class << self
4
+
5
+ def [](object)
6
+ Inquisitive.const_get(:"#{object.class}", false).new object
7
+ rescue NameError
8
+ object
9
+ end
10
+
11
+ def present?(object)
12
+ case object
13
+ when ::String
14
+ not object.empty?
15
+ when ::Array
16
+ object.any? do |value|
17
+ Inquisitive.present? value
18
+ end
19
+ when ::Hash
20
+ object.values.any? do |value|
21
+ Inquisitive.present? value
22
+ end
23
+ else
24
+ !!object
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ private
31
+
32
+ def predicate_method?(string)
33
+ string[-1] == '?'
34
+ end
35
+
36
+ def predication(string)
37
+ string[0..-2]
38
+ end
39
+
40
+ end
41
+
42
+ require "inquisitive/string"
43
+ require "inquisitive/array"
44
+ unless Object.const_defined? :HashWithIndifferentAccess
45
+ require "inquisitive/hash_with_indifferent_access"
46
+ end
47
+ require "inquisitive/hash"
48
+ require "inquisitive/environment"
@@ -0,0 +1,24 @@
1
+ module Inquisitive
2
+ class Array < ::Array
3
+ include Inquisitive
4
+ attr_accessor :negated, :array
5
+
6
+ def exclude
7
+ self.dup.tap{ |a| a.negated = !a.negated }
8
+ end
9
+
10
+ private
11
+
12
+ def respond_to_missing?(method_name, include_private = false)
13
+ predicate_method?(method_name)
14
+ end
15
+ def method_missing(method_name, *arguments)
16
+ if predicate_method? method_name
17
+ (include? predication(method_name)) ^ negated
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,89 @@
1
+ module Inquisitive
2
+ module Environment
3
+ include Inquisitive
4
+
5
+ def inquires_about(env_var, opts={})
6
+
7
+ env_accessor = opts.fetch(:with, env_var.downcase)
8
+ @__env_accessors__ ||= HashWithIndifferentAccess.new
9
+ @__env_accessors__[env_accessor] = env_var
10
+
11
+ mode = Inquisitive[ opts.fetch(:mode, :dynamic).to_s ]
12
+
13
+ if mode.dynamic?
14
+
15
+ define_singleton_method :"#{env_accessor}" do
16
+ Inquisitive[Parser[@__env_accessors__[__method__]]]
17
+ end
18
+
19
+ else
20
+
21
+ @__cached_env__ ||= HashWithIndifferentAccess.new
22
+ @__cached_env__[env_accessor] = Inquisitive[Parser[env_var]] if mode.static?
23
+
24
+ define_singleton_method :"#{env_accessor}" do
25
+ if @__cached_env__.has_key? __method__
26
+ @__cached_env__[__method__]
27
+ else
28
+ @__cached_env__[__method__] = Inquisitive[Parser[@__env_accessors__[__method__]]]
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ present_if = opts.fetch(:present_if, nil)
35
+
36
+ @__env_presence__ ||= HashWithIndifferentAccess.new
37
+ @__env_presence__["#{env_accessor}?"] = present_if if present_if
38
+
39
+ define_singleton_method :"#{env_accessor}?" do
40
+ if @__env_presence__.has_key? __method__
41
+ @__env_presence__[__method__] === send(predication(__method__))
42
+ else
43
+ Inquisitive.present? send(predication(__method__))
44
+ end
45
+ end
46
+ end
47
+
48
+ module Parser
49
+ class << self
50
+
51
+ def [](var_name)
52
+ if ENV.has_key? var_name
53
+ env_var = ENV[var_name]
54
+
55
+ if env_var.include? ','
56
+ env_var.split(',').map(&:strip)
57
+ else
58
+ env_var
59
+ end
60
+
61
+ elsif Parser.env_key? var_name
62
+
63
+ Parser.env_keys_from(var_name).reduce({}) do |hash, key|
64
+ hash[Parser.key_for(key, var_name)] = Inquisitive[Parser[key]]
65
+ hash
66
+ end
67
+
68
+ end
69
+ end
70
+
71
+ def env_keys_from(var_name)
72
+ ENV.keys.select do |key|
73
+ key =~ /^#{var_name}_/
74
+ end
75
+ end
76
+
77
+ def env_key?(var_name)
78
+ !env_keys_from(var_name).empty?
79
+ end
80
+
81
+ def key_for(env_key, var_name)
82
+ env_key.gsub("#{var_name}_", '').downcase
83
+ end
84
+
85
+ end
86
+ end
87
+
88
+ end
89
+ end