darkext 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/History.txt +140 -0
- data/LICENSE +21 -0
- data/README.md +45 -0
- data/Rakefile +45 -0
- data/TODO +4 -0
- data/VERSION.yml +4 -0
- data/app_generators/sinatra_app_generator.rb +57 -0
- data/app_generators/templates/app.rb +16 -0
- data/app_generators/templates/config.ru +2 -0
- data/app_generators/templates/error.rb +8 -0
- data/app_generators/templates/gitignore +0 -0
- data/app_generators/templates/helpers.rb +12 -0
- data/app_generators/templates/http_method.rb +8 -0
- data/app_generators/templates/options.rb +9 -0
- data/bin/sinatra-app +14 -0
- data/darkext.gemspec +105 -0
- data/lib/darkext.rb +13 -0
- data/lib/darkext/array.rb +52 -0
- data/lib/darkext/beagle.rb +88 -0
- data/lib/darkext/boolean.rb +17 -0
- data/lib/darkext/fiber.rb +48 -0
- data/lib/darkext/float.rb +6 -0
- data/lib/darkext/hash.rb +38 -0
- data/lib/darkext/integer.rb +10 -0
- data/lib/darkext/io.rb +37 -0
- data/lib/darkext/net.rb +28 -0
- data/lib/darkext/numeric.rb +35 -0
- data/lib/darkext/object.rb +11 -0
- data/lib/darkext/sinatra.rb +70 -0
- data/lib/darkext/sitemap_generator.rb +90 -0
- data/lib/darkext/statistics.rb +197 -0
- data/lib/darkext/string.rb +63 -0
- data/lib/darkext/symbol.rb +7 -0
- data/spec/array_spec.rb +112 -0
- data/spec/beagle_spec.rb +42 -0
- data/spec/boolean_spec.rb +53 -0
- data/spec/fiber_spec.rb +35 -0
- data/spec/float_spec.rb +18 -0
- data/spec/hash_spec.rb +26 -0
- data/spec/integer_spec.rb +22 -0
- data/spec/io_spec.rb +44 -0
- data/spec/net_spec.rb +23 -0
- data/spec/numeric_spec.rb +52 -0
- data/spec/object_spec.rb +28 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/statistics_spec.rb +162 -0
- data/spec/string_spec.rb +54 -0
- data/spec/symbol_spec.rb +16 -0
- metadata +119 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'builder'
|
4
|
+
require 'darkext/hash'
|
5
|
+
|
6
|
+
module Darkext
|
7
|
+
class Darkext::SitemapBuilder
|
8
|
+
FORUM_CLASS_DEFAULT = 'forumtitle'
|
9
|
+
TOPIC_CLASS_DEFAULT = 'topictitle'
|
10
|
+
FORUM_PRIORITY_DEFAULT = 0.8
|
11
|
+
TOPIC_PRIORITY_DEFAULT = 0.5
|
12
|
+
PAGE_PRIORITY_DEFAULT = 0.1
|
13
|
+
FORUM_CHANGE_FREQ_DEFAULT = 'daily'
|
14
|
+
TOPIC_CHANGE_FREQ_DEFAULT = 'hourly'
|
15
|
+
PAGE_CHANGE_FREQ_DEFAULT = 'monthly'
|
16
|
+
|
17
|
+
def initialize(opts={})
|
18
|
+
defaults = {
|
19
|
+
:forum => {
|
20
|
+
:class => FORUM_CLASS_DEFAULT,
|
21
|
+
:priority => FORUM_PRIORITY_DEFAULT,
|
22
|
+
:change_freq => FORUM_CHANGE_FREQ_DEFAULT
|
23
|
+
},
|
24
|
+
:topic => {
|
25
|
+
:class => TOPIC_CLASS_DEFAULT,
|
26
|
+
:priority => TOPIC_PRIORITY_DEFAULT,
|
27
|
+
:change_freq => TOPIC_CHANGE_FREQ_DEFAULT
|
28
|
+
},
|
29
|
+
:page => {
|
30
|
+
:priority => PAGE_PRIORITY_DEFAULT,
|
31
|
+
:change_freq => PAGE_CHANGE_FREQ_DEFAULT
|
32
|
+
}
|
33
|
+
}
|
34
|
+
defaults.deep_merge!(opts)
|
35
|
+
|
36
|
+
@options = defaults
|
37
|
+
|
38
|
+
@forums = Array.new
|
39
|
+
@topics = Array.new
|
40
|
+
@pages = Array.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def index(url, target = $stdout)
|
44
|
+
@pages << url
|
45
|
+
|
46
|
+
root = Hpricot(open(url))
|
47
|
+
(root/"a.#{@options.nested_find(:forum,:class)}").each do |link|
|
48
|
+
index_forum(link.attributes['href'].to_s)
|
49
|
+
end
|
50
|
+
|
51
|
+
xml = Builder::XmlMarkup.new(:target => $stdout, :indent => 1)
|
52
|
+
xml.instruct!
|
53
|
+
xml.urlset "xmlns" => "http://www.sitemaps.org/schemas/sitemap/0.9" do
|
54
|
+
@pages.each do |page|
|
55
|
+
xml.url do
|
56
|
+
xml.loc(page)
|
57
|
+
xml.changefreq(@options.nested_find(:page,:change_freq))
|
58
|
+
xml.priority(@options.nested_find(:page,:priority))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
@forums.each do |forum|
|
62
|
+
xml.url do
|
63
|
+
xml.loc(forum)
|
64
|
+
xml.changefreq(@options.nested_find(:forum,:change_freq))
|
65
|
+
xml.priority(@options.nested_find(:forum,:priority))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
@topics.each do |topic|
|
69
|
+
xml.url do
|
70
|
+
xml.loc(topic)
|
71
|
+
xml.changefreq(@options.nested_find(:topic,:change_freq))
|
72
|
+
xml.priority(@options.nested_find(:topic,:priority))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def index_forum(href)
|
80
|
+
root = Hpricot(open(href))
|
81
|
+
(root/"a.#{@options.nested_find(:forum,:class)}").each do |link|
|
82
|
+
index_forum(link.attributes['href'].to_s)
|
83
|
+
end
|
84
|
+
(root/"a.#{@options.nested_find(:topic,:class)}").each do |link|
|
85
|
+
@topics << link.attributes['href'].to_s
|
86
|
+
end
|
87
|
+
@forums << href
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'mathn'
|
2
|
+
|
3
|
+
require 'darkext/array'
|
4
|
+
require 'darkext/numeric'
|
5
|
+
require 'darkext/symbol'
|
6
|
+
|
7
|
+
class Array
|
8
|
+
# Finds the mean of the array
|
9
|
+
def mean
|
10
|
+
raise ArgumentError.new('Array size must be > 0') if self.size.zero?
|
11
|
+
self.sum / self.size.to_f
|
12
|
+
end
|
13
|
+
alias :average :mean
|
14
|
+
alias :ave :mean
|
15
|
+
|
16
|
+
def harmonic_mean
|
17
|
+
raise ArgumentError.new('Array size must be > 0') if self.size.zero?
|
18
|
+
self.size.to_f / self.map { |i| 1 / i.to_f }.sum
|
19
|
+
end
|
20
|
+
alias :h_mean :harmonic_mean
|
21
|
+
|
22
|
+
def geometric_mean
|
23
|
+
raise ArgumentError.new('Array size must be > 0') if self.size.zero?
|
24
|
+
self.product.root(self.size)
|
25
|
+
end
|
26
|
+
alias :g_mean :geometric_mean
|
27
|
+
|
28
|
+
# Finds the median of the array
|
29
|
+
def median
|
30
|
+
raise ArgumentError.new('Array size must be > 0') if self.size.zero?
|
31
|
+
case self.size % 2
|
32
|
+
when 0
|
33
|
+
return self.sort[(self.size / 2) - 1, 2].mean
|
34
|
+
when 1
|
35
|
+
return self.sort[self.size / 2]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Generates a histogram hash for the array
|
40
|
+
def histogram
|
41
|
+
self.sort.inject({}) do |a,x|
|
42
|
+
a[x] = a[x].to_i + 1
|
43
|
+
a
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Finds the mode of the array
|
48
|
+
def mode
|
49
|
+
raise ArgumentError.new('Array size must be > 0') if self.size.zero?
|
50
|
+
map = self.histogram
|
51
|
+
max = map.values.max
|
52
|
+
map.keys.select { |x| map[x] == max }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Variance
|
56
|
+
def variance
|
57
|
+
raise ArgumentError.new('Array size must be > 0') if self.size.zero?
|
58
|
+
self.sum_of_squares.to_f / (self.size).to_f
|
59
|
+
end
|
60
|
+
|
61
|
+
# Standard deviation
|
62
|
+
def standard_deviation
|
63
|
+
raise ArgumentError.new('Array size must be > 0') if self.size.zero?
|
64
|
+
self.variance.abs.sqrt
|
65
|
+
end
|
66
|
+
alias :stddev :standard_deviation
|
67
|
+
|
68
|
+
# Randomly samples n elements
|
69
|
+
def sample(n = 1)
|
70
|
+
(1..n).collect { self[rand(self.size)] }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Generates a confidence interval
|
74
|
+
def ci(opts = { })
|
75
|
+
raise ArgumentError.new('Array size must be > 0') if self.size.zero?
|
76
|
+
opts = { :percent => 0.95, :rho => 1, :type => :center }.merge(opts)
|
77
|
+
percent = opts[:percent]
|
78
|
+
rho = opts[:rho]
|
79
|
+
m = self.mean
|
80
|
+
ret = Array.new
|
81
|
+
div = (opts[:type] == :center ? 2 : 1)
|
82
|
+
i = ((Darkext::Statistics::zscore((1 - percent) / div) * rho) /
|
83
|
+
self.size.sqrt).abs
|
84
|
+
case opts[:type]
|
85
|
+
when :center
|
86
|
+
ret << m - i
|
87
|
+
ret << m + i
|
88
|
+
when :upper
|
89
|
+
ret << m + i
|
90
|
+
when :lower
|
91
|
+
ret << m - i
|
92
|
+
end
|
93
|
+
return ret
|
94
|
+
end
|
95
|
+
|
96
|
+
# Standardizes the array
|
97
|
+
def standardize
|
98
|
+
self.clone.standardize!
|
99
|
+
end
|
100
|
+
|
101
|
+
# Destructive standardize
|
102
|
+
def standardize!
|
103
|
+
m = self.mean.to_f
|
104
|
+
rho = self.standard_deviation.to_f
|
105
|
+
self.map! { |v| (v.to_f - m) / rho }
|
106
|
+
end
|
107
|
+
|
108
|
+
def sum_of_squares
|
109
|
+
raise ArgumentError.new('Array size must be > 0') if self.size.zero?
|
110
|
+
m = self.mean
|
111
|
+
self.map { |v| v - m }.squares.sum
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
module Darkext
|
116
|
+
module Darkext::Statistics
|
117
|
+
# Finds the probability of a z-score
|
118
|
+
def self.prob(z)
|
119
|
+
p = Math::erf(z.abs/2.sqrt) / 2
|
120
|
+
return 0.5 + p if 0 < z
|
121
|
+
return 0.5 - p
|
122
|
+
end
|
123
|
+
|
124
|
+
# Finds the zscore of a probability
|
125
|
+
def self.zscore(p, epsilon = 0.00000000000001)
|
126
|
+
return -1 if (1 < p || 0 > p)
|
127
|
+
minz, maxz = -6, 6
|
128
|
+
zscore = 0.5
|
129
|
+
prob = 0
|
130
|
+
while (maxz - minz) > epsilon
|
131
|
+
prob = prob(zscore)
|
132
|
+
prob > p ? maxz = zscore : minz = zscore
|
133
|
+
zscore = (maxz + minz) * 0.5
|
134
|
+
end
|
135
|
+
return zscore
|
136
|
+
end
|
137
|
+
|
138
|
+
# Finds a two tail p-val for a high/low array
|
139
|
+
# can't remember how to use this
|
140
|
+
=begin
|
141
|
+
def self.p_val(r, n = 30, rho = 1, mu = r.mean)
|
142
|
+
probs = r.map do |x|
|
143
|
+
(x - mu) / (rho / n.sqrt)
|
144
|
+
end.map do |x|
|
145
|
+
Statistics::prob(x)
|
146
|
+
end
|
147
|
+
return 1 - (probs[1] - probs[0])
|
148
|
+
end
|
149
|
+
=end
|
150
|
+
|
151
|
+
module Darkext::Statistics::Regression
|
152
|
+
# Do a least squares linear regression on the two sets of x's and y's
|
153
|
+
# Returns a hash containing many relevant values
|
154
|
+
# * n (:n)
|
155
|
+
# * B_1 (:b_1)
|
156
|
+
# * B_0 (:b_0)
|
157
|
+
# * predicted values (:predicted)
|
158
|
+
# * residuals (:residuals)
|
159
|
+
# * SSE (:ss_e)
|
160
|
+
# * SST (:ss_t)
|
161
|
+
# * R^2 (:r_2)
|
162
|
+
# * R (:r)
|
163
|
+
# * unbiased estimator (:estimator)
|
164
|
+
# * the equation as a lambda (:equation)
|
165
|
+
# Raises an argument error if the arguments are not the same size or either is zero
|
166
|
+
def self.least_squares(xs,ys)
|
167
|
+
raise ArgumentError.new('Arrays must have size > 0') if xs.size.zero? || ys.size.zero?
|
168
|
+
raise ArgumentError.new('Arrays must be of equal size') if xs.size != ys.size
|
169
|
+
n = xs.size
|
170
|
+
b_1 = (xs.zip(ys).map(&:product).sum - ((ys.sum * xs.sum)/n))/(xs.map(&:square).sum - (xs.sum.square/n))
|
171
|
+
b_0 = ys.mean - b_1 * xs.mean
|
172
|
+
equation = lambda { |x| b_0 + b_1 * x }
|
173
|
+
predicted = xs.map(&equation)
|
174
|
+
residuals = ys.zip(predicted).map { |y| y.shift - y.shift }
|
175
|
+
ss_e = residuals.map(&:square).sum
|
176
|
+
ss_t = ys.sum_of_squares
|
177
|
+
estimator = ss_e/(n - 2)
|
178
|
+
r_2 = 1 - (ss_e/ss_t)
|
179
|
+
r = r_2.sqrt
|
180
|
+
reg = {
|
181
|
+
:n => n,
|
182
|
+
:b_1 => b_1,
|
183
|
+
:b_0 => b_0,
|
184
|
+
:predicted => predicted,
|
185
|
+
:residuals => residuals,
|
186
|
+
:ss_e => ss_e,
|
187
|
+
:ss_t => ss_t,
|
188
|
+
:estimator => estimator,
|
189
|
+
:equation => equation,
|
190
|
+
:r_2 => r_2,
|
191
|
+
:r => r
|
192
|
+
}
|
193
|
+
return reg
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'darkext/hash'
|
2
|
+
|
3
|
+
class String
|
4
|
+
# Parses a string like "1..10" to a Range
|
5
|
+
def to_range
|
6
|
+
case self.count('.')
|
7
|
+
when 2
|
8
|
+
elements = self.split('..')
|
9
|
+
if elements[0] == elements[0].to_i.to_s
|
10
|
+
return Range.new(elements[0].to_i, elements[1].to_i)
|
11
|
+
else
|
12
|
+
return Range.new(elements[0], elements[1])
|
13
|
+
end
|
14
|
+
when 3
|
15
|
+
elements = self.split('...')
|
16
|
+
if elements[0] == elements[0].to_i.to_s
|
17
|
+
return Range.new(elements[0].to_i, elements[1].to_i, true)
|
18
|
+
else
|
19
|
+
return Range.new(elements[0], elements[1], true)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
raise ArgumentError.new('Could not parse range')
|
23
|
+
end
|
24
|
+
|
25
|
+
# Executes the string with system
|
26
|
+
# * :background => true to run command in the background using & (currently only works on *nix systems)
|
27
|
+
# * :capture => true to capture the output. If :capture => true, background is voided
|
28
|
+
def exec(opts = {})
|
29
|
+
opts.with_defaults!(:background => false, :capture => false)
|
30
|
+
return `#{self}` if opts[:capture]
|
31
|
+
return fork { system(self) } if opts[:background]
|
32
|
+
return system(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Prints the String using print
|
36
|
+
def print
|
37
|
+
Kernel.print(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def printn
|
41
|
+
Kernel.print(self + "\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
def true?
|
45
|
+
self.downcase == 'true'
|
46
|
+
end
|
47
|
+
|
48
|
+
def false?
|
49
|
+
self.downcase == 'false'
|
50
|
+
end
|
51
|
+
|
52
|
+
def starts_with?(str)
|
53
|
+
str = str.to_s
|
54
|
+
str == self[0, str.length]
|
55
|
+
end
|
56
|
+
|
57
|
+
def ends_with?(str)
|
58
|
+
str = str.to_s
|
59
|
+
str == self[-str.length, str.length]
|
60
|
+
end
|
61
|
+
|
62
|
+
alias :/ :split
|
63
|
+
end
|
data/spec/array_spec.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Array do
|
4
|
+
before(:each) do
|
5
|
+
@a = (1..5).to_a
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should respond to all the new methods' do
|
9
|
+
%w(rotate rotate_reverse sum product squares squares! random pick randomize randomize!).each { |method| [1].methods.include?(method).should == true }
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should not remove or add elements when randomizing' do
|
13
|
+
@a.randomize.size.should == @a.size
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should rotate back to normal' do
|
17
|
+
b = @a.clone
|
18
|
+
b.rotate(b.size)
|
19
|
+
b.should == @a
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should rotate reverse back to normal' do
|
23
|
+
b = @a.clone
|
24
|
+
b.rotate_reverse(b.size)
|
25
|
+
b.should == @a
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should return a Numeric from sum and sum' do
|
29
|
+
@a.sum.should be_a_kind_of(Numeric)
|
30
|
+
@a.product.should be_a_kind_of(Numeric)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should return an array of equal size from squares' do
|
34
|
+
squares = @a.squares
|
35
|
+
squares.should be_a_kind_of(Array)
|
36
|
+
squares.size.should == @a.size
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should destructively collect squares' do
|
40
|
+
b = @a.clone
|
41
|
+
b.squares!.should == @a.squares
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should pick randomly' do
|
45
|
+
counts = [0,0,0,0,0]
|
46
|
+
count = 1000000
|
47
|
+
count.times do
|
48
|
+
r = @a.random
|
49
|
+
counts[r - 1] += 1
|
50
|
+
end
|
51
|
+
counts.each do |v|
|
52
|
+
(v/count.to_f).should be_close(1/counts.size.to_f,0.001)
|
53
|
+
end
|
54
|
+
|
55
|
+
@a.include?(@a.random).should be_true
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should return an Array from randomize' do
|
59
|
+
@a.randomize.should be_a_kind_of(Array)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should return nil from sum, product and random if size == 0' do
|
63
|
+
%w(sum product random).each do |method|
|
64
|
+
Array.new.send(method.intern).should be_nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should rotate properly' do
|
69
|
+
@a.rotate
|
70
|
+
@a.should == [2,3,4,5,1]
|
71
|
+
@a.rotate
|
72
|
+
@a.should == [3,4,5,1,2]
|
73
|
+
@a.rotate(@a.size)
|
74
|
+
@a.should == [3,4,5,1,2]
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should rotate reverse properly' do
|
78
|
+
@a.rotate_reverse
|
79
|
+
@a.should == [5,1,2,3,4]
|
80
|
+
@a.rotate_reverse
|
81
|
+
@a.should == [4,5,1,2,3]
|
82
|
+
@a.rotate_reverse(@a.size)
|
83
|
+
@a.should == [4,5,1,2,3]
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should sum properly' do
|
87
|
+
@a.sum.should == 15
|
88
|
+
[1].sum.should == 1
|
89
|
+
%w(a b c).sum.should == 'abc'
|
90
|
+
[1.0,0.1,0.01].sum.should == 1.11
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should do product properly' do
|
94
|
+
@a.product.should == 120
|
95
|
+
[1].product.should == 1
|
96
|
+
['a',3].product.should == 'aaa'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should do squares properly' do
|
100
|
+
@a.squares.should == [1,4,9,16,25]
|
101
|
+
[1].squares.should == [1]
|
102
|
+
@a.squares!
|
103
|
+
@a.should == [1,4,9,16,25]
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should randomize' do
|
107
|
+
@a.randomize.should_not == @a
|
108
|
+
a = @a.clone
|
109
|
+
@a.randomize!
|
110
|
+
@a.should_not == a
|
111
|
+
end
|
112
|
+
end
|