vex 0.2

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.
Files changed (114) hide show
  1. data/Manifest +112 -0
  2. data/Rakefile +53 -0
  3. data/VERSION +1 -0
  4. data/config/README +2 -0
  5. data/config/dependencies.rb +10 -0
  6. data/config/gem.yml +7 -0
  7. data/init.rb +36 -0
  8. data/lib/nokogiri/nokogiri_ext.rb +96 -0
  9. data/lib/vex.rb +5 -0
  10. data/lib/vex/action_controller.rb +4 -0
  11. data/lib/vex/action_controller/verify_action.rb +97 -0
  12. data/lib/vex/action_controller/whitelisted_actions.rb +45 -0
  13. data/lib/vex/active_record.rb +3 -0
  14. data/lib/vex/active_record/__init__.rb +0 -0
  15. data/lib/vex/active_record/advisory_lock.rb +11 -0
  16. data/lib/vex/active_record/advisory_lock/mysql_adapter.rb +16 -0
  17. data/lib/vex/active_record/advisory_lock/sqlite_adapter.rb +78 -0
  18. data/lib/vex/active_record/belongs_to_many.rb +143 -0
  19. data/lib/vex/active_record/find_by_extension.rb +70 -0
  20. data/lib/vex/active_record/gem.rb +8 -0
  21. data/lib/vex/active_record/lite_table.rb +139 -0
  22. data/lib/vex/active_record/lite_view.rb +140 -0
  23. data/lib/vex/active_record/mass_load.rb +65 -0
  24. data/lib/vex/active_record/mysql_backup.rb +21 -0
  25. data/lib/vex/active_record/plugins/default_value_for/LICENSE.TXT +19 -0
  26. data/lib/vex/active_record/plugins/default_value_for/README.rdoc +421 -0
  27. data/lib/vex/active_record/plugins/default_value_for/Rakefile +6 -0
  28. data/lib/vex/active_record/plugins/default_value_for/init.rb +91 -0
  29. data/lib/vex/active_record/plugins/default_value_for/test.rb +279 -0
  30. data/lib/vex/active_record/plugins/default_value_for/test.sqlite3 +0 -0
  31. data/lib/vex/active_record/random_id.rb +56 -0
  32. data/lib/vex/active_record/resolver.rb +49 -0
  33. data/lib/vex/active_record/serialize_hash.rb +125 -0
  34. data/lib/vex/active_record/to_html.rb +53 -0
  35. data/lib/vex/active_record/validate.rb +76 -0
  36. data/lib/vex/active_record/validation_error_ext.rb +68 -0
  37. data/lib/vex/base.rb +2 -0
  38. data/lib/vex/base/app.rb +75 -0
  39. data/lib/vex/base/array/at_random.rb +17 -0
  40. data/lib/vex/base/array/cross.rb +26 -0
  41. data/lib/vex/base/array/each_batch.rb +32 -0
  42. data/lib/vex/base/array/parallel_map.rb +98 -0
  43. data/lib/vex/base/deprecation.rb +41 -0
  44. data/lib/vex/base/enumerable/deep.rb +95 -0
  45. data/lib/vex/base/enumerable/enumerable_ext.rb +59 -0
  46. data/lib/vex/base/enumerable/progress.rb +71 -0
  47. data/lib/vex/base/filesystem/fast_copy.rb +61 -0
  48. data/lib/vex/base/filesystem/grep.rb +34 -0
  49. data/lib/vex/base/filesystem/lock.rb +43 -0
  50. data/lib/vex/base/filesystem/lock.rb.test.lck +0 -0
  51. data/lib/vex/base/filesystem/lock.rb.test.pid +1 -0
  52. data/lib/vex/base/filesystem/make_dirs.rb +94 -0
  53. data/lib/vex/base/filesystem/parse_filename.rb +36 -0
  54. data/lib/vex/base/filesystem/tmp_file.rb +87 -0
  55. data/lib/vex/base/filesystem/write.rb +43 -0
  56. data/lib/vex/base/hash/compact.rb +38 -0
  57. data/lib/vex/base/hash/cross.rb +117 -0
  58. data/lib/vex/base/hash/easy_access.rb +141 -0
  59. data/lib/vex/base/hash/ensure_keys.rb +18 -0
  60. data/lib/vex/base/hash/extract.rb +71 -0
  61. data/lib/vex/base/hash/extras.rb +62 -0
  62. data/lib/vex/base/hash/inspect.rb +17 -0
  63. data/lib/vex/base/hash/simple_access_methods.rb +74 -0
  64. data/lib/vex/base/invalid_argument/invalid_argument.rb +97 -0
  65. data/lib/vex/base/local_conf.rb +35 -0
  66. data/lib/vex/base/net/http_ext.rb +227 -0
  67. data/lib/vex/base/net/socket_ext.rb +43 -0
  68. data/lib/vex/base/object/insp.rb +123 -0
  69. data/lib/vex/base/object/multiple_attributes.rb +58 -0
  70. data/lib/vex/base/object/singleton_methods.rb +23 -0
  71. data/lib/vex/base/object/with_benchmark.rb +110 -0
  72. data/lib/vex/base/range_array.rb +40 -0
  73. data/lib/vex/base/range_ext.rb +28 -0
  74. data/lib/vex/base/safe_token.rb +156 -0
  75. data/lib/vex/base/string/string_ext.rb +136 -0
  76. data/lib/vex/base/thread/deferred.rb +52 -0
  77. data/lib/vex/base/thread/sleep.rb +11 -0
  78. data/lib/vex/base/time/date_ext.rb +12 -0
  79. data/lib/vex/boot.rb +40 -0
  80. data/lib/vex/boot/array.rb +22 -0
  81. data/lib/vex/boot/blank.rb +41 -0
  82. data/lib/vex/boot/string.rb +60 -0
  83. data/migration/create_request_log.rb +28 -0
  84. data/r.rb +35 -0
  85. data/script/console +19 -0
  86. data/script/rebuild +7 -0
  87. data/tasks/echoe.rake +52 -0
  88. data/tasks/validate_db.rake +14 -0
  89. data/test/ar.rb +30 -0
  90. data/test/auto.rb +31 -0
  91. data/test/base-tests/local_conf.rb +25 -0
  92. data/test/base.rb +2 -0
  93. data/test/boot.rb +3 -0
  94. data/test/config/local.defaults.yml +4 -0
  95. data/test/config/local.yml +8 -0
  96. data/test/test.sqlite3 +0 -0
  97. data/test/test.sqlite3.Class#create.lck +0 -0
  98. data/test/test.sqlite3.Class#create.lck.lck +0 -0
  99. data/test/test.sqlite3.Class#create.lck.pid +1 -0
  100. data/test/test.sqlite3.Class#create.pid +1 -0
  101. data/test/test.sqlite3.LiteView.make.holders__view_dummy.lck +0 -0
  102. data/test/test.sqlite3.LiteView.make.holders__view_dummy.lck.lck +0 -0
  103. data/test/test.sqlite3.LiteView.make.holders__view_dummy.lck.pid +1 -0
  104. data/test/test.sqlite3.LiteView.make.holders__view_dummy.pid +1 -0
  105. data/test/test.sqlite3.vex.lck +0 -0
  106. data/test/test_helper.rb +49 -0
  107. data/test/tmp/copy.dat +1 -0
  108. data/test/tmp/lock.sqlite3 +0 -0
  109. data/test/tmp/lock.sqlite3.etest.lck +0 -0
  110. data/test/tmp/lock.sqlite3.etest.pid +1 -0
  111. data/test/tmp/somedata.dat +61 -0
  112. data/vex.gemspec +49 -0
  113. data/vex.tmproj +186 -0
  114. metadata +305 -0
