duck_puncher 2.3.0 → 2.4.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 +4 -4
- data/README.md +33 -24
- data/bin/console +1 -0
- data/lib/duck_puncher/duck.rb +30 -0
- data/lib/duck_puncher/ducks/active_record.rb +48 -0
- data/lib/duck_puncher/ducks/array.rb +18 -0
- data/lib/duck_puncher/ducks/hash.rb +25 -0
- data/lib/duck_puncher/ducks/method.rb +56 -0
- data/lib/duck_puncher/ducks/numeric.rb +51 -0
- data/lib/duck_puncher/ducks/object.rb +15 -0
- data/lib/duck_puncher/ducks/string.rb +13 -0
- data/lib/duck_puncher/ducks.rb +29 -0
- data/lib/duck_puncher/gem_installer.rb +0 -3
- data/lib/duck_puncher/version.rb +1 -1
- data/lib/duck_puncher.rb +31 -12
- data/test/duck_puncher/string_test.rb +9 -0
- metadata +11 -9
- data/lib/duck_puncher/active_record_extensions.rb +0 -48
- data/lib/duck_puncher/array.rb +0 -18
- data/lib/duck_puncher/hash.rb +0 -25
- data/lib/duck_puncher/method.rb +0 -56
- data/lib/duck_puncher/numeric.rb +0 -51
- data/lib/duck_puncher/object.rb +0 -19
- data/lib/duck_puncher/string.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d30be8964065fb57d3de53e4e23b2deaf8ceb277
|
4
|
+
data.tar.gz: f2bc471c1a31a1f438680f21440b86d489092cd1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e117be77a8e6a2a80f0ba415315039fa727ba1495e52bfa9b83cd7b5d631b92ebce331eaac51f6b7fb2d5c76a00e2f88f19892f4f84039686fab277976a40588
|
7
|
+
data.tar.gz: 64a2e78858356beacb9f30ea6cd6955046c8f90fc596fd0d15c64cbe2e121423e85b9ffe384e08826655f45eddc74bae824a6c401877e65ebf62c44908adc851
|
data/README.md
CHANGED
@@ -2,33 +2,42 @@
|
|
2
2
|
|
3
3
|
These are the ducks I can punch:
|
4
4
|
|
5
|
-
Array#m => `[].m(:to_s)`
|
6
|
-
Array#mm => `[].mm(:sub, /[aeiou]/, '*')`
|
7
|
-
Array#get => `[].methods.get('ty?')`
|
8
|
-
Hash#seek => `{a: 1, b: {c: 2}}.seek(:b, :c)`
|
9
|
-
Numeric#to_currency => `25.245.to_currency`
|
10
|
-
Numeric#to_duration => `10_000.to_duration`
|
11
|
-
Numeric#to_time_ago => `10_000.to_time_ago`
|
12
|
-
Numeric#to_rad => `10.15.to_rad`
|
13
|
-
String#pluralize => `'hour'.pluralize(2)`
|
14
|
-
|
15
|
-
Object#
|
16
|
-
>> `require 'pry'`
|
17
|
-
LoadError: cannot load such file -- pry
|
18
|
-
from (irb):1:in `require'
|
19
|
-
from (irb):1
|
20
|
-
from bin/console:10:in `<main>'
|
21
|
-
>> require! 'pry'
|
22
|
-
Fetching: method_source-0.8.2.gem (100%)
|
23
|
-
Fetching: slop-3.6.0.gem (100%)
|
24
|
-
Fetching: coderay-1.1.0.gem (100%)
|
25
|
-
Fetching: pry-0.10.3.gem (100%)
|
26
|
-
=> true
|
27
|
-
>> Pry.start
|
28
|
-
[1] pry(main)>
|
5
|
+
Array#m => `[].m(:to_s)` => `[].map(&:to_s)`
|
6
|
+
Array#mm => `[].mm(:sub, /[aeiou]/, '*')` => `[].map { |x| x.sub(/[aeiou]/, '*') }`
|
7
|
+
Array#get => `[].methods.get('ty?')` => [:empty?]
|
8
|
+
Hash#seek => `{a: 1, b: {c: 2}}.seek(:b, :c)` => 2
|
9
|
+
Numeric#to_currency => `25.245.to_currency` => 25.25
|
10
|
+
Numeric#to_duration => `10_000.to_duration` => '2 h 46 min'
|
11
|
+
Numeric#to_time_ago => `10_000.to_time_ago` => '2 hours ago'
|
12
|
+
Numeric#to_rad => `10.15.to_rad` => 0.17715091907742445
|
13
|
+
String#pluralize => `'hour'.pluralize(2)` => "hours"
|
14
|
+
String#underscore => `'DuckPuncher::JSONStorage'.underscore` => 'duck_puncher/json_storage'
|
15
|
+
Object#clone! => `Object.new.clone!` => a deep clone of the object (using Marshal.dump)
|
29
16
|
Method#to_instruct => `Benchmark.method(:measure).to_instruct` returns the Ruby VM instruction sequence for the method
|
30
17
|
Method#to_source => `Benchmark.method(:measure).to_source` returns the method definition as a string
|
18
|
+
|
19
|
+
I also provide an experimental punch that tries to download the required gem if it doesn't exist on your computer. The
|
20
|
+
method is called `require!` and works like this:
|
21
|
+
|
22
|
+
Downloads and activates a gem for the current and subsequent consoles. For example:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
>> `require 'pry'`
|
26
|
+
LoadError: cannot load such file -- pry
|
27
|
+
from (irb):1:in `require'
|
28
|
+
from (irb):1
|
29
|
+
from bin/console:10:in `<main>'
|
30
|
+
>> require! 'pry'
|
31
|
+
Fetching: method_source-0.8.2.gem (100%)
|
32
|
+
Fetching: slop-3.6.0.gem (100%)
|
33
|
+
Fetching: coderay-1.1.0.gem (100%)
|
34
|
+
Fetching: pry-0.10.3.gem (100%)
|
35
|
+
=> true
|
36
|
+
>> Pry.start
|
37
|
+
[1] pry(main)>
|
38
|
+
```
|
31
39
|
|
40
|
+
Try it out if your feeling frisky! However, I noticed it doesn't work well with bigger gems and those with native extensions.
|
32
41
|
|
33
42
|
## Install
|
34
43
|
|
data/bin/console
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
module DuckPuncher
|
2
|
+
class Duck
|
3
|
+
attr_accessor :name, :options
|
4
|
+
|
5
|
+
def initialize(name, options = {})
|
6
|
+
@name = name
|
7
|
+
@options = options
|
8
|
+
@punched = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_path
|
12
|
+
"duck_puncher/ducks/#{name.to_s.downcase}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def punch
|
16
|
+
return false if options[:if] && !options[:if].call
|
17
|
+
options[:before].call if options[:before]
|
18
|
+
const.send :include, DuckPuncher::Ducks.const_get(name)
|
19
|
+
@punched = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def const
|
23
|
+
@const ||= (options[:class] || name).to_s.split('::').inject(Kernel) { |k, part| k.const_get part }
|
24
|
+
end
|
25
|
+
|
26
|
+
def punched?
|
27
|
+
@punched
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module DuckPuncher
|
2
|
+
module ActiveRecord
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
def associations?
|
8
|
+
associations.present?
|
9
|
+
end
|
10
|
+
|
11
|
+
def associations
|
12
|
+
reflections.select { |key, _| send(key).present? rescue nil }.keys
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def except_for(*ids)
|
17
|
+
scoped.where("#{quoted_table_name}.id NOT IN (?)", ids)
|
18
|
+
end
|
19
|
+
|
20
|
+
def since(time)
|
21
|
+
scoped.where("#{quoted_table_name}.created_at > ?", time)
|
22
|
+
end
|
23
|
+
|
24
|
+
alias created_since since
|
25
|
+
|
26
|
+
def before(time)
|
27
|
+
scoped.where("#{quoted_table_name}.created_at < ?", time)
|
28
|
+
end
|
29
|
+
|
30
|
+
def updated_since(time)
|
31
|
+
scoped.where("#{quoted_table_name}.updated_at > ?", time)
|
32
|
+
end
|
33
|
+
|
34
|
+
def between(start_at, end_at)
|
35
|
+
scoped.where("#{quoted_table_name}.created_at BETWEEN ? AND ", start_at, end_at)
|
36
|
+
end
|
37
|
+
|
38
|
+
def latest
|
39
|
+
scoped.order("#{quoted_table_name}.id ASC").last
|
40
|
+
end
|
41
|
+
|
42
|
+
# shim for backwards compatibility with Rails 3
|
43
|
+
def scoped
|
44
|
+
where(nil)
|
45
|
+
end if Rails::VERSION::MAJOR != 3
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module DuckPuncher
|
2
|
+
module Ducks
|
3
|
+
module Array
|
4
|
+
def m(method_name)
|
5
|
+
map(&method_name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def mm(method_name, *args)
|
9
|
+
map { |x| x.public_send(method_name, *args) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(regex_or_str)
|
13
|
+
regex = regex_or_str.is_a?(Regexp) ? regex_or_str : Regexp.new(Regexp.escape(regex_or_str))
|
14
|
+
select { |x| x.to_s =~ regex }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module DuckPuncher
|
2
|
+
module Ducks
|
3
|
+
module Hash
|
4
|
+
# http://coryodaniel.com/index.php/2009/12/30/ruby-getting-deeply-nested-values-from-a-hash-in-one-line-of-code/
|
5
|
+
def seek(*_keys_)
|
6
|
+
last_level = self
|
7
|
+
sought_value = nil
|
8
|
+
|
9
|
+
_keys_.each_with_index do |_key_, _idx_|
|
10
|
+
if last_level.is_a?(Hash) && last_level.has_key?(_key_)
|
11
|
+
if _idx_ + 1 == _keys_.length
|
12
|
+
sought_value = last_level[_key_]
|
13
|
+
else
|
14
|
+
last_level = last_level[_key_]
|
15
|
+
end
|
16
|
+
else
|
17
|
+
break
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
sought_value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module DuckPuncher
|
2
|
+
module Ducks
|
3
|
+
module Method
|
4
|
+
def to_instruct
|
5
|
+
definition = Definition.new(self)
|
6
|
+
RubyVM::InstructionSequence.new(definition.lines.join).disasm if definition.lines.any?
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_source
|
10
|
+
Definition.new(self).to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
class Definition
|
14
|
+
def initialize(method_handle)
|
15
|
+
@file_path, @line_num = *method_handle.source_location
|
16
|
+
@line_num = @line_num.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
# @description finds the method's source code using the indent size of the file. This means we are
|
20
|
+
# restricted when it comes to parsing crappy formatted ruby files
|
21
|
+
def lines
|
22
|
+
return @lines if defined? @lines
|
23
|
+
return [] unless @file_path
|
24
|
+
@lines = []
|
25
|
+
File.open(@file_path) do |f|
|
26
|
+
found = false
|
27
|
+
i = 0
|
28
|
+
while line = f.gets and i += 1 and !found
|
29
|
+
next if i < @line_num
|
30
|
+
@lines << line
|
31
|
+
if @indent_size
|
32
|
+
found = @indent_size == find_indent_size(line)
|
33
|
+
else
|
34
|
+
@indent_size = find_indent_size(line)
|
35
|
+
found = line.end_with?("end\n")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
@lines
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
if lines.any?
|
44
|
+
lines.join.gsub /^\s{#{find_indent_size(lines.first)}}/, ''
|
45
|
+
else
|
46
|
+
''
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_indent_size(line)
|
51
|
+
line[/(\s*)/].size
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module DuckPuncher
|
2
|
+
module Ducks
|
3
|
+
module Numeric
|
4
|
+
def to_currency(prefix = '')
|
5
|
+
"#{prefix}%.2f" % self.round(2)
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_duration(seconds = false)
|
9
|
+
secs = to_i
|
10
|
+
mins = secs / 60
|
11
|
+
hours = mins / 60
|
12
|
+
buffer = ''
|
13
|
+
if hours > 0
|
14
|
+
num_mins = mins % 60
|
15
|
+
buffer << "#{hours} h"
|
16
|
+
buffer << " #{num_mins} min" unless num_mins.zero?
|
17
|
+
elsif mins > 0
|
18
|
+
num_secs = secs % 60
|
19
|
+
buffer << "#{mins} min"
|
20
|
+
buffer << " #{num_secs} s" if seconds && num_secs.nonzero?
|
21
|
+
elsif seconds && secs >= 0
|
22
|
+
buffer << "#{secs} s"
|
23
|
+
end
|
24
|
+
buffer
|
25
|
+
end
|
26
|
+
|
27
|
+
# similar to Rails' #time_ago_in_words
|
28
|
+
def to_time_ago
|
29
|
+
secs = to_i
|
30
|
+
mins = secs / 60
|
31
|
+
hours = mins / 60
|
32
|
+
days = hours / 24
|
33
|
+
buffer = ''
|
34
|
+
if days > 0
|
35
|
+
buffer << "#{days} #{'day'.pluralize(days)}"
|
36
|
+
elsif hours > 0
|
37
|
+
buffer << "#{hours} #{'hour'.pluralize(hours)}"
|
38
|
+
elsif mins > 0
|
39
|
+
buffer << "#{mins} #{'minute'.pluralize(mins)}"
|
40
|
+
elsif secs >= 0
|
41
|
+
buffer << "less than a minute"
|
42
|
+
end
|
43
|
+
buffer << ' ago'
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_rad
|
47
|
+
self / 180 * Math::PI
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module DuckPuncher
|
2
|
+
module Ducks
|
3
|
+
module Object
|
4
|
+
def clone!
|
5
|
+
Marshal.load Marshal.dump self
|
6
|
+
end unless defined? clone!
|
7
|
+
|
8
|
+
def require!(file_or_gem, version = '')
|
9
|
+
if DuckPuncher::GemInstaller.new.perform(file_or_gem, version)
|
10
|
+
require file_or_gem.tr('-', '/')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module DuckPuncher
|
2
|
+
module Ducks
|
3
|
+
module String
|
4
|
+
def pluralize(count)
|
5
|
+
"#{self}#{'s' if count != 1}"
|
6
|
+
end unless method_defined?(:pluralize)
|
7
|
+
|
8
|
+
def underscore
|
9
|
+
gsub(/\B([A-Z])([a-z_0-9])/, '_\1\2').gsub('::', '/').downcase
|
10
|
+
end unless method_defined?(:underscore)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module DuckPuncher
|
2
|
+
module Ducks
|
3
|
+
class << self
|
4
|
+
def list
|
5
|
+
@list ||= [
|
6
|
+
Duck.new(:Array),
|
7
|
+
Duck.new(:Numeric),
|
8
|
+
Duck.new(:Hash),
|
9
|
+
Duck.new(:String),
|
10
|
+
Duck.new(:Object),
|
11
|
+
Duck.new(:Method, before: -> { DuckPuncher::GemInstaller.initialize! }),
|
12
|
+
Duck.new(:ActiveRecord, class: 'ActiveRecord::Base', if: -> { defined? ::ActiveRecord })
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](name)
|
17
|
+
list.find { |duck| duck.name == name.to_sym }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Autoload our ducks
|
23
|
+
#
|
24
|
+
|
25
|
+
list.each do |duck|
|
26
|
+
autoload duck.name, duck.load_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative 'json_storage'
|
2
|
-
|
3
1
|
class DuckPuncher::GemInstaller
|
4
2
|
def self.initialize!
|
5
3
|
spec_data = DuckPuncher::JSONStorage.read('load_paths.json').values
|
@@ -19,7 +17,6 @@ class DuckPuncher::GemInstaller
|
|
19
17
|
# @param [String] name of the gem
|
20
18
|
# @param [String] version of the gem to install (e.g. '1.2.3')
|
21
19
|
def perform(*args)
|
22
|
-
require 'rubygems/dependency_installer'
|
23
20
|
installer = Gem::DependencyInstaller.new(install_dir: Bundler.bundle_path.to_s, bin_dir: RbConfig::CONFIG['bindir'])
|
24
21
|
installer.install *args.reject(&:empty?)
|
25
22
|
installer.installed_gems.each do |gem|
|
data/lib/duck_puncher/version.rb
CHANGED
data/lib/duck_puncher.rb
CHANGED
@@ -1,24 +1,43 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'fileutils'
|
3
|
+
require 'logger'
|
3
4
|
require 'duck_puncher/version'
|
4
5
|
|
5
6
|
module DuckPuncher
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :Object, 'duck_puncher/object'
|
11
|
-
autoload :Method, 'duck_puncher/method'
|
12
|
-
|
13
|
-
if defined? ActiveRecord
|
14
|
-
autoload :ActiveRecordExtensions, 'duck_puncher/active_record_extensions'
|
15
|
-
end
|
7
|
+
autoload :JSONStorage, 'duck_puncher/json_storage'
|
8
|
+
autoload :GemInstaller, 'duck_puncher/gem_installer'
|
9
|
+
autoload :Duck, 'duck_puncher/duck'
|
10
|
+
autoload :Ducks, 'duck_puncher/ducks'
|
16
11
|
|
17
12
|
def self.punch!(*names)
|
18
|
-
names.each
|
13
|
+
names.each do |name|
|
14
|
+
if duck = Ducks[name]
|
15
|
+
if duck.punched?
|
16
|
+
log.info %Q(Already punched #{name})
|
17
|
+
else
|
18
|
+
log.warn %Q(Punching the #{name} ducky)
|
19
|
+
unless duck.punch
|
20
|
+
log.error %Q(Failed to punch #{name}!)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
else
|
24
|
+
log.info %Q(Couldn't find "#{name}" in my list of Ducks! I know about: #{Ducks.list.map(&:name).map(&:to_s)})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
nil
|
19
28
|
end
|
20
29
|
|
21
30
|
def self.punch_all!
|
22
|
-
|
31
|
+
log.warn 'Punching all ducks! Watch out!'
|
32
|
+
Ducks.list.each &:punch
|
33
|
+
end
|
34
|
+
|
35
|
+
class << self
|
36
|
+
attr_accessor :log
|
37
|
+
end
|
38
|
+
|
39
|
+
self.log = Logger.new(STDOUT).tap do |config|
|
40
|
+
config.level = Logger::INFO
|
41
|
+
config.formatter = proc { |*args| "#{args.first}: #{args.last.to_s}\n" }
|
23
42
|
end
|
24
43
|
end
|
@@ -6,4 +6,13 @@ class StringTest < MiniTest::Test
|
|
6
6
|
assert_equal 'hour'.pluralize(0), 'hours'
|
7
7
|
assert_equal 'hour'.pluralize(2), 'hours'
|
8
8
|
end
|
9
|
+
|
10
|
+
def test_underscore
|
11
|
+
assert_equal 'MiniTest'.underscore, 'mini_test'
|
12
|
+
assert_equal 'MiniTestDoItToIt'.underscore, 'mini_test_do_it_to_it'
|
13
|
+
assert_equal 'MiniTest::Helper'.underscore, 'mini_test/helper'
|
14
|
+
assert_equal 'MiniTest::Helper::Expectations'.underscore, 'mini_test/helper/expectations'
|
15
|
+
assert_equal 'mini_test.rb', 'mini_test.rb'.underscore
|
16
|
+
assert_equal 'duck_puncher/json_storage', 'DuckPuncher::JSONStorage'.underscore
|
17
|
+
end
|
9
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: duck_puncher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Buckley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -97,15 +97,17 @@ files:
|
|
97
97
|
- bin/console
|
98
98
|
- duck_puncher.gemspec
|
99
99
|
- lib/duck_puncher.rb
|
100
|
-
- lib/duck_puncher/
|
101
|
-
- lib/duck_puncher/
|
100
|
+
- lib/duck_puncher/duck.rb
|
101
|
+
- lib/duck_puncher/ducks.rb
|
102
|
+
- lib/duck_puncher/ducks/active_record.rb
|
103
|
+
- lib/duck_puncher/ducks/array.rb
|
104
|
+
- lib/duck_puncher/ducks/hash.rb
|
105
|
+
- lib/duck_puncher/ducks/method.rb
|
106
|
+
- lib/duck_puncher/ducks/numeric.rb
|
107
|
+
- lib/duck_puncher/ducks/object.rb
|
108
|
+
- lib/duck_puncher/ducks/string.rb
|
102
109
|
- lib/duck_puncher/gem_installer.rb
|
103
|
-
- lib/duck_puncher/hash.rb
|
104
110
|
- lib/duck_puncher/json_storage.rb
|
105
|
-
- lib/duck_puncher/method.rb
|
106
|
-
- lib/duck_puncher/numeric.rb
|
107
|
-
- lib/duck_puncher/object.rb
|
108
|
-
- lib/duck_puncher/string.rb
|
109
111
|
- lib/duck_puncher/version.rb
|
110
112
|
- test/duck_puncher/array_test.rb
|
111
113
|
- test/duck_puncher/hash_test.rb
|
@@ -1,48 +0,0 @@
|
|
1
|
-
module ActiveRecordExtensions
|
2
|
-
def self.included(base)
|
3
|
-
base.extend(ClassMethods)
|
4
|
-
end
|
5
|
-
|
6
|
-
def associations?
|
7
|
-
associations.present?
|
8
|
-
end
|
9
|
-
|
10
|
-
def associations
|
11
|
-
reflections.select { |key, _| send(key).present? rescue nil }.keys
|
12
|
-
end
|
13
|
-
|
14
|
-
module ClassMethods
|
15
|
-
def except_for(*ids)
|
16
|
-
scoped.where("#{quoted_table_name}.id NOT IN (?)", ids)
|
17
|
-
end
|
18
|
-
|
19
|
-
def since(time)
|
20
|
-
scoped.where("#{quoted_table_name}.created_at > ?", time)
|
21
|
-
end
|
22
|
-
|
23
|
-
alias created_since since
|
24
|
-
|
25
|
-
def before(time)
|
26
|
-
scoped.where("#{quoted_table_name}.created_at < ?", time)
|
27
|
-
end
|
28
|
-
|
29
|
-
def updated_since(time)
|
30
|
-
scoped.where("#{quoted_table_name}.updated_at > ?", time)
|
31
|
-
end
|
32
|
-
|
33
|
-
def between(start_at, end_at)
|
34
|
-
scoped.where("#{quoted_table_name}.created_at BETWEEN ? AND ", start_at, end_at)
|
35
|
-
end
|
36
|
-
|
37
|
-
def latest
|
38
|
-
scoped.order("#{quoted_table_name}.id ASC").last
|
39
|
-
end
|
40
|
-
|
41
|
-
# shim for backwards compatibility with Rails 3
|
42
|
-
def scoped
|
43
|
-
where(nil)
|
44
|
-
end if Rails::VERSION::MAJOR != 3
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
ActiveRecord::Base.send(:include, ActiveRecordExtensions)
|
data/lib/duck_puncher/array.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
module DuckPuncher
|
2
|
-
module Array
|
3
|
-
def m(method_name)
|
4
|
-
map(&method_name)
|
5
|
-
end
|
6
|
-
|
7
|
-
def mm(method_name, *args)
|
8
|
-
map { |x| x.public_send(method_name, *args) }
|
9
|
-
end
|
10
|
-
|
11
|
-
def get(regex_or_str)
|
12
|
-
regex = regex_or_str.is_a?(Regexp) ? regex_or_str : Regexp.new(Regexp.escape(regex_or_str))
|
13
|
-
select { |x| x.to_s =~ regex }
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
Array.send(:include, DuckPuncher::Array)
|
data/lib/duck_puncher/hash.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module DuckPuncher
|
2
|
-
module Hash
|
3
|
-
# http://coryodaniel.com/index.php/2009/12/30/ruby-getting-deeply-nested-values-from-a-hash-in-one-line-of-code/
|
4
|
-
def seek(*_keys_)
|
5
|
-
last_level = self
|
6
|
-
sought_value = nil
|
7
|
-
|
8
|
-
_keys_.each_with_index do |_key_, _idx_|
|
9
|
-
if last_level.is_a?(Hash) && last_level.has_key?(_key_)
|
10
|
-
if _idx_ + 1 == _keys_.length
|
11
|
-
sought_value = last_level[_key_]
|
12
|
-
else
|
13
|
-
last_level = last_level[_key_]
|
14
|
-
end
|
15
|
-
else
|
16
|
-
break
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
sought_value
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
Hash.send(:include, DuckPuncher::Hash)
|
data/lib/duck_puncher/method.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
module DuckPuncher
|
2
|
-
module Method
|
3
|
-
def to_instruct
|
4
|
-
definition = Definition.new(self)
|
5
|
-
RubyVM::InstructionSequence.new(definition.lines.join).disasm if definition.lines.any?
|
6
|
-
end
|
7
|
-
|
8
|
-
def to_source
|
9
|
-
Definition.new(self).to_s
|
10
|
-
end
|
11
|
-
|
12
|
-
class Definition
|
13
|
-
def initialize(method_handle)
|
14
|
-
@file_path, @line_num = *method_handle.source_location
|
15
|
-
@line_num = @line_num.to_i
|
16
|
-
end
|
17
|
-
|
18
|
-
# @description finds the method's source code using the indent size of the file. This means we are
|
19
|
-
# restricted when it comes to parsing crappy formatted ruby files
|
20
|
-
def lines
|
21
|
-
return @lines if defined? @lines
|
22
|
-
return [] unless @file_path
|
23
|
-
@lines = []
|
24
|
-
File.open(@file_path) do |f|
|
25
|
-
found = false
|
26
|
-
i = 0
|
27
|
-
while line = f.gets and i += 1 and !found
|
28
|
-
next if i < @line_num
|
29
|
-
@lines << line
|
30
|
-
if @indent_size
|
31
|
-
found = @indent_size == find_indent_size(line)
|
32
|
-
else
|
33
|
-
@indent_size = find_indent_size(line)
|
34
|
-
found = line.end_with?("end\n")
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
@lines
|
39
|
-
end
|
40
|
-
|
41
|
-
def to_s
|
42
|
-
if lines.any?
|
43
|
-
lines.join.gsub /^\s{#{find_indent_size(lines.first)}}/, ''
|
44
|
-
else
|
45
|
-
''
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def find_indent_size(line)
|
50
|
-
line[/(\s*)/].size
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
Method.send(:include, DuckPuncher::Method)
|
data/lib/duck_puncher/numeric.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
module DuckPuncher
|
2
|
-
module Numeric
|
3
|
-
def to_currency(prefix = '')
|
4
|
-
"#{prefix}%.2f" % self.round(2)
|
5
|
-
end
|
6
|
-
|
7
|
-
def to_duration(seconds = false)
|
8
|
-
secs = to_i
|
9
|
-
mins = secs / 60
|
10
|
-
hours = mins / 60
|
11
|
-
buffer = ''
|
12
|
-
if hours > 0
|
13
|
-
num_mins = mins % 60
|
14
|
-
buffer << "#{hours} h"
|
15
|
-
buffer << " #{num_mins} min" unless num_mins.zero?
|
16
|
-
elsif mins > 0
|
17
|
-
num_secs = secs % 60
|
18
|
-
buffer << "#{mins} min"
|
19
|
-
buffer << " #{num_secs} s" if seconds && num_secs.nonzero?
|
20
|
-
elsif seconds && secs >= 0
|
21
|
-
buffer << "#{secs} s"
|
22
|
-
end
|
23
|
-
buffer
|
24
|
-
end
|
25
|
-
|
26
|
-
# similar to Rails' #time_ago_in_words
|
27
|
-
def to_time_ago
|
28
|
-
secs = to_i
|
29
|
-
mins = secs / 60
|
30
|
-
hours = mins / 60
|
31
|
-
days = hours / 24
|
32
|
-
buffer = ''
|
33
|
-
if days > 0
|
34
|
-
buffer << "#{days} #{'day'.pluralize(days)}"
|
35
|
-
elsif hours > 0
|
36
|
-
buffer << "#{hours} #{'hour'.pluralize(hours)}"
|
37
|
-
elsif mins > 0
|
38
|
-
buffer << "#{mins} #{'minute'.pluralize(mins)}"
|
39
|
-
elsif secs >= 0
|
40
|
-
buffer << "less than a minute"
|
41
|
-
end
|
42
|
-
buffer << ' ago'
|
43
|
-
end
|
44
|
-
|
45
|
-
def to_rad
|
46
|
-
self / 180 * Math::PI
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
Numeric.send(:include, DuckPuncher::Numeric)
|
data/lib/duck_puncher/object.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require_relative 'json_storage'
|
2
|
-
require_relative 'gem_installer'
|
3
|
-
|
4
|
-
module DuckPuncher
|
5
|
-
module Object
|
6
|
-
def clone!
|
7
|
-
Marshal.load Marshal.dump self
|
8
|
-
end unless defined? clone!
|
9
|
-
|
10
|
-
def require!(file_or_gem, version = '')
|
11
|
-
if DuckPuncher::GemInstaller.new.perform(file_or_gem, version)
|
12
|
-
require file_or_gem.tr('-', '/')
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
DuckPuncher::GemInstaller.initialize!
|
19
|
-
Object.send(:include, DuckPuncher::Object)
|