darkext 0.12.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.
- 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
|