bogo 0.1.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/CHANGELOG.md +2 -0
- data/CONTRIBUTING.md +25 -0
- data/LICENSE +13 -0
- data/README.md +6 -0
- data/bogo.gemspec +15 -0
- data/lib/bogo.rb +10 -0
- data/lib/bogo/animal_strings.rb +24 -0
- data/lib/bogo/lazy.rb +238 -0
- data/lib/bogo/memoization.rb +53 -0
- data/lib/bogo/smash.rb +151 -0
- data/lib/bogo/version.rb +4 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9b544e3d18be2de80a851293f139ef363d3e2460
|
4
|
+
data.tar.gz: 2634b067067e337a84006f5583263cbb18399930
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7c2f47cfac15a3cbcb72bd8e4d74dc2e2954512f7c8b4afff66624133eb5dfc93afaf2c16db37278cb38e4ff375fd632c8f2ec63fba83403f924f17139133f4d
|
7
|
+
data.tar.gz: d5df748c23547d30bf5db35672c94bb489cfabbaf35125870a451e3c5dc6eb4e038358e7831deb0a7623d3706884e9ea312329acb281be7393bf36f86cf23bba
|
data/CHANGELOG.md
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
## Branches
|
4
|
+
|
5
|
+
### `master` branch
|
6
|
+
|
7
|
+
The master branch is the current stable released version.
|
8
|
+
|
9
|
+
### `develop` branch
|
10
|
+
|
11
|
+
The develop branch is the current edge of development.
|
12
|
+
|
13
|
+
## Pull requests
|
14
|
+
|
15
|
+
* https://github.com/spox/bogo/pulls
|
16
|
+
|
17
|
+
Please base all pull requests of the `develop` branch. Merges to
|
18
|
+
`master` only occur through the `develop` branch. Pull requests
|
19
|
+
based on `master` will likely be cherry picked.
|
20
|
+
|
21
|
+
## Issues
|
22
|
+
|
23
|
+
Need to report an issue? Use the github issues:
|
24
|
+
|
25
|
+
* https://github.com/spox/bogo/issues
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2014 Chris Roberts
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
data/bogo.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
|
2
|
+
require 'bogo/version'
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'bogo'
|
5
|
+
s.version = Bogo::VERSION.version
|
6
|
+
s.summary = 'Helper libraries'
|
7
|
+
s.author = 'Chris Roberts'
|
8
|
+
s.email = 'code@chrisroberts.org'
|
9
|
+
s.homepage = 'https://github.com/spox/bogo'
|
10
|
+
s.description = 'Helper libraries'
|
11
|
+
s.require_path = 'lib'
|
12
|
+
s.license = 'Apache 2.0'
|
13
|
+
s.add_dependency 'hashie'
|
14
|
+
s.files = Dir['lib/**/*'] + %w(bogo.gemspec README.md CHANGELOG.md CONTRIBUTING.md LICENSE)
|
15
|
+
end
|
data/lib/bogo.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bogo'
|
2
|
+
|
3
|
+
module Bogo
|
4
|
+
# Animal stylings on strings
|
5
|
+
module AnimalStrings
|
6
|
+
|
7
|
+
# Camel case string
|
8
|
+
# @param string [String]
|
9
|
+
# @return [String]
|
10
|
+
def camel(string)
|
11
|
+
string.to_s.split('_').map{|k| "#{k.slice(0,1).upcase}#{k.slice(1,k.length)}"}.join
|
12
|
+
end
|
13
|
+
|
14
|
+
# Snake case (underscore) string
|
15
|
+
#
|
16
|
+
# @param string [String]
|
17
|
+
# @return [String]
|
18
|
+
def snake(string)
|
19
|
+
string.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').gsub('-', '_').downcase
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/lib/bogo/lazy.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
require 'bogo'
|
2
|
+
require 'digest/sha2'
|
3
|
+
|
4
|
+
module Bogo
|
5
|
+
# Adds functionality to facilitate laziness
|
6
|
+
module Lazy
|
7
|
+
|
8
|
+
# Instance methods for laziness
|
9
|
+
module InstanceMethods
|
10
|
+
|
11
|
+
# @return [Smash] argument hash
|
12
|
+
def data
|
13
|
+
unless(@data)
|
14
|
+
@data = Smash.new
|
15
|
+
end
|
16
|
+
@data
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Smash] updated data
|
20
|
+
def dirty
|
21
|
+
unless(@dirty)
|
22
|
+
@dirty = Smash.new
|
23
|
+
end
|
24
|
+
@dirty
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Smash] current data state
|
28
|
+
def attributes
|
29
|
+
data.merge(dirty)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create new instance
|
33
|
+
#
|
34
|
+
# @param args [Hash]
|
35
|
+
# @return [self]
|
36
|
+
def load_data(args={})
|
37
|
+
args = args.to_smash
|
38
|
+
@data = Smash.new
|
39
|
+
self.class.attributes.each do |name, options|
|
40
|
+
val = args[name]
|
41
|
+
if(options[:required] && !args.has_key?(name) && !options.has_key?(:default))
|
42
|
+
raise ArgumentError.new("Missing required option: `#{name}`")
|
43
|
+
end
|
44
|
+
if(val.nil? && !args.has_key?(name) && options[:default])
|
45
|
+
if(options[:default])
|
46
|
+
val = options[:default].respond_to?(:call) ? options[:default].call : options[:default]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
if(args.has_key?(name) || val)
|
50
|
+
self.send("#{name}=", val)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# Identifies valid state and automatically
|
57
|
+
# merges dirty attributes into data, clears
|
58
|
+
# dirty attributes
|
59
|
+
#
|
60
|
+
# @return [self]
|
61
|
+
def valid_state
|
62
|
+
data.merge!(dirty)
|
63
|
+
dirty.clear
|
64
|
+
@_checksum = Digest::SHA256.hexdigest(MultiJson.dump(data))
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Model is dirty or specific attribute is dirty
|
69
|
+
#
|
70
|
+
# @param attr [String, Symbol] name of attribute
|
71
|
+
# @return [TrueClass, FalseClass] model or attribute is dirty
|
72
|
+
def dirty?(attr=nil)
|
73
|
+
if(attr)
|
74
|
+
dirty.has_key?(attr)
|
75
|
+
else
|
76
|
+
if(@_checksum)
|
77
|
+
!dirty.empty? ||
|
78
|
+
@_checksum != Digest::SHA256.hexdigest(MultiJson.dump(data))
|
79
|
+
else
|
80
|
+
true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [String]
|
86
|
+
def to_s
|
87
|
+
"<#{self.class.name}:#{object_id}>"
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [String]
|
91
|
+
def inspect
|
92
|
+
"<#{self.class.name}:#{object_id} [#{data.inspect}]>"
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
# Class methods for laziness
|
98
|
+
module ClassMethods
|
99
|
+
|
100
|
+
# Add new attributes to class
|
101
|
+
#
|
102
|
+
# @param name [String]
|
103
|
+
# @param type [Class, Array<Class>]
|
104
|
+
# @param options [Hash]
|
105
|
+
# @option options [TrueClass, FalseClass] :required must be provided on initialization
|
106
|
+
# @option options [Object, Proc] :default default value
|
107
|
+
# @option options [Proc] :coerce
|
108
|
+
# @return [nil]
|
109
|
+
def attribute(name, type, options={})
|
110
|
+
name = name.to_sym
|
111
|
+
options = options.to_smash
|
112
|
+
attributes[name] = Smash.new(:type => type).merge(options)
|
113
|
+
coerce = attributes[name][:coerce]
|
114
|
+
valid_types = [attributes[name][:type], NilClass].flatten.compact
|
115
|
+
allowed_values = attributes[name][:allowed]
|
116
|
+
multiple_values = attributes[name][:multiple]
|
117
|
+
depends_on = attributes[name][:depends]
|
118
|
+
define_method(name) do
|
119
|
+
send(depends_on) if depends_on
|
120
|
+
self.class.on_missing(self) unless data.has_key?(name) || dirty.has_key?(name)
|
121
|
+
dirty[name] || data[name]
|
122
|
+
end
|
123
|
+
define_method("#{name}=") do |val|
|
124
|
+
values = multiple_values && val.is_a?(Array) ? val : [val]
|
125
|
+
values.map! do |item|
|
126
|
+
valid_type = valid_types.detect do |klass|
|
127
|
+
item.is_a?(klass)
|
128
|
+
end
|
129
|
+
if(coerce && !valid_type)
|
130
|
+
item = coerce.arity == 2 ? coerce.call(item, self) : coerce.call(item)
|
131
|
+
end
|
132
|
+
valid_type = valid_types.detect do |klass|
|
133
|
+
item.is_a?(klass)
|
134
|
+
end
|
135
|
+
unless(valid_type)
|
136
|
+
raise TypeError.new("Invalid type for `#{name}` (#{item} <#{item.class}>). Valid - #{valid_types.map(&:to_s).join(',')}")
|
137
|
+
end
|
138
|
+
if(allowed_values)
|
139
|
+
unless(allowed_values.include?(item))
|
140
|
+
raise ArgumentError.new("Invalid value provided for `#{name}` (#{item.inspect}). Allowed - #{allowed_values.map(&:inspect).join(', ')}")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
item
|
144
|
+
end
|
145
|
+
if(!multiple_values && !val.is_a?(Array))
|
146
|
+
dirty[name] = values.first
|
147
|
+
else
|
148
|
+
dirty[name] = values
|
149
|
+
end
|
150
|
+
end
|
151
|
+
define_method("#{name}?") do
|
152
|
+
send(depends_on) if depends_on
|
153
|
+
self.class.on_missing(self) unless data.has_key?(name)
|
154
|
+
!!data[name]
|
155
|
+
end
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
|
159
|
+
# Return attributes
|
160
|
+
#
|
161
|
+
# @param args [Symbol] :required or :optional
|
162
|
+
# @return [Array<Hash>]
|
163
|
+
def attributes(*args)
|
164
|
+
@attributes ||= Smash.new
|
165
|
+
if(args.include?(:required))
|
166
|
+
Smash[@attributes.find_all{|k,v| v[:required]}]
|
167
|
+
elsif(args.include?(:optional))
|
168
|
+
Smash[@attributes.find_all{|k,v| !v[:required]}]
|
169
|
+
else
|
170
|
+
@attributes
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Instance method to call on missing attribute or
|
175
|
+
# object to call method on if set
|
176
|
+
#
|
177
|
+
# @param param [Symbol, Object]
|
178
|
+
# @return [Symbol]
|
179
|
+
def on_missing(param=nil)
|
180
|
+
if(param)
|
181
|
+
if(param.is_a?(Symbol))
|
182
|
+
@missing_method = param
|
183
|
+
else
|
184
|
+
if(@missing_method && !@calling_on_missing)
|
185
|
+
@calling_on_missing = true
|
186
|
+
begin
|
187
|
+
param.send(@missing_method)
|
188
|
+
ensure
|
189
|
+
@calling_on_missing = false
|
190
|
+
end
|
191
|
+
end
|
192
|
+
@missing_method
|
193
|
+
end
|
194
|
+
else
|
195
|
+
@missing_method
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Directly set attribute hash
|
200
|
+
#
|
201
|
+
# @param attrs [Hash]
|
202
|
+
# @return [TrueClass]
|
203
|
+
# @todo need deep dup here
|
204
|
+
def set_attributes(attrs)
|
205
|
+
@attributes = attrs.to_smash
|
206
|
+
true
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
class << self
|
212
|
+
|
213
|
+
# Injects laziness into class
|
214
|
+
#
|
215
|
+
# @param klass [Class]
|
216
|
+
def included(klass)
|
217
|
+
klass.class_eval do
|
218
|
+
include InstanceMethods
|
219
|
+
extend ClassMethods
|
220
|
+
|
221
|
+
class << self
|
222
|
+
|
223
|
+
def inherited(klass)
|
224
|
+
klass.set_attributes(
|
225
|
+
MultiJson.load(
|
226
|
+
MultiJson.dump(self.attributes)
|
227
|
+
).to_smash
|
228
|
+
)
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'bogo'
|
2
|
+
|
3
|
+
module Bogo
|
4
|
+
# Memoization helpers
|
5
|
+
module Memoization
|
6
|
+
|
7
|
+
# Memoize data
|
8
|
+
#
|
9
|
+
# @param key [String, Symbol] identifier for data
|
10
|
+
# @param direct [Truthy, Falsey] direct skips key prepend of object id
|
11
|
+
# @yield block to create data
|
12
|
+
# @yieldreturn data to memoize
|
13
|
+
# @return [Object] data
|
14
|
+
def memoize(key, direct=false)
|
15
|
+
unless(direct)
|
16
|
+
key = "#{self.object_id}_#{key}"
|
17
|
+
end
|
18
|
+
unless(_memo.has_key?(key))
|
19
|
+
_memo[key] = yield
|
20
|
+
end
|
21
|
+
_memo[key]
|
22
|
+
end
|
23
|
+
|
24
|
+
def _memo
|
25
|
+
Thread.current[:bogo_memoization] ||= Smash.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# Remove memoized value
|
29
|
+
#
|
30
|
+
# @param key [String, Symbol] identifier for data
|
31
|
+
# @param direct [Truthy, Falsey] direct skips key prepend of object id
|
32
|
+
# @return [NilClass]
|
33
|
+
def unmemoize(key, direct=false)
|
34
|
+
unless(direct)
|
35
|
+
key = "#{self.object_id}_#{key}"
|
36
|
+
end
|
37
|
+
_memo.delete(key)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Remove all memoized values
|
41
|
+
#
|
42
|
+
# @return [TrueClass]
|
43
|
+
def clear_memoizations!
|
44
|
+
_memo.keys.find_all do |key|
|
45
|
+
key.to_s.start_with?("#{self.object_id}_")
|
46
|
+
end.each do |key|
|
47
|
+
_memo.delete(key)
|
48
|
+
end
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/bogo/smash.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
require 'digest/sha2'
|
3
|
+
require 'bogo'
|
4
|
+
|
5
|
+
module Bogo
|
6
|
+
|
7
|
+
# Customized Hash
|
8
|
+
class Smash < Hash
|
9
|
+
include Hashie::Extensions::IndifferentAccess
|
10
|
+
include Hashie::Extensions::MergeInitializer
|
11
|
+
include Hashie::Extensions::DeepMerge
|
12
|
+
include Hashie::Extensions::Coercion
|
13
|
+
|
14
|
+
coerce_value Hash, Smash
|
15
|
+
|
16
|
+
# Create new instance
|
17
|
+
#
|
18
|
+
# @param args [Object] argument list
|
19
|
+
def initialize(*args)
|
20
|
+
base = nil
|
21
|
+
if(args.first.is_a?(::Hash))
|
22
|
+
base = args.shift
|
23
|
+
end
|
24
|
+
super *args
|
25
|
+
if(base)
|
26
|
+
self.replace(base.to_smash)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def merge!(hash)
|
31
|
+
hash = hash.to_smash
|
32
|
+
super(hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Get value at given path
|
36
|
+
#
|
37
|
+
# @param args [String, Symbol] key path to walk
|
38
|
+
# @return [Object, NilClass]
|
39
|
+
def retrieve(*args)
|
40
|
+
args.inject(self) do |memo, key|
|
41
|
+
if(memo.is_a?(Hash))
|
42
|
+
memo.to_smash[key]
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
alias_method :get, :retrieve
|
49
|
+
|
50
|
+
# Fetch value at given path or return a default value
|
51
|
+
#
|
52
|
+
# @param args [String, Symbol, Object] key path to walk. last value default to return
|
53
|
+
# @return [Object] value at key or default value
|
54
|
+
def fetch(*args)
|
55
|
+
default_value = args.pop
|
56
|
+
retrieve(*args) || default_value
|
57
|
+
end
|
58
|
+
|
59
|
+
# Set value at given path
|
60
|
+
#
|
61
|
+
# @param args [String, Symbol, Object] key path to walk. set last value to given path
|
62
|
+
# @return [Object] value set
|
63
|
+
def set(*args)
|
64
|
+
unless(args.size > 1)
|
65
|
+
raise ArgumentError.new 'Set requires at least one key and a value'
|
66
|
+
end
|
67
|
+
value = args.pop
|
68
|
+
set_key = args.pop
|
69
|
+
leaf = args.inject(self) do |memo, key|
|
70
|
+
unless(memo[key].is_a?(Hash))
|
71
|
+
memo[key] = Smash.new
|
72
|
+
end
|
73
|
+
memo[key]
|
74
|
+
end
|
75
|
+
leaf[set_key] = value
|
76
|
+
value
|
77
|
+
end
|
78
|
+
|
79
|
+
# Convert to Hash
|
80
|
+
#
|
81
|
+
# @return [Hash]
|
82
|
+
def to_hash(*args)
|
83
|
+
self.to_type_converter(::Hash, :to_hash, *args)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Calculate checksum of hash (sha256)
|
87
|
+
#
|
88
|
+
# @return [String] checksum
|
89
|
+
def checksum
|
90
|
+
Digest::SHA256.hexdigest(self.to_smash(:sorted).to_s)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Hook helper into toplevel `Hash`
|
97
|
+
class Hash
|
98
|
+
|
99
|
+
# Convert to Smash
|
100
|
+
#
|
101
|
+
# @return [Smash]
|
102
|
+
def to_smash(*args)
|
103
|
+
self.to_type_converter(::Smash, :to_smash, *args)
|
104
|
+
end
|
105
|
+
alias_method :hulk_smash, :to_smash
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
109
|
+
# Convert to type
|
110
|
+
#
|
111
|
+
# @param type [Class] hash type
|
112
|
+
# @param convert_call [Symbol] builtin hash convert
|
113
|
+
# @return [Smash]
|
114
|
+
def to_type_converter(type, convert_call, *args)
|
115
|
+
type.new.tap do |smash|
|
116
|
+
if(args.include?(:sorted))
|
117
|
+
process = self.sort_by do |entry|
|
118
|
+
entry.first.to_s
|
119
|
+
end
|
120
|
+
else
|
121
|
+
process = self
|
122
|
+
end
|
123
|
+
process.each do |k,v|
|
124
|
+
smash[k.is_a?(Symbol) ? k.to_s : k] = smash_conversion(v, convert_call, *args)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Convert object to smash if applicable
|
130
|
+
#
|
131
|
+
# @param obj [Object]
|
132
|
+
# @param convert_call [Symbol] builtin hash convert
|
133
|
+
# @return [Smash, Object]
|
134
|
+
def smash_conversion(obj, convert_call, *args)
|
135
|
+
case obj
|
136
|
+
when Hash
|
137
|
+
obj.send(convert_call, *args)
|
138
|
+
when Array
|
139
|
+
obj.map do |i|
|
140
|
+
smash_conversion(i, convert_call, *args)
|
141
|
+
end
|
142
|
+
else
|
143
|
+
obj
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
unless(defined?(Smash))
|
150
|
+
Smash = Bogo::Smash
|
151
|
+
end
|
data/lib/bogo/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bogo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Roberts
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: hashie
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Helper libraries
|
28
|
+
email: code@chrisroberts.org
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- CHANGELOG.md
|
34
|
+
- CONTRIBUTING.md
|
35
|
+
- LICENSE
|
36
|
+
- README.md
|
37
|
+
- bogo.gemspec
|
38
|
+
- lib/bogo.rb
|
39
|
+
- lib/bogo/animal_strings.rb
|
40
|
+
- lib/bogo/lazy.rb
|
41
|
+
- lib/bogo/memoization.rb
|
42
|
+
- lib/bogo/smash.rb
|
43
|
+
- lib/bogo/version.rb
|
44
|
+
homepage: https://github.com/spox/bogo
|
45
|
+
licenses:
|
46
|
+
- Apache 2.0
|
47
|
+
metadata: {}
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 2.2.2
|
65
|
+
signing_key:
|
66
|
+
specification_version: 4
|
67
|
+
summary: Helper libraries
|
68
|
+
test_files: []
|
69
|
+
has_rdoc:
|