unpatched 0.0.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.
- data/License +20 -0
- data/README.md +62 -0
- data/Rakefile +37 -0
- data/lib/unpatched.rb +168 -0
- data/lib/unpatched/version.rb +42 -0
- data/spec/unpatched_spec.rb +14 -0
- data/unpatched.gemspec +33 -0
- metadata +65 -0
data/License
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Konstantin Haase
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
Convenience library, like ActiveSupport or extlib, but without a single
|
2
|
+
monkey-patch. It is also an API experiment.
|
3
|
+
|
4
|
+
# Usage
|
5
|
+
|
6
|
+
Stand alone:
|
7
|
+
|
8
|
+
``` ruby
|
9
|
+
require 'unpatched'
|
10
|
+
inlcude Unpatched
|
11
|
+
|
12
|
+
like("FooBar").but.underscore!
|
13
|
+
about(1).month.and(4).days.ago!
|
14
|
+
```
|
15
|
+
|
16
|
+
Is that too much sugar? Try this:
|
17
|
+
|
18
|
+
``` ruby
|
19
|
+
require 'unpatched'
|
20
|
+
include Unpatched::Unfancy
|
21
|
+
_('FooBar').underscore!
|
22
|
+
```
|
23
|
+
|
24
|
+
Still too much?
|
25
|
+
|
26
|
+
``` ruby
|
27
|
+
require 'unpatched'
|
28
|
+
Unpatched['FooBar'].underscore!
|
29
|
+
```
|
30
|
+
|
31
|
+
With Sinatra:
|
32
|
+
|
33
|
+
``` ruby
|
34
|
+
require 'sinatra'
|
35
|
+
require 'unpatched'
|
36
|
+
helpers Unpatched
|
37
|
+
|
38
|
+
get '/' do
|
39
|
+
expires exactly(1.5).minutes.from_now!
|
40
|
+
"Hey, ho, let's go!"
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
# When to use an exclamation mark
|
45
|
+
|
46
|
+
Short: You don't have to. But methods without one will always return magically
|
47
|
+
enhanced objects. Since you never want to pass those on (otherwise, what's the
|
48
|
+
point?), you can get vanilla objects by using an exclamation mark.
|
49
|
+
|
50
|
+
Rule of thumb: Use it at the end of your method chain.
|
51
|
+
|
52
|
+
# Installation
|
53
|
+
|
54
|
+
gem install unpatched
|
55
|
+
|
56
|
+
# TODO
|
57
|
+
|
58
|
+
* Write code and documentation
|
59
|
+
* Fix project description in gemspec
|
60
|
+
* Change testing framework if necessary (see Rakefile, currently RSpec)
|
61
|
+
* Adjust unpatched.gemspec if your github name is not rkh
|
62
|
+
* Adjust License if your real name is not Konstantin Haase
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler'
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
rescue LoadError => e
|
7
|
+
$stderr.puts e
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "run specs"
|
11
|
+
task(:spec) { ruby '-S rspec spec' }
|
12
|
+
|
13
|
+
desc "generate gemspec"
|
14
|
+
task 'unpatched.gemspec' do
|
15
|
+
require 'unpatched/version'
|
16
|
+
content = File.read 'unpatched.gemspec'
|
17
|
+
|
18
|
+
fields = {
|
19
|
+
:authors => `git shortlog -sn`.scan(/[^\d\s].*/),
|
20
|
+
:email => `git shortlog -sne`.scan(/[^<]+@[^>]+/),
|
21
|
+
:files => `git ls-files`.split("\n").reject { |f| f =~ /^(\.|Gemfile)/ }
|
22
|
+
}
|
23
|
+
|
24
|
+
fields.each do |field, values|
|
25
|
+
updated = " s.#{field} = ["
|
26
|
+
updated << values.map { |v| "\n %p" % v }.join(',')
|
27
|
+
updated << "\n ]"
|
28
|
+
content.sub!(/ s\.#{field} = \[\n( .*\n)* \]/, updated)
|
29
|
+
end
|
30
|
+
|
31
|
+
content.sub! /(s\.version.*=\s+).*/, "\\1\"#{Unpatched::VERSION}\""
|
32
|
+
File.open('unpatched.gemspec', 'w') { |f| f << content }
|
33
|
+
end
|
34
|
+
|
35
|
+
task :gemspec => 'unpatched.gemspec'
|
36
|
+
task :default => :spec
|
37
|
+
task :test => :spec
|
data/lib/unpatched.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'unpatched/version'
|
2
|
+
|
3
|
+
module Unpatched
|
4
|
+
MINUTE = 60
|
5
|
+
HOUR = 60 * MINUTE
|
6
|
+
DAY = 24 * HOUR
|
7
|
+
MONTH = 30 * DAY
|
8
|
+
YEAR = 365 * DAY
|
9
|
+
|
10
|
+
module Utils
|
11
|
+
extend self
|
12
|
+
|
13
|
+
def wrapped(value)
|
14
|
+
NormalWrapper.new(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def unwrapped(value)
|
18
|
+
value = value.__instance_eval__ { @value } while Wrapper === value
|
19
|
+
value
|
20
|
+
end
|
21
|
+
|
22
|
+
def numeric(value)
|
23
|
+
case value
|
24
|
+
when Numeric then value
|
25
|
+
when Wrapper then numeric unwrapped(value)
|
26
|
+
else
|
27
|
+
begin
|
28
|
+
Integer value
|
29
|
+
rescue ArgumentError
|
30
|
+
Float value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
alias num numeric
|
36
|
+
end
|
37
|
+
|
38
|
+
class Wrapper < defined?(BasicObject) ? BasicObject : Object
|
39
|
+
def self.const_missing(const) ::Unpatched.const_get(const) end
|
40
|
+
alias __instance_eval__ instance_eval
|
41
|
+
|
42
|
+
if superclass == Object
|
43
|
+
instance_methods.each do |m|
|
44
|
+
undef_method(m) if m.to_s !~ /(?:^__|^send$|^object_id$)/
|
45
|
+
end
|
46
|
+
else
|
47
|
+
undef ==
|
48
|
+
undef equal?
|
49
|
+
undef instance_eval
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(value)
|
53
|
+
@value = Utils.unwrapped(value)
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
@value.inspect
|
58
|
+
end
|
59
|
+
|
60
|
+
def and(value = (undefined = true))
|
61
|
+
return self if undefined
|
62
|
+
if Wrapper === value
|
63
|
+
Utils.wrapped @value + unwrapped(value)
|
64
|
+
else
|
65
|
+
MultiWrapper.new self, value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
alias_method :but, :and
|
70
|
+
end
|
71
|
+
|
72
|
+
class MultiWrapper < Wrapper
|
73
|
+
def initialize(base, value)
|
74
|
+
@base, @value = base, Utils.wrapped(value)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def method_missing(*a, &b)
|
79
|
+
@base + @value.__send__(*a, &b)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class NormalWrapper < Wrapper
|
84
|
+
def second
|
85
|
+
value = Utils.unwrapped @value
|
86
|
+
if value.respond_to? :second
|
87
|
+
value.second
|
88
|
+
else
|
89
|
+
Utils.wrapped Utils.num(value)
|
90
|
+
end
|
91
|
+
rescue TypeError, ArgumentError => err
|
92
|
+
raise err unless value.respond_to? :[]
|
93
|
+
value[1]
|
94
|
+
end
|
95
|
+
|
96
|
+
def underscore
|
97
|
+
to_s.
|
98
|
+
gsub(/::/, '/').
|
99
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
100
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
101
|
+
tr("-", "_").
|
102
|
+
downcase
|
103
|
+
end
|
104
|
+
|
105
|
+
def seconds; Utils.wrapped Utils.num(@value) end
|
106
|
+
def minute; Utils.wrapped Utils.num(@value) * MINUTE end
|
107
|
+
def hour; Utils.wrapped Utils.num(@value) * HOUR end
|
108
|
+
def day; Utils.wrapped Utils.num(@value) * DAY end
|
109
|
+
def month; Utils.wrapped Utils.num(@value) * MONTH end
|
110
|
+
def year; Utils.wrapped Utils.num(@value) * YEAR end
|
111
|
+
|
112
|
+
def before(other)
|
113
|
+
Utils.wrapped Utils.unwrapped(other) - @value
|
114
|
+
end
|
115
|
+
|
116
|
+
def after(other)
|
117
|
+
Utils.wrapped Utils.unwrapped(other) + @value
|
118
|
+
end
|
119
|
+
|
120
|
+
def ago
|
121
|
+
before Time.now
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
def from_now
|
126
|
+
after Time.now
|
127
|
+
end
|
128
|
+
|
129
|
+
alias minutes minute
|
130
|
+
alias hours hour
|
131
|
+
alias days day
|
132
|
+
alias months month
|
133
|
+
alias years year
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def method_missing(m, *a, &b)
|
138
|
+
a.map! { |e| Utils.unwrapped e }
|
139
|
+
if m[-1] == ?! and NormalWrapper.method_defined?(method = m[0..-2])
|
140
|
+
result = __send__(method, *a, &b)
|
141
|
+
Utils.unwrapped result
|
142
|
+
else
|
143
|
+
Utils.wrapped @value.__send__(m, *a, &b)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
module Unfancy
|
149
|
+
private
|
150
|
+
def _(value)
|
151
|
+
Utils.wrapped(value)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
module Fancy
|
156
|
+
include Unfancy
|
157
|
+
%w[about exactly like is some].each do |meth|
|
158
|
+
alias_method meth, :_
|
159
|
+
private meth
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.[](value)
|
164
|
+
Utils.wrapped(value)
|
165
|
+
end
|
166
|
+
|
167
|
+
include Fancy
|
168
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Unpatched
|
2
|
+
def self.version
|
3
|
+
VERSION
|
4
|
+
end
|
5
|
+
|
6
|
+
module VERSION
|
7
|
+
extend Comparable
|
8
|
+
|
9
|
+
MAJOR = 0
|
10
|
+
MINOR = 0
|
11
|
+
TINY = 1
|
12
|
+
SIGNATURE = [MAJOR, MINOR, TINY]
|
13
|
+
STRING = SIGNATURE.join '.'
|
14
|
+
|
15
|
+
def self.major; MAJOR end
|
16
|
+
def self.minor; MINOR end
|
17
|
+
def self.tiny; TINY end
|
18
|
+
def self.to_s; STRING end
|
19
|
+
|
20
|
+
def self.hash
|
21
|
+
STRING.hash
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.<=>(other)
|
25
|
+
other = other.split('.').map { |i| i.to_i } if other.respond_to? :split
|
26
|
+
SIGNATURE <=> Array(other)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.inspect
|
30
|
+
STRING.inspect
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.respond_to?(meth, *)
|
34
|
+
meth.to_s !~ /^__|^to_str$/ and STRING.respond_to? meth unless super
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.method_missing(meth, *args, &block)
|
38
|
+
return super unless STRING.respond_to?(meth)
|
39
|
+
STRING.send(meth, *args, &block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'unpatched'
|
2
|
+
|
3
|
+
describe Unpatched do
|
4
|
+
before { extend Unpatched }
|
5
|
+
it 'should not fail me' do
|
6
|
+
exactly(2).seconds.should == 2
|
7
|
+
exactly(2).minutes.should == 120
|
8
|
+
about(10).minutes.and(1).second.should == 601
|
9
|
+
_('FooBar').underscore.should == 'foo_bar'
|
10
|
+
about(10).minutes.ago!.should be_a(Time)
|
11
|
+
about(10).minutes.from_now!.should be_a(Time)
|
12
|
+
exactly(10).before(12).should == 2
|
13
|
+
end
|
14
|
+
end
|
data/unpatched.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Run `rake unpatched.gemspec` to update the gemspec.
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
# general infos
|
4
|
+
s.name = "unpatched"
|
5
|
+
s.version = "0.0.1"
|
6
|
+
s.description = "Convenience library without a single monkey-patch."
|
7
|
+
s.homepage = "http://github.com/rkh/unpatched"
|
8
|
+
s.summary = s.description
|
9
|
+
|
10
|
+
# generated from git shortlog -sn
|
11
|
+
s.authors = [
|
12
|
+
"Konstantin Haase"
|
13
|
+
]
|
14
|
+
|
15
|
+
# generated from git shortlog -sne
|
16
|
+
s.email = [
|
17
|
+
"konstantin.mailinglists@googlemail.com"
|
18
|
+
]
|
19
|
+
|
20
|
+
# generated from git ls-files
|
21
|
+
s.files = [
|
22
|
+
"License",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"lib/unpatched.rb",
|
26
|
+
"lib/unpatched/version.rb",
|
27
|
+
"spec/unpatched_spec.rb",
|
28
|
+
"unpatched.gemspec"
|
29
|
+
]
|
30
|
+
|
31
|
+
# dependencies
|
32
|
+
s.add_development_dependency "rspec", "~> 2.0"
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unpatched
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Konstantin Haase
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-05-31 00:00:00.000000000 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
requirement: &2153180300 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2.0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2153180300
|
26
|
+
description: Convenience library without a single monkey-patch.
|
27
|
+
email:
|
28
|
+
- konstantin.mailinglists@googlemail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- License
|
34
|
+
- README.md
|
35
|
+
- Rakefile
|
36
|
+
- lib/unpatched.rb
|
37
|
+
- lib/unpatched/version.rb
|
38
|
+
- spec/unpatched_spec.rb
|
39
|
+
- unpatched.gemspec
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/rkh/unpatched
|
42
|
+
licenses: []
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.6.2
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: Convenience library without a single monkey-patch.
|
65
|
+
test_files: []
|