@@ -0,0 +1,23 @@
1
+ class Object
2
+ module SingletonMethods
3
+ # returns the singleton class of an object
4
+ def singleton_class
5
+ class << self; self; end
6
+ end
7
+
8
+ # defines a method on an object
9
+ def define_object_method(name, &block)
10
+ singleton_class.send :define_method, name, &block
11
+ end
12
+ end
13
+
14
+ include SingletonMethods
15
+ end
16
+
17
+ module SingletonMethods::Etest
18
+ def test_singleton_methods
19
+ s = "s"
20
+ s.define_object_method :bla do "blabla" end
21
+ assert_equal("blabla", s.bla)
22
+ end
23
+ end if VEX_TEST == "base"
@@ -0,0 +1,110 @@
1
+ require "benchmark"
2
+
3
+ module Object::WithBenchmark
4
+
5
+ class BenchmarkProxy
6
+ def initialize(host, *args)
7
+ @host = host
8
+
9
+ if args.length == 1 && args.first.is_a?(String)
10
+ @out, @label = nil, args.first
11
+ else
12
+ @out, @label = *args
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def report(msg)
19
+ if !@out
20
+ logger = @host.respond_to?(:logger) && @host.logger || App.logger
21
+
22
+ logger.warn(msg)
23
+ STDERR.puts(msg) if App.env == "development"
24
+ elsif @out.respond_to?(:warn)
25
+ @out.warn(msg)
26
+ elsif @out.respond_to?(:<<)
27
+ @out << "#{msg}\n"
28
+ end
29
+ end
30
+
31
+ def method_missing(*args, &block)
32
+ result = ex = nil
33
+
34
+ realtime = Benchmark.realtime do
35
+ begin
36
+ result = @host.__send__(*args, &block)
37
+ rescue
38
+ ex = $!
39
+ end
40
+ end
41
+
42
+ msg = "#{@label || "benchmarked"}: #{ex && "EXCEPTION #{ex} after "}#{"%.2f secs" % realtime}"
43
+
44
+ report(msg)
45
+
46
+ raise ex if ex
47
+ result
48
+ end
49
+ end
50
+
51
+ def no_benchmark(*args, &block)
52
+ block_given? ? yield : self
53
+ end
54
+
55
+ def benchmark(*args, &block)
56
+ count = args.last.is_a?(Fixnum) ? args.pop : 1
57
+
58
+ proxy = BenchmarkProxy.new self, *args, &block
59
+ return proxy unless block_given?
60
+ return proxy.yield(&block) if count <= 1
61
+ return proxy.yield do
62
+ count.times(&block)
63
+ end
64
+ end
65
+
66
+ def yield(&block); yield; end
67
+ end
68
+
69
+ Object.send :include, Object::WithBenchmark
70
+
71
+ module Object::WithBenchmark::Etest
72
+ def test_results
73
+ App.logger.stubs(:warn).returns(nil)
74
+
75
+ assert_equal 6, "string".benchmark.length
76
+ assert_raise(NoMethodError) {
77
+ "string".benchmark.you_dont_know_me
78
+ }
79
+ end
80
+
81
+ def test_benchmark
82
+ s = ""
83
+ assert_equal 6, "string".benchmark(s, "").length
84
+ assert s.length > 0
85
+ end
86
+
87
+ def test_label
88
+ App.logger.stubs(:warn).returns(nil)
89
+
90
+ assert_equal 6, "string".benchmark("oh! a label!").length
91
+ end
92
+
93
+ class DL
94
+ attr :msg
95
+ def warn(s);
96
+ @msg = s
97
+ end
98
+ end
99
+
100
+ def test_logger
101
+ s = "string"
102
+
103
+ def s.logger; @logger ||= DL.new; end
104
+ assert s.respond_to?(:logger)
105
+ assert s.logger.msg.blank?
106
+
107
+ assert_equal 6, s.benchmark.length
108
+ assert !s.logger.msg.blank?
109
+ end
110
+ end if VEX_TEST == "base"
@@ -0,0 +1,40 @@
1
+ class RangeArray < Array
2
+ def initialize(array)
3
+ min = max = nil
4
+
5
+ array.each do |i|
6
+ if min
7
+ if i >= min && i <= max then next
8
+ elsif i == min-1 then min = i
9
+ elsif i == max+1 then max = i
10
+ else
11
+ push min, max
12
+ min = max = i
13
+ end
14
+ else
15
+ min = max = i
16
+ end
17
+ end
18
+
19
+ push min, max if min
20
+ end
21
+
22
+ def push(min, max)
23
+ super min == max ? min : min..max
24
+ end
25
+ end
26
+
27
+ module RangeArray::Etest
28
+ def ra(*array)
29
+ RangeArray.new array
30
+ end
31
+
32
+ def test_range_array
33
+ assert_equal [1..3], ra(1, 2, 3)
34
+ assert_equal [1..3, 5..6], ra(1, 2, 3, 5, 6)
35
+ assert_equal [1..3, 5..6, 8], ra(1, 2, 3, 5, 6, 8)
36
+ assert_equal [1..2, 7, 3, 5..6, 8], ra(1, 2, 7, 3, 5, 6, 8)
37
+ assert_equal [1..2, 7, 3, 5..6, 8, 4], ra(1, 2, 7, 3, 5, 6, 8, 4)
38
+ assert_equal [1..2, 7, 3, 5..6, 8, 4, -3], ra(1, 2, 7, 3, 5, 6, 8, 4, -3)
39
+ end
40
+ end if VEX_TEST == "base"
@@ -0,0 +1,28 @@
1
+ class Range
2
+ def limit(value)
3
+ if value < first then first
4
+ elsif value > last then last
5
+ else value
6
+ end
7
+ end
8
+ end
9
+
10
+ class Numeric
11
+ def limit(range)
12
+ range.limit(self)
13
+ end
14
+ end
15
+
16
+ module Range::Etest
17
+ def test_range
18
+ assert_equal 1, (1..3).limit(-1)
19
+ assert_equal 1, (1..3).limit(1)
20
+ assert_equal 2, (1..3).limit(2)
21
+ assert_equal 3, (1..3).limit(3)
22
+ assert_equal 3, (1..3).limit(4)
23
+ end
24
+
25
+ def test_numeric
26
+ assert_equal 1, -1.limit(1..3)
27
+ end
28
+ end if VEX_TEST == "base"
@@ -0,0 +1,156 @@
1
+ require 'digest/sha1'
2
+ require 'json'
3
+ require 'base64'
4
+
5
+ module SafeToken
6
+ CipherError = if defined?(OpenSSL::Cipher::CipherError)
7
+ OpenSSL::Cipher::CipherError
8
+ else
9
+ OpenSSL::CipherError
10
+ end
11
+
12
+ class InvalidToken < RuntimeError; end
13
+ class TokenTooLong < InvalidToken; end
14
+ class TokenExpired < InvalidToken
15
+ attr :expires
16
+ def initialize(expires)
17
+ @expires = expires
18
+ end
19
+
20
+ def to_s
21
+ "token expired at #{expires}"
22
+ end
23
+ end
24
+ private
25
+
26
+ DEFAULTS = {
27
+ :secret => "46a348efe02807c999d69709abdbcd1b",
28
+ :limit => 800, # This should be safe for an URL
29
+ :crypt => false
30
+ }
31
+
32
+ def self.hash!(opts, data)
33
+ Digest::SHA1.hexdigest data
34
+ end
35
+
36
+ def self.aes(encrypt_or_decrypt, data, opts)
37
+ return data if opts[:crypt] == false
38
+
39
+ # get the password and make sure it is long enough for the algorithm to work
40
+ secret = opts[:crypt].is_a?(String) ? opts[:crypt] : opts[:secret]
41
+ secret = Digest::SHA1.hexdigest(secret)
42
+
43
+ c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
44
+ c.send encrypt_or_decrypt
45
+ c.key = secret
46
+ s = c.update(data)
47
+ s << c.final
48
+ end
49
+
50
+ def self.encode64(s)
51
+ Base64.encode64(s).gsub("\n", '').gsub("=", '-')
52
+ end
53
+
54
+ def self.decode64(s)
55
+ Base64.decode64(s.gsub("-", '='))
56
+ end
57
+
58
+ def self.limit!(s, opts)
59
+ if opts[:limit] && s.length > opts[:limit]
60
+ raise TokenTooLong, "Token too long: (#{s.length} bytes, allowed are #{opts[:limit]})"
61
+ end
62
+ s
63
+ end
64
+
65
+ public
66
+
67
+ def self.generate(data, opts = {})
68
+ opts = DEFAULTS.dup.update(opts)
69
+ expires = opts[:expires].to_i if opts[:expires]
70
+
71
+ data = data.to_json
72
+
73
+ hash = hash!(opts, "#{opts[:secret]}-#{expires}:#{data}")
74
+ s = "sha1:#{hash}:#{expires}:#{data}"
75
+
76
+ s = aes(:encrypt, s, opts)
77
+ s = encode64(s)
78
+ limit!(s, opts)
79
+ end
80
+
81
+ def self.validate(s, opts = {})
82
+ opts = DEFAULTS.dup.update(opts)
83
+
84
+ begin
85
+ s = decode64(s)
86
+ s = aes(:decrypt, s, opts)
87
+ rescue SafeToken::CipherError
88
+ raise InvalidToken, "Invalid token encryption: #{$!}"
89
+ end
90
+
91
+ raise InvalidToken, "Invalid token syntax" unless s =~ /^([^:]*):([^:]*):([^:]*):(.*)/
92
+
93
+ method, hash, expires, data = $1, $2, $3, $4
94
+
95
+ unless hash!(opts, "#{opts[:secret]}-#{expires}:#{data}") == hash
96
+ raise InvalidToken, "Invalid token #{s}"
97
+ end
98
+
99
+ if !expires.empty?
100
+ expires = Time.at(expires.to_i)
101
+ raise TokenExpired, expires if expires < Time.now
102
+ end
103
+
104
+ JSON.parse(data)
105
+ end
106
+ end
107
+
108
+ module SafeToken::Etest
109
+ def data
110
+ {"a" => "123232", "b" => { "c" => [ 1, 2, "d" ]}}
111
+ end
112
+
113
+ def test_expiration
114
+ token = SafeToken.generate(data, :crypt => false, :expires => Time.now-10)
115
+ assert_raise(SafeToken::TokenExpired) {
116
+ begin
117
+ SafeToken.validate(token)
118
+ rescue
119
+ assert $!.to_s =~ /expired/
120
+ raise
121
+ end
122
+ }
123
+
124
+ token = SafeToken.generate(data, :crypt => false, :expires => Time.now+10)
125
+ assert_equal data, SafeToken.validate(token)
126
+ end
127
+
128
+ def test_token
129
+ token = SafeToken.generate(data, :crypt => false)
130
+ assert_equal data, SafeToken.validate(token)
131
+ end
132
+
133
+ def test_token_w_crypt
134
+ token = SafeToken.generate(data, :crypt => true)
135
+ assert_equal data, SafeToken.validate(token, :crypt => true)
136
+ end
137
+
138
+ def test_token_w_crypt2
139
+ token1 = SafeToken.generate(data, :crypt => "secret")
140
+ token2 = SafeToken.generate(data, :crypt => true)
141
+ token3 = SafeToken.generate(data, :crypt => false)
142
+ assert_not_equal(token1, token2)
143
+ assert_not_equal(token1, token3)
144
+ assert_not_equal(token2, token3)
145
+ end
146
+
147
+ def test_invalid_enc_parameters
148
+ token = SafeToken.generate(data, :crypt => true)
149
+
150
+ OpenSSL::Cipher::Cipher.any_instance.stubs(:update).raises(SafeToken::CipherError)
151
+ assert_raise(SafeToken::InvalidToken) {
152
+ assert_equal data, SafeToken.validate(token, :crypt => true)
153
+ }
154
+ end
155
+ end if VEX_TEST == "base"
156
+
@@ -0,0 +1,136 @@
1
+ module StringExt
2
+ def constantize?
3
+ constantize
4
+ rescue LoadError, NameError
5
+ STDERR.puts $!.to_s
6
+ end
7
+
8
+ def uri?
9
+ !!(self =~ /^[a-z][a-z]+:/)
10
+ end
11
+
12
+ def unhtml
13
+ return self if blank?
14
+ s = Sanitize.clean(self)
15
+ HTMLEntities.new.decode(s)
16
+ end
17
+
18
+ # truncate :length => 30, :omission => "…"
19
+ def truncate(*args)
20
+ opts = if args.length == 1 && args.first.is_a?(Hash)
21
+ args.first
22
+ else
23
+ args.inject({}) do |hash, arg|
24
+ key = case arg
25
+ when Fixnum then :length
26
+ when String then :omission
27
+ end
28
+
29
+ invalid_argument!(arg) if key.nil? || hash[key]
30
+
31
+ hash.update key => arg
32
+ end
33
+ end
34
+
35
+ _truncate(opts)
36
+ end
37
+
38
+ def _truncate(opts)
39
+ opts = { :length => opts, :omission => "…" } unless opts.is_a?(Hash)
40
+ max_length = opts[:length] || 30
41
+ omission = opts[:omission] || "…"
42
+
43
+ #
44
+ # Treat multibytes differently
45
+ if respond_to?(:mb_chars)
46
+ l = max_length - omission.mb_chars.length
47
+ if mb_chars.length > max_length
48
+ return (mb_chars[0...l] + omission).to_s
49
+ end
50
+ else
51
+ l = max_length - omission.length
52
+ if length > max_length
53
+ return self[0...l] + omission
54
+ end
55
+ end
56
+
57
+ self
58
+ end
59
+
60
+ def truncate!(opts = {})
61
+ replace truncate(opts)
62
+ end
63
+
64
+ def word_wrap(line_len=100)
65
+ lines = split("\n")
66
+ lines.map { |line| StringExt.word_wrap(line, line_len) }.join("\n")
67
+ end
68
+
69
+ def self.word_wrap(line, line_len)
70
+ r = []
71
+ while line.length > line_len
72
+ # find last space in the first line_len characters. Failing that we
73
+ # take the *first* (sic!) space in the entire line.
74
+ firstline = line[0..line_len]
75
+ space_idx = firstline.rindex(/\s/) || line.index(/\s/, line_len)
76
+
77
+ if !space_idx
78
+ r << line
79
+ line = ""
80
+ else
81
+ r << line[0...space_idx]
82
+ line = line[space_idx+1..-1]
83
+ end
84
+ end
85
+
86
+ r << line unless line.blank?
87
+ r.join("\n")
88
+ end
89
+ end
90
+
91
+ String.send :include, StringExt
92
+
93
+ module StringExt::Etest
94
+ def test_unhtml
95
+ assert_equal("", "".unhtml)
96
+ assert_equal("hjghjg", "<p>hjghjg</p>".unhtml)
97
+ assert_equal("&auml", "&auml".unhtml)
98
+ assert_equal("ä", "&auml;".unhtml)
99
+ end
100
+
101
+ # truncate :length => 30, :omission => "..."
102
+ def test_truncate
103
+ assert_equal("", "".truncate)
104
+ assert_equal("123456", "123456".truncate)
105
+ assert_equal("123…", "1234567".truncate(:length => 6))
106
+ assert_equal("123…", "1234567".truncate(6))
107
+ assert_equal("12345~", "1234567".truncate(6, "~"))
108
+ end
109
+
110
+ def test_truncate!
111
+ s = "1234567"
112
+
113
+ assert_equal "123…", s.truncate(6)
114
+ assert_equal "1234567", s
115
+
116
+ assert_equal "123…", s.truncate!(6)
117
+ assert_equal "123…", s
118
+ end
119
+
120
+ def test_word_wrap
121
+ assert_equal "abcdef\nghijkl", "abcdef ghijkl".word_wrap(8)
122
+ assert_equal "abc def\nghijkl", "abc def ghijkl".word_wrap(8)
123
+ assert_equal "abcdefghijkl", "abcdefghijkl".word_wrap(8)
124
+ end
125
+
126
+ def test_uri
127
+ assert_equal true, "http://".uri?
128
+ assert_equal false, "//".uri?
129
+ assert_equal false, "c:\\x\\y".uri?
130
+ end
131
+
132
+ def test_constantize
133
+ assert_equal String, "String".constantize?
134
+ assert_equal nil, "I::Dont::Know::This".constantize?
135
+ end
136
+ end if VEX_TEST == "base"