footing 0.2.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +58 -15
- data/README.md +23 -59
- data/Rakefile +1 -1
- data/footing.gemspec +20 -0
- data/lib/footing.rb +5 -54
- data/lib/footing/concerns/hash_methods.rb +39 -0
- data/lib/footing/hash.rb +7 -0
- data/lib/footing/object.rb +55 -0
- data/lib/footing/version.rb +1 -1
- metadata +13 -51
- data/lib/footing/extensions/array.rb +0 -18
- data/lib/footing/extensions/hash.rb +0 -123
- data/lib/footing/extensions/kernel.rb +0 -18
- data/lib/footing/extensions/nil_class.rb +0 -17
- data/lib/footing/extensions/numeric.rb +0 -37
- data/lib/footing/extensions/object.rb +0 -28
- data/lib/footing/extensions/postgresql_adapter.rb +0 -20
- data/lib/footing/extensions/schema_statements.rb +0 -75
- data/lib/footing/extensions/string.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62331199211ec8658359707f77253412878bf094
|
4
|
+
data.tar.gz: 5f342a27486320579d36a1dab9b851bdaee79de7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c22c8d9868db21ab5528d5a8855b63775243121feb1430b0604c5dd1c73a0115973555d12908a0a96700196682c33f40222ac499b29c84a79530ba7da4fffd77
|
7
|
+
data.tar.gz: 09c724182d4234decdca085191b171043059759e45bada2d6b82e56137f01c4e21b2944e558c24550264106e3f72c58d6abfb1e4acf5328a0dc9d16df60a9771
|
data/Gemfile.lock
CHANGED
@@ -1,37 +1,80 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
footing (0.
|
4
|
+
footing (1.0.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
binding_of_caller (0.7.2)
|
10
10
|
debug_inspector (>= 0.0.1)
|
11
|
-
|
11
|
+
byebug (8.2.1)
|
12
|
+
coderay (1.1.0)
|
13
|
+
coveralls (0.8.10)
|
14
|
+
json (~> 1.8)
|
15
|
+
rest-client (>= 1.6.8, < 2)
|
16
|
+
simplecov (~> 0.11.0)
|
17
|
+
term-ansicolor (~> 1.3)
|
18
|
+
thor (~> 0.19.1)
|
19
|
+
tins (~> 1.6.0)
|
12
20
|
debug_inspector (0.0.2)
|
21
|
+
docile (1.1.5)
|
22
|
+
domain_name (0.5.25)
|
23
|
+
unf (>= 0.0.5, < 1.0.0)
|
24
|
+
http-cookie (1.0.2)
|
25
|
+
domain_name (~> 0.5)
|
26
|
+
interception (0.5)
|
27
|
+
json (1.8.3)
|
13
28
|
method_source (0.8.2)
|
14
|
-
|
15
|
-
|
16
|
-
os
|
29
|
+
mime-types (2.99)
|
30
|
+
netrc (0.11.0)
|
17
31
|
os (0.9.6)
|
18
|
-
pry (0.
|
19
|
-
coderay (~> 1.0)
|
20
|
-
method_source (~> 0.8)
|
32
|
+
pry (0.10.3)
|
33
|
+
coderay (~> 1.1.0)
|
34
|
+
method_source (~> 0.8.1)
|
21
35
|
slop (~> 3.4)
|
22
|
-
pry-
|
36
|
+
pry-byebug (3.3.0)
|
37
|
+
byebug (~> 8.0)
|
38
|
+
pry (~> 0.10)
|
39
|
+
pry-rescue (1.4.2)
|
40
|
+
interception (>= 0.5)
|
41
|
+
pry
|
42
|
+
pry-stack_explorer (0.4.9.2)
|
23
43
|
binding_of_caller (>= 0.7)
|
24
44
|
pry (>= 0.9.11)
|
25
|
-
|
26
|
-
|
45
|
+
pry-test (0.6.4)
|
46
|
+
os
|
47
|
+
pry
|
48
|
+
pry-byebug
|
49
|
+
pry-rescue
|
50
|
+
pry-stack_explorer
|
51
|
+
rake (10.4.2)
|
52
|
+
rest-client (1.8.0)
|
53
|
+
http-cookie (>= 1.0.2, < 2.0)
|
54
|
+
mime-types (>= 1.16, < 3.0)
|
55
|
+
netrc (~> 0.7)
|
56
|
+
simplecov (0.11.1)
|
57
|
+
docile (~> 1.1.0)
|
58
|
+
json (~> 1.8)
|
59
|
+
simplecov-html (~> 0.10.0)
|
60
|
+
simplecov-html (0.10.0)
|
61
|
+
slop (3.6.0)
|
62
|
+
term-ansicolor (1.3.2)
|
63
|
+
tins (~> 1.0)
|
64
|
+
thor (0.19.1)
|
65
|
+
tins (1.6.0)
|
66
|
+
unf (0.1.4)
|
67
|
+
unf_ext
|
68
|
+
unf_ext (0.0.7.1)
|
27
69
|
|
28
70
|
PLATFORMS
|
29
71
|
ruby
|
30
72
|
|
31
73
|
DEPENDENCIES
|
74
|
+
coveralls
|
32
75
|
footing!
|
33
|
-
|
34
|
-
micro_test
|
35
|
-
pry
|
36
|
-
pry-stack_explorer
|
76
|
+
pry-test
|
37
77
|
rake
|
78
|
+
|
79
|
+
BUNDLED WITH
|
80
|
+
1.10.6
|
data/README.md
CHANGED
@@ -1,71 +1,35 @@
|
|
1
|
+
[![Lines of Code](http://img.shields.io/badge/lines_of_code-87-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
|
2
|
+
[![Code Status](http://img.shields.io/codeclimate/github/hopsoft/footing.svg?style=flat)](https://codeclimate.com/github/hopsoft/footing)
|
3
|
+
[![Dependency Status](http://img.shields.io/gemnasium/hopsoft/footing.svg?style=flat)](https://gemnasium.com/hopsoft/footing)
|
4
|
+
[![Build Status](http://img.shields.io/travis/hopsoft/footing.svg?style=flat)](https://travis-ci.org/hopsoft/footing)
|
5
|
+
[![Coverage Status](https://img.shields.io/coveralls/hopsoft/footing.svg?style=flat)](https://coveralls.io/r/hopsoft/footing?branch=master)
|
6
|
+
[![Downloads](http://img.shields.io/gem/dt/footing.svg?style=flat)](http://rubygems.org/gems/footing)
|
7
|
+
|
1
8
|
# Footing
|
2
9
|
|
3
|
-
[
|
4
|
-
[
|
5
|
-
[
|
10
|
+
An [ActiveSupport](https://github.com/rails/rails/tree/master/activesupport)
|
11
|
+
style utility library that employs [delegation](https://en.wikipedia.org/wiki/Delegation_(programming))
|
12
|
+
instead of [monkey patching](https://en.wikipedia.org/wiki/Monkey_patch).
|
6
13
|
|
7
|
-
|
14
|
+
__NOTE:__ _The project is structured so that it can support explicit monkey patching if you prefer to use that strategy._
|
8
15
|
|
9
|
-
|
10
|
-
Think of it as a lightweight version of ActiveSupport that doesn't implicitly change native behavior.
|
16
|
+
## Immutabilty
|
11
17
|
|
12
|
-
|
18
|
+
Footing employs some principles of [immutability](https://en.wikipedia.org/wiki/Immutable_object) that are common in
|
19
|
+
[functional programming](https://en.wikipedia.org/wiki/Functional_programming).
|
20
|
+
The integrity of original objects/data is preserved because Footing creates a deep copy by default.
|
13
21
|
|
14
|
-
|
22
|
+
__NOTE:__ _This behavior can be overridden to improve performance... just be sure you know what you're doing_
|
15
23
|
|
16
|
-
|
17
|
-
Footing.patch! String, Footing::String
|
18
|
-
Footing.patch! Numeric, Footing::Numeric
|
19
|
-
```
|
24
|
+
## Hash
|
20
25
|
|
21
|
-
|
26
|
+
### Filter
|
22
27
|
|
23
|
-
|
24
|
-
String.ancestors
|
25
|
-
[
|
26
|
-
String,
|
27
|
-
Footing::String, # <--
|
28
|
-
Comparable,
|
29
|
-
Object,
|
30
|
-
Kernel,
|
31
|
-
BasicObject
|
32
|
-
]
|
33
|
-
|
34
|
-
Numeric.ancestors
|
35
|
-
[
|
36
|
-
Numeric,
|
37
|
-
Footing::Numeric, # <--
|
38
|
-
Comparable,
|
39
|
-
Object,
|
40
|
-
Kernel,
|
41
|
-
BasicObject
|
42
|
-
]
|
43
|
-
```
|
44
|
-
|
45
|
-
## Instance patching
|
46
|
-
|
47
|
-
If you don't want to corrupt the entire runtime, you can patch an instance.
|
28
|
+
Recursively filter out unwanted values based on key.
|
48
29
|
|
49
30
|
```ruby
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
31
|
+
data = { name: "Joe", password: "secret" }
|
32
|
+
copy = Footing::Hash.new(data)
|
33
|
+
copy.filter!(:password)
|
34
|
+
copy.inner_object # => {:name=>"Joe", :password=>"[FILTERED]"}
|
54
35
|
```
|
55
|
-
|
56
|
-
## Patch free
|
57
|
-
|
58
|
-
Dont like monkey patches? Run patch free by setting up utility methods instead.
|
59
|
-
|
60
|
-
```ruby
|
61
|
-
Footing.util! Footing::String
|
62
|
-
Footing::String.escape "foo", "o" # => "f\\o\\o"
|
63
|
-
```
|
64
|
-
|
65
|
-
## The Library
|
66
|
-
|
67
|
-
The suite of functionality is pretty small right now.
|
68
|
-
Poke around the [extensions directory](https://github.com/hopsoft/footing/tree/master/lib/footing/extensions) to see what's available.
|
69
|
-
|
70
|
-
Pull requests welcome.
|
71
|
-
|
data/Rakefile
CHANGED
data/footing.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "lib", "footing", "version")
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "footing"
|
5
|
+
spec.license = "MIT"
|
6
|
+
spec.version = Footing::VERSION
|
7
|
+
spec.homepage = "https://github.com/hopsoft/footing"
|
8
|
+
spec.summary = "A utility belt lib with sane monkey patching."
|
9
|
+
spec.description = "A utility belt lib with sane monkey patching."
|
10
|
+
|
11
|
+
spec.authors = ["Nathan Hopkins"]
|
12
|
+
spec.email = ["natehop@gmail.com"]
|
13
|
+
|
14
|
+
spec.files = Dir["lib/**/*.rb", "[A-Z]*"]
|
15
|
+
spec.test_files = Dir["lib/**/*.rb"]
|
16
|
+
|
17
|
+
spec.add_development_dependency "rake"
|
18
|
+
spec.add_development_dependency "pry-test"
|
19
|
+
spec.add_development_dependency "coveralls"
|
20
|
+
end
|
data/lib/footing.rb
CHANGED
@@ -1,55 +1,6 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
require "footing/version"
|
3
|
+
require "footing/object"
|
4
|
+
require "footing/hash"
|
5
|
+
require "pry"
|
3
6
|
|
4
|
-
module Footing
|
5
|
-
class << self
|
6
|
-
|
7
|
-
# Patches a Module or instance with the given extension.
|
8
|
-
# @param [Module, Object] obj The Module or instance to patch.
|
9
|
-
# @param [Module] extension The Module that contains the patches.
|
10
|
-
def patch!(obj, extension)
|
11
|
-
context = patch_context(obj)
|
12
|
-
raise "#{obj.class.name} doesn't support patching!" unless context
|
13
|
-
context.send :include, extension
|
14
|
-
end
|
15
|
-
|
16
|
-
# Creates class methods for all instance methods in the module.
|
17
|
-
# This allows users to invoke utility methods rather than monkey patching if they so desire.
|
18
|
-
# @param [Module] mod The Module to setup util methods for.
|
19
|
-
def util!(mod)
|
20
|
-
proxy = Class.new(SimpleDelegator)
|
21
|
-
Footing.patch! proxy, mod
|
22
|
-
|
23
|
-
mod.instance_methods(false).each do |method_name|
|
24
|
-
mod.define_singleton_method(method_name) do |target, *args|
|
25
|
-
method = proxy.instance_method(method_name)
|
26
|
-
target = proxy.new(target)
|
27
|
-
if method.parameters.empty?
|
28
|
-
method.bind(target).call
|
29
|
-
else
|
30
|
-
method.bind(target).call(*args)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def patch_context(obj)
|
39
|
-
context = obj if obj.is_a? Module
|
40
|
-
begin
|
41
|
-
context ||= class << obj
|
42
|
-
self
|
43
|
-
end
|
44
|
-
rescue Exception
|
45
|
-
end
|
46
|
-
context
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
Dir[File.expand_path("../**/*.rb", __FILE__)].each do |file|
|
53
|
-
next if file =~ /(lib\/footing\.rb|version\.rb)\z/
|
54
|
-
ENV["FOOTING_DEV"] ? load(file) : require(file)
|
55
|
-
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Footing
|
2
|
+
module HashMethods
|
3
|
+
|
4
|
+
# Recursively filters the values for the specified keys in place.
|
5
|
+
# IMPORTANT: This mutates the Hash.
|
6
|
+
#
|
7
|
+
# @param keys [Array<Symbol,String,Regexp>] The keys to filter
|
8
|
+
# @param replacement [Object] The replacement value to use
|
9
|
+
# @return [Footing::Hash] Returns self
|
10
|
+
def filter!(keys, replacement: "[FILTERED]")
|
11
|
+
should_replace = lambda do |key|
|
12
|
+
replace = false
|
13
|
+
keys.each do |k|
|
14
|
+
break if replace
|
15
|
+
replace = k.is_a?(Regexp) ? key.to_s =~ k : key.to_s == k.to_s
|
16
|
+
end
|
17
|
+
replace
|
18
|
+
end
|
19
|
+
|
20
|
+
Footing::Hash.new(self).inner_object.each do |key, value|
|
21
|
+
if value.is_a?(::Hash)
|
22
|
+
Footing::Hash.new(value, copy: false).filter!(keys, replacement: replacement)
|
23
|
+
elsif value.is_a?(Enumerable)
|
24
|
+
value.each do |val|
|
25
|
+
if val.is_a?(::Hash)
|
26
|
+
Footing::Hash.new(val, copy: false).filter!(keys, replacement: replacement)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
else
|
30
|
+
value = replacement if should_replace.call(key)
|
31
|
+
self[key] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/footing/hash.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Footing
|
2
|
+
class Object
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_reader :inner_object
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def target_name
|
9
|
+
self.name.gsub(/\AFooting::/, "")
|
10
|
+
end
|
11
|
+
|
12
|
+
def match?(o)
|
13
|
+
o.class.ancestors.map(&:name).include?(target_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def new(o, copy: true)
|
17
|
+
return o if o.is_a?(self)
|
18
|
+
super o, copy: copy
|
19
|
+
end
|
20
|
+
|
21
|
+
def copy(o)
|
22
|
+
Marshal.load Marshal.dump(o)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(o, copy: true)
|
27
|
+
raise ArgumentError.new("Types must match") unless self.class.match?(o)
|
28
|
+
o = self.class.copy(o) if copy
|
29
|
+
@inner_object = o
|
30
|
+
end
|
31
|
+
|
32
|
+
def eigen
|
33
|
+
@eigen ||= class << self
|
34
|
+
self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing(name, *args)
|
39
|
+
if inner_object.respond_to?(name)
|
40
|
+
eigen.instance_eval do
|
41
|
+
define_method(name) { |*a| inner_object.send name, *a }
|
42
|
+
end
|
43
|
+
return inner_object.send name, *args
|
44
|
+
end
|
45
|
+
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
def respond_to?(name)
|
50
|
+
return true if inner_object.respond_to?(name)
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/lib/footing/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: footing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Hopkins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: pry-test
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -39,35 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
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: pry
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: pry-stack_explorer
|
42
|
+
name: coveralls
|
71
43
|
requirement: !ruby/object:Gem::Requirement
|
72
44
|
requirements:
|
73
45
|
- - ">="
|
@@ -92,16 +64,11 @@ files:
|
|
92
64
|
- LICENSE.txt
|
93
65
|
- README.md
|
94
66
|
- Rakefile
|
67
|
+
- footing.gemspec
|
95
68
|
- lib/footing.rb
|
96
|
-
- lib/footing/
|
97
|
-
- lib/footing/
|
98
|
-
- lib/footing/
|
99
|
-
- lib/footing/extensions/nil_class.rb
|
100
|
-
- lib/footing/extensions/numeric.rb
|
101
|
-
- lib/footing/extensions/object.rb
|
102
|
-
- lib/footing/extensions/postgresql_adapter.rb
|
103
|
-
- lib/footing/extensions/schema_statements.rb
|
104
|
-
- lib/footing/extensions/string.rb
|
69
|
+
- lib/footing/concerns/hash_methods.rb
|
70
|
+
- lib/footing/hash.rb
|
71
|
+
- lib/footing/object.rb
|
105
72
|
- lib/footing/version.rb
|
106
73
|
homepage: https://github.com/hopsoft/footing
|
107
74
|
licenses:
|
@@ -123,19 +90,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
90
|
version: '0'
|
124
91
|
requirements: []
|
125
92
|
rubyforge_project:
|
126
|
-
rubygems_version: 2.
|
93
|
+
rubygems_version: 2.4.5
|
127
94
|
signing_key:
|
128
95
|
specification_version: 4
|
129
96
|
summary: A utility belt lib with sane monkey patching.
|
130
97
|
test_files:
|
131
|
-
- lib/footing/
|
132
|
-
- lib/footing/
|
133
|
-
- lib/footing/
|
134
|
-
- lib/footing/extensions/nil_class.rb
|
135
|
-
- lib/footing/extensions/numeric.rb
|
136
|
-
- lib/footing/extensions/object.rb
|
137
|
-
- lib/footing/extensions/postgresql_adapter.rb
|
138
|
-
- lib/footing/extensions/schema_statements.rb
|
139
|
-
- lib/footing/extensions/string.rb
|
98
|
+
- lib/footing/concerns/hash_methods.rb
|
99
|
+
- lib/footing/hash.rb
|
100
|
+
- lib/footing/object.rb
|
140
101
|
- lib/footing/version.rb
|
141
102
|
- lib/footing.rb
|
103
|
+
has_rdoc:
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Footing
|
2
|
-
module Array
|
3
|
-
|
4
|
-
# Recursively casts all string values in this Array.
|
5
|
-
# See Footing::String#cast
|
6
|
-
def cast_values!
|
7
|
-
each_with_index do |value, index|
|
8
|
-
if value.respond_to?(:cast_values!)
|
9
|
-
value.cast_values!
|
10
|
-
elsif value.respond_to?(:cast)
|
11
|
-
self[index] = value.cast
|
12
|
-
end
|
13
|
-
end
|
14
|
-
self
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
@@ -1,123 +0,0 @@
|
|
1
|
-
module Footing
|
2
|
-
module Hash
|
3
|
-
|
4
|
-
# Rekeys the Hash by invoking a method on the existing keys
|
5
|
-
# and uses the return value as the new key.
|
6
|
-
#
|
7
|
-
# NOTE: Creates and returns a new Hash.
|
8
|
-
#
|
9
|
-
# Example:
|
10
|
-
# h = { [1] => "short", [1,2] => "medium", [1,2,3] => "long" }
|
11
|
-
# h.rekey(:length) # => { 1 => "short", 2 => "medium", 3 => "long" }
|
12
|
-
#
|
13
|
-
# @param [Symbol] name The method name to invoke on the existing keys.
|
14
|
-
# @return [Hash] A new Hash that has been re-keyed.
|
15
|
-
def rekey(method_name)
|
16
|
-
reduce({}) do |new_hash, (key, value)|
|
17
|
-
new_hash[key.public_send(method_name)] = value
|
18
|
-
new_hash
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Recursively forces all String values to the specified encoding.
|
23
|
-
# @param [] encoding The encoding to use.
|
24
|
-
# @yield [value] Yields the value after the encoding has been applied.
|
25
|
-
def force_encoding!(encoding, &block)
|
26
|
-
each do |key, value|
|
27
|
-
if value.respond_to?(:force_encoding!)
|
28
|
-
value.force_encoding!(encoding, &block)
|
29
|
-
elsif value.is_a?(Enumerable)
|
30
|
-
value.each do |val|
|
31
|
-
next unless val.respond_to?(:force_encoding!)
|
32
|
-
val.force_encoding!(encoding, &block)
|
33
|
-
end
|
34
|
-
elsif value.is_a?(String)
|
35
|
-
# force encoding then strip all non ascii chars
|
36
|
-
if block_given?
|
37
|
-
self[key] = yield(value.force_encoding(encoding))
|
38
|
-
else
|
39
|
-
self[key] = value.force_encoding(encoding)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Recursively adjusts the values of the Hash in place.
|
46
|
-
#
|
47
|
-
# @example
|
48
|
-
# dict = {:a => 1, :b => 2, :c => 3}
|
49
|
-
# dict.adjust_values! { |v| v.to_s }
|
50
|
-
# dict # => {:a => "1", :b => "2", :c => "3"}
|
51
|
-
#
|
52
|
-
# @yield [value] Yields the current value to the block.
|
53
|
-
# The result of the block is then assigned to the corresponding key.
|
54
|
-
def adjust_values!(&block)
|
55
|
-
each do |key, value|
|
56
|
-
if value.respond_to?(:adjust_values!)
|
57
|
-
value.adjust_values!(&block)
|
58
|
-
elsif value.is_a?(Enumerable)
|
59
|
-
value.each do |val|
|
60
|
-
next unless val.respond_to?(:adjust_values!)
|
61
|
-
val.adjust_values!(&block)
|
62
|
-
end
|
63
|
-
else
|
64
|
-
self[key] = yield(value)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
self
|
68
|
-
end
|
69
|
-
|
70
|
-
# Recursively casts all string values in this Hash.
|
71
|
-
# See Footing::String#cast
|
72
|
-
def cast_values!
|
73
|
-
each do |key, value|
|
74
|
-
if value.respond_to?(:cast_values!)
|
75
|
-
value.cast_values!
|
76
|
-
elsif value.is_a?(Enumerable)
|
77
|
-
value.each do |val|
|
78
|
-
next unless val.respond_to?(:cast_values!)
|
79
|
-
val.cast_values!
|
80
|
-
end
|
81
|
-
elsif value.respond_to?(:cast)
|
82
|
-
self[key] = value.cast
|
83
|
-
end
|
84
|
-
end
|
85
|
-
self
|
86
|
-
end
|
87
|
-
|
88
|
-
def filter!(keys, replacement="[FILTERED]")
|
89
|
-
should_replace = lambda do |key|
|
90
|
-
replace = false
|
91
|
-
keys.each do |k|
|
92
|
-
break if replace
|
93
|
-
replace = k.is_a?(Regexp) ? key.to_s =~ k : key.to_s == k.to_s
|
94
|
-
end
|
95
|
-
replace
|
96
|
-
end
|
97
|
-
|
98
|
-
each do |key, value|
|
99
|
-
if value.respond_to?(:filter!)
|
100
|
-
value.filter!(keys, replacement)
|
101
|
-
elsif value.is_a?(Enumerable)
|
102
|
-
value.each do |val|
|
103
|
-
next unless val.respond_to?(:filter!)
|
104
|
-
val.filter!(keys, replacement)
|
105
|
-
end
|
106
|
-
else
|
107
|
-
value = replacement if should_replace.call(key)
|
108
|
-
self[key] = value
|
109
|
-
end
|
110
|
-
end
|
111
|
-
self
|
112
|
-
end
|
113
|
-
|
114
|
-
def silence!(keys)
|
115
|
-
filter! keys, nil
|
116
|
-
end
|
117
|
-
|
118
|
-
def copy
|
119
|
-
Marshal.load(Marshal.dump(self))
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Footing
|
2
|
-
module Kernel
|
3
|
-
|
4
|
-
# Safely evals text inside of a sandbox.
|
5
|
-
# @see http://phrogz.net/programmingruby/taint.html Ruby safe level description.
|
6
|
-
# @param [String] text The text to eval.
|
7
|
-
# @param [Integer] level The safe level to apply.
|
8
|
-
# @return [Object]
|
9
|
-
def safe_eval(text, level=4)
|
10
|
-
sandbox = lambda do
|
11
|
-
$SAFE = level
|
12
|
-
eval(text.to_s)
|
13
|
-
end
|
14
|
-
sandbox.call
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Footing
|
2
|
-
module NilClass
|
3
|
-
|
4
|
-
# Calling [] on nil returns nil instead of raising an exception.
|
5
|
-
# Helpful when looping over nested Hashes.
|
6
|
-
#
|
7
|
-
# @example
|
8
|
-
# dict = {}
|
9
|
-
# dict[:foo][:bar][:baz] # => nil
|
10
|
-
#
|
11
|
-
# @param [Object] key The key to lookup.
|
12
|
-
def [](key)
|
13
|
-
nil
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module Footing
|
2
|
-
module Numeric
|
3
|
-
|
4
|
-
# Returns a positive representation of the number.
|
5
|
-
def positive
|
6
|
-
abs
|
7
|
-
end
|
8
|
-
|
9
|
-
# Returns a negative representation of the number.
|
10
|
-
def negative
|
11
|
-
abs.flip_sign
|
12
|
-
end
|
13
|
-
|
14
|
-
# Flips the sign on the number making it either either positive or negative.
|
15
|
-
def flip_sign
|
16
|
-
self * -1
|
17
|
-
end
|
18
|
-
|
19
|
-
# Returns the percentage that this number is of the passed number.
|
20
|
-
# @example
|
21
|
-
# 8.percent_of(10) # => 80.0
|
22
|
-
# @param [Numeric] number The number to calculate the percentage with
|
23
|
-
def percent_of(number)
|
24
|
-
percent = (self.to_f / number.to_f) * 100 if number > 0
|
25
|
-
percent ||= 0.0
|
26
|
-
end
|
27
|
-
|
28
|
-
# Rounds the number to a certain number of decimal places.
|
29
|
-
# @example
|
30
|
-
# 1.784329.round_to(1) # => 1.8
|
31
|
-
# @param [Numeric] decimal_places The number of decimal places to round to
|
32
|
-
def round_to(decimal_places)
|
33
|
-
(self * 10**decimal_places).round.to_f / 10**decimal_places
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module Footing
|
2
|
-
module Object
|
3
|
-
|
4
|
-
# Returns the eigen class for the object.
|
5
|
-
def eigen
|
6
|
-
class << self
|
7
|
-
self
|
8
|
-
end
|
9
|
-
rescue Exception
|
10
|
-
nil
|
11
|
-
end
|
12
|
-
|
13
|
-
# Indicates if the object has an eigen class.
|
14
|
-
def has_eigen?
|
15
|
-
!eigen.nil?
|
16
|
-
end
|
17
|
-
|
18
|
-
# Trys to invoke a method on the object.
|
19
|
-
# Returns nil if the method isn't supported.
|
20
|
-
def try(name, *args, &block)
|
21
|
-
if respond_to?(name)
|
22
|
-
return public_send(name, *args, &block)
|
23
|
-
end
|
24
|
-
nil
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# Extend Rails with this module to add a uuid method in your migrations.
|
2
|
-
#
|
3
|
-
# Example:
|
4
|
-
# # rails_root/config/application.rb
|
5
|
-
# config.after_initialize do
|
6
|
-
# Footing.patch! ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::TableDefinition, Footing::PGTableDefinition
|
7
|
-
# end
|
8
|
-
#
|
9
|
-
module Footing
|
10
|
-
module PGTableDefinition
|
11
|
-
|
12
|
-
# Provides a uuid method when inside a migration's create_table block.
|
13
|
-
def uuid(*args)
|
14
|
-
options = args.extract_options!
|
15
|
-
#column(args[0], 'uuid default uuid_generate_v1()', options)
|
16
|
-
column(args[0], 'uuid', options)
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
# Extend Rails to support adding timestamp indexes for created_at & updated_at using day granularity.
|
2
|
-
#
|
3
|
-
# @example Make these statements available to ActiveRecord::Migration change, up, down methods
|
4
|
-
# # rails_root/config/application.rb
|
5
|
-
# config.after_initialize do
|
6
|
-
# Footing.patch! ActiveRecord::ConnectionAdapters::AbstractAdapter, Footing::PGSchemaStatements
|
7
|
-
# end
|
8
|
-
#
|
9
|
-
module Footing
|
10
|
-
module PGSchemaStatements
|
11
|
-
|
12
|
-
# Adds indexes to the created_at and updated_at timestamp columns with 'day' granularity.
|
13
|
-
def add_timestamp_indexes(table_name)
|
14
|
-
%w(created_at updated_at).each do |column_name|
|
15
|
-
add_datetime_index(table_name, column_name, :precision => :day)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
# Removes indexes to the created_at and updated_at timestamp columns with 'day' granularity.
|
20
|
-
def remove_timestamp_indexes(table_name)
|
21
|
-
%w(created_at updated_at).each do |column_name|
|
22
|
-
remove_datetime_index(table_name, column_name, :precision => :day)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Adds an index on a datetime column using the specified precision.
|
27
|
-
# @param [Symbol,String] table_name The name of the table to migrate.
|
28
|
-
# @param [Symbol,String] column_name The name of the column to migrate.
|
29
|
-
# @param [Hash] options
|
30
|
-
# @option options [Symbol,String] :precision
|
31
|
-
# - microseconds
|
32
|
-
# - milliseconds
|
33
|
-
# - second
|
34
|
-
# - minute *default
|
35
|
-
# - hour
|
36
|
-
# - day
|
37
|
-
# - week
|
38
|
-
# - month
|
39
|
-
# - quarter
|
40
|
-
# - year
|
41
|
-
# - decade
|
42
|
-
# - century
|
43
|
-
# - millennium
|
44
|
-
def add_datetime_index(table_name, column_name, options={})
|
45
|
-
options[:precision] ||= :minute
|
46
|
-
index_name = "index_#{table_name}_on_#{column_name}_by_#{options[:precision]}"
|
47
|
-
execute "create index #{index_name} on #{quote_table_name(table_name)} (date_trunc('#{options[:precision]}', #{quote_column_name(column_name)}))"
|
48
|
-
end
|
49
|
-
|
50
|
-
# Removes an index on a datetime column using the specified precision.
|
51
|
-
# @param [Symbol,String] table_name The name of the table to migrate.
|
52
|
-
# @param [Symbol,String] column_name The name of the column to migrate.
|
53
|
-
# @param [Hash] options
|
54
|
-
# @option options [Symbol,String] :precision
|
55
|
-
# - microseconds
|
56
|
-
# - milliseconds
|
57
|
-
# - second
|
58
|
-
# - minute *default
|
59
|
-
# - hour
|
60
|
-
# - day
|
61
|
-
# - week
|
62
|
-
# - month
|
63
|
-
# - quarter
|
64
|
-
# - year
|
65
|
-
# - decade
|
66
|
-
# - century
|
67
|
-
# - millennium
|
68
|
-
def remove_datetime_index(table_name, column_name, options={})
|
69
|
-
options[:precision] ||= :minute
|
70
|
-
index_name = "index_#{table_name}_on_#{column_name}_by_#{options[:precision]}"
|
71
|
-
execute "drop index if exists #{index_name}"
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
module Footing
|
2
|
-
module String
|
3
|
-
|
4
|
-
# Generates a random string.
|
5
|
-
#
|
6
|
-
# @example
|
7
|
-
# Footing.util! Footing::String
|
8
|
-
# Footing::String.random_key(100, :reject => ["1", "I", "l", "0", "O"])
|
9
|
-
#
|
10
|
-
# @param [Integer] length The length of the key to generate. Defaults to 12.
|
11
|
-
# @param [Hash] opts
|
12
|
-
# @option opts [Array] :reject A list of characters to omit from the key.
|
13
|
-
# @option opts [Boolean] :upcase Indicates that only use uppercase characters should be used.
|
14
|
-
# @return [String]
|
15
|
-
def random(length=12, opts={})
|
16
|
-
@chars ||= [(0..9).to_a, ('a'..'z').to_a, ('A'..'Z').to_a].flatten.map { |c| c.to_s }
|
17
|
-
chars = @chars.reject do |c|
|
18
|
-
c =~ /[a-z]/ if opts[:upcase]
|
19
|
-
end
|
20
|
-
opts[:reject] ||= []
|
21
|
-
opts[:reject] = opts[:reject].map { |c| c.to_s }
|
22
|
-
(1..length).map{ |i| (chars - opts[:reject]).sample }.join
|
23
|
-
end
|
24
|
-
|
25
|
-
# Escapes a series of chars.
|
26
|
-
def escape(*chars)
|
27
|
-
gsub(/(?<!\\)(#{chars.join("|")})/) do |char|
|
28
|
-
"\\" + char
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Converts a word with underscores into a sentance with a capitalized first word.
|
33
|
-
def humanize
|
34
|
-
self.downcase.gsub(/_/, " ").capitalize
|
35
|
-
end
|
36
|
-
|
37
|
-
# Similar to humanize but it capitalizes each word
|
38
|
-
def titleize
|
39
|
-
self.gsub(/\s/, "_").split('_').map(&:capitalize).join(' ')
|
40
|
-
end
|
41
|
-
alias :titlecase :titleize
|
42
|
-
|
43
|
-
|
44
|
-
def classify
|
45
|
-
self.gsub(/\s/, "_").titleize.gsub(/\W/, "")
|
46
|
-
end
|
47
|
-
|
48
|
-
# Indicates if this string represents a number.
|
49
|
-
def numeric?
|
50
|
-
!!(self =~ /\A[0-9]+\.*[0-9]*\z/)
|
51
|
-
end
|
52
|
-
|
53
|
-
# Indicates if this string represents a boolean value.
|
54
|
-
def boolean?
|
55
|
-
!!(self =~ /\A(true|false)\z/i)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Casts this string to another datatype.
|
59
|
-
# Supported datatypes:
|
60
|
-
# * Integer
|
61
|
-
# * Float
|
62
|
-
# * Boolean
|
63
|
-
def cast
|
64
|
-
return to_f if numeric? && index(".")
|
65
|
-
return to_i if numeric?
|
66
|
-
if boolean?
|
67
|
-
return true if self =~ /\Atrue\z/i
|
68
|
-
return false if self =~ /\Afalse\z/i
|
69
|
-
end
|
70
|
-
self
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|