manveru-innate 2009.02.06

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/CHANGELOG +1409 -0
  2. data/COPYING +18 -0
  3. data/MANIFEST +100 -0
  4. data/README.md +485 -0
  5. data/Rakefile +139 -0
  6. data/example/app/retro_games.rb +57 -0
  7. data/example/app/whywiki_erb/layout/wiki.html.erb +15 -0
  8. data/example/app/whywiki_erb/spec/wiki.rb +19 -0
  9. data/example/app/whywiki_erb/start.rb +45 -0
  10. data/example/app/whywiki_erb/view/edit.html.erb +6 -0
  11. data/example/app/whywiki_erb/view/index.html.erb +10 -0
  12. data/example/custom_middleware.rb +43 -0
  13. data/example/error_handling.rb +31 -0
  14. data/example/hello.rb +12 -0
  15. data/example/howto_spec.rb +60 -0
  16. data/example/link.rb +35 -0
  17. data/example/providing_hash.rb +46 -0
  18. data/example/session.rb +42 -0
  19. data/innate.gemspec +118 -0
  20. data/lib/innate.rb +191 -0
  21. data/lib/innate/action.rb +156 -0
  22. data/lib/innate/adapter.rb +89 -0
  23. data/lib/innate/cache.rb +117 -0
  24. data/lib/innate/cache/api.rb +106 -0
  25. data/lib/innate/cache/drb.rb +58 -0
  26. data/lib/innate/cache/file_based.rb +39 -0
  27. data/lib/innate/cache/marshal.rb +17 -0
  28. data/lib/innate/cache/memory.rb +22 -0
  29. data/lib/innate/cache/yaml.rb +17 -0
  30. data/lib/innate/core_compatibility/basic_object.rb +9 -0
  31. data/lib/innate/core_compatibility/string.rb +3 -0
  32. data/lib/innate/current.rb +37 -0
  33. data/lib/innate/dynamap.rb +81 -0
  34. data/lib/innate/helper.rb +195 -0
  35. data/lib/innate/helper/aspect.rb +62 -0
  36. data/lib/innate/helper/cgi.rb +39 -0
  37. data/lib/innate/helper/flash.rb +36 -0
  38. data/lib/innate/helper/link.rb +55 -0
  39. data/lib/innate/helper/partial.rb +90 -0
  40. data/lib/innate/helper/redirect.rb +85 -0
  41. data/lib/innate/helper/send_file.rb +18 -0
  42. data/lib/innate/log.rb +23 -0
  43. data/lib/innate/log/color_formatter.rb +43 -0
  44. data/lib/innate/log/hub.rb +72 -0
  45. data/lib/innate/mock.rb +49 -0
  46. data/lib/innate/node.rb +471 -0
  47. data/lib/innate/options.rb +91 -0
  48. data/lib/innate/options/dsl.rb +155 -0
  49. data/lib/innate/request.rb +165 -0
  50. data/lib/innate/response.rb +18 -0
  51. data/lib/innate/route.rb +109 -0
  52. data/lib/innate/session.rb +104 -0
  53. data/lib/innate/session/flash.rb +94 -0
  54. data/lib/innate/setup.rb +23 -0
  55. data/lib/innate/spec.rb +42 -0
  56. data/lib/innate/state.rb +22 -0
  57. data/lib/innate/state/accessor.rb +130 -0
  58. data/lib/innate/state/fiber.rb +68 -0
  59. data/lib/innate/state/thread.rb +39 -0
  60. data/lib/innate/traited.rb +20 -0
  61. data/lib/innate/trinity.rb +22 -0
  62. data/lib/innate/version.rb +3 -0
  63. data/lib/innate/view.rb +67 -0
  64. data/lib/innate/view/erb.rb +17 -0
  65. data/lib/innate/view/none.rb +9 -0
  66. data/lib/rack/middleware_compiler.rb +62 -0
  67. data/lib/rack/reloader.rb +192 -0
  68. data/spec/example/hello.rb +14 -0
  69. data/spec/example/link.rb +29 -0
  70. data/spec/helper.rb +2 -0
  71. data/spec/innate/cache/common.rb +45 -0
  72. data/spec/innate/cache/marshal.rb +5 -0
  73. data/spec/innate/cache/memory.rb +5 -0
  74. data/spec/innate/cache/yaml.rb +5 -0
  75. data/spec/innate/dynamap.rb +22 -0
  76. data/spec/innate/helper.rb +66 -0
  77. data/spec/innate/helper/aspect.rb +80 -0
  78. data/spec/innate/helper/cgi.rb +37 -0
  79. data/spec/innate/helper/flash.rb +148 -0
  80. data/spec/innate/helper/link.rb +82 -0
  81. data/spec/innate/helper/partial.rb +66 -0
  82. data/spec/innate/helper/redirect.rb +148 -0
  83. data/spec/innate/helper/send_file.rb +21 -0
  84. data/spec/innate/helper/view/aspect_hello.erb +1 -0
  85. data/spec/innate/helper/view/locals.erb +1 -0
  86. data/spec/innate/helper/view/loop.erb +4 -0
  87. data/spec/innate/helper/view/num.erb +1 -0
  88. data/spec/innate/helper/view/partial.erb +1 -0
  89. data/spec/innate/helper/view/recursive.erb +8 -0
  90. data/spec/innate/mock.rb +84 -0
  91. data/spec/innate/node.rb +180 -0
  92. data/spec/innate/node/bar.html +1 -0
  93. data/spec/innate/node/foo.html.erb +1 -0
  94. data/spec/innate/node/with_layout.erb +3 -0
  95. data/spec/innate/options.rb +90 -0
  96. data/spec/innate/parameter.rb +154 -0
  97. data/spec/innate/request.rb +73 -0
  98. data/spec/innate/route.rb +129 -0
  99. data/spec/innate/session.rb +59 -0
  100. data/spec/innate/traited.rb +55 -0
  101. metadata +160 -0
@@ -0,0 +1,36 @@
1
+ module Innate
2
+ module Helper
3
+ # Simple access to session.flash.
4
+ #
5
+ # Flash is a mechanism using sessions to provide a rotating holder of
6
+ # key/value pairs.
7
+ #
8
+ # Every request that is made will rotate one step, dropping contents stored
9
+ # two requests ago.
10
+ #
11
+ # The purpose of this class is to provide an easy way of setting/retrieving
12
+ # from the current flash.
13
+ #
14
+ # Flash is a way to keep a temporary pairs of keys and values for the duration
15
+ # of two requests, the current and following.
16
+ #
17
+ # Very vague Example:
18
+ #
19
+ # On the first request, for example on registering:
20
+ #
21
+ # flash[:error] = "You should reconsider your username, it's taken already"
22
+ # redirect R(self, :register)
23
+ #
24
+ # This is the request from the redirect:
25
+ #
26
+ # do_stuff if flash[:error]
27
+ #
28
+ # On the request after this, flash[:error] is gone.
29
+ module Flash
30
+ # Just for convenience
31
+ def flash
32
+ session.flash
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,55 @@
1
+ module Innate
2
+ module Helper
3
+ module Link
4
+ def self.included(into)
5
+ into.extend(self)
6
+ end
7
+
8
+ # Provide the path to given Node and actions.
9
+ # Escapes GET parameters.
10
+ #
11
+ # Usage, mapping is Pages => '/', Users => '/users':
12
+ #
13
+ # Pages.r # => URI('/')
14
+ # Pages.r(:foo) # => URI('/foo')
15
+ # Pages.r(:foo, :bar) # => URI('/foo/bar')
16
+ # Pages.r(:foo, :a => :b) # => URI('/foo?a=b')
17
+ # Pages.r(:foo, :bar, :a => :b) # => URI('/foo/bar?a=b')
18
+ #
19
+ # Users.r # => URI('/users/')
20
+ # Users.r(:foo) # => URI('/users/foo')
21
+ # Users.r(:foo, :bar) # => URI('/users/foo/bar')
22
+ # Users.r(:foo, :a => :b) # => URI('/users/foo?a=b')
23
+ # Users.r(:foo, :bar, :a => :b) # => URI('/users/foo/bar?a=b')
24
+ def route(name = '/', *args)
25
+ hash = {}
26
+ hashes, names = args.partition{|arg| arg.respond_to?(:merge!) }
27
+ hashes.each{|to_merge| hash.merge!(to_merge) }
28
+
29
+ location = Innate.to(self) || Innate.to(self.class)
30
+ front = Array[location, name, *names].join('/').squeeze('/')
31
+
32
+ if hash.empty?
33
+ URI(front)
34
+ else
35
+ escape = Rack::Utils.method(:escape)
36
+ query = hash.map{|key, value| "#{escape[key]}=#{escape[value]}" }.join(';')
37
+ URI("#{front}?#{query}")
38
+ end
39
+ end
40
+ alias r route
41
+
42
+ # Usage, given Wiki is mapped to `/wiki`:
43
+ # Wiki.a(:home) # => '<a href="/wiki/home">home</a>'
44
+ # Wiki.a('home', :home) # => '<a href="/wiki/home">home</a>'
45
+ # Wiki.a('home', :/) # => '<a href="/wiki/">home</a>'
46
+ # Wiki.a('foo', :/, :foo => :bar) # => '<a href="/wiki/?foo=bar">foo</a>'
47
+ def anchor(text, *args)
48
+ href = args.empty? ? r(text) : r(*args)
49
+ text = Rack::Utils.escape_html(text)
50
+ %(<a href="#{href}">#{text}</a>)
51
+ end
52
+ alias a anchor
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,90 @@
1
+ # Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ module Innate
5
+ module Helper
6
+
7
+ # = Helper::Partial
8
+ #
9
+ # === Example Usage
10
+ #
11
+ # class MyController
12
+ # def index
13
+ # end
14
+ #
15
+ # def list
16
+ # plain = request['plain']
17
+ # "Hello World from List! Plain List == #{plain}"
18
+ # end
19
+ # end
20
+ #
21
+ #
22
+ # <html>
23
+ # <head><title>Partial Render Index</title></head>
24
+ # <body>
25
+ # #{render_partial(Rs(:list), 'plain' => true)}
26
+ # </body>
27
+ # </html>
28
+ module Partial
29
+ module_function
30
+
31
+ # Renders a url 'inline'.
32
+ #
33
+ # +url+ normal URL, like you'd use for redirecting.
34
+ # +options+ optional, will be used as request parameters.
35
+ #
36
+ # Issues a mock request to the given +url+ with +options+ turned into
37
+ # query arguments.
38
+ def render_partial(url, options = {})
39
+ uri = URI(url)
40
+ query = options # Innate::Current.request.params.merge(options)
41
+ uri.query = Rack::Utils.build_query(query)
42
+
43
+ body = nil
44
+
45
+ Innate::Mock.session do |session|
46
+ cookie = Innate::Current.session.cookie
47
+ session.cookie = cookie
48
+ body = session.get(uri.to_s, options).body
49
+ end
50
+
51
+ body
52
+ end
53
+
54
+ # Render the template file in view_root of the
55
+ # current controller.
56
+ #
57
+ # TODO:
58
+ # * the local variable hack isn't working because innate allocates a new
59
+ # binding
60
+ # For now one can simply use instance variables, which I prefer anyway.
61
+ # * Doesn't work for absolute paths, but there are no specs for that yet.
62
+ def render_template(path, variables = {})
63
+ path = path.to_s
64
+
65
+ ext = File.extname(path)
66
+ basename = File.basename(path, ext)
67
+ ext = ext[1..-1]
68
+
69
+ action = Innate::Current.action.dup
70
+ action.view = action.node.find_view(basename, ext)
71
+ action.method = action.node.find_method(basename, action.params)
72
+ action.sync_variables(action)
73
+ action.variables.merge!(variables)
74
+
75
+ =begin
76
+ variables.each do |key, value|
77
+ value = "ObjectSpace._id2ref(#{value.object_id})"
78
+ eval "#{key} = #{value}", action.binding
79
+ end
80
+ =end
81
+
82
+ action.call
83
+ end
84
+
85
+ def render_action(method, *params)
86
+ render_partial(r(method), *params)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,85 @@
1
+ module Innate
2
+ module Helper
3
+ module Redirect
4
+ def respond(body, status = 200, header = {})
5
+ response.write body
6
+ response.status = status
7
+ header['Content-Type'] ||= 'text/html'
8
+ header.each{|k,v| response[k] = v }
9
+
10
+ throw(:respond, response)
11
+ end
12
+
13
+ def respond!(body, status = 200, header = {})
14
+ header['Content-Type'] ||= 'text/html'
15
+ throw(:respond, Response.new(body, status, header))
16
+ end
17
+
18
+ # +target+ should be anything responding to #to_s.
19
+ # To check or modify the URI the redirect will go to you may pass a
20
+ # block, the result value of the block is ignored:
21
+ #
22
+ # redirect("/"){|uri| uri.scheme = 'http' }
23
+ # redirect("/"){|uri| uri.host = 'secure.com' if uri.scheme =~ /s/ }
24
+ #
25
+ # +options+ may contain:
26
+ #
27
+ # :scheme => "http" | "https" | "ftp" | ...
28
+ # :host => "localhost" | "foo.com" | "123.123.123.123" | ...
29
+ # :port => 7000 | "80" | 80 | ...
30
+ #
31
+ # :status => 302 | 300 | 303 | ...
32
+ # :body => "This is a redirect, hold on while we teleport" | ...
33
+ #
34
+ # :raw! => true | false | nil | ...
35
+ #
36
+ # Note that all options are optional and you may just pass a +target+.
37
+
38
+ def redirect(target, options = {})
39
+ target = target.to_s
40
+
41
+ case target
42
+ when /^http/, /^\//
43
+ uri = URI(target)
44
+ else
45
+ uri = URI("/#{target}")
46
+ end
47
+
48
+ uri.scheme ||= options[:scheme] || request.scheme
49
+ uri.host ||= options[:host] || request.host
50
+ uri.port ||= options[:port] || request.port
51
+
52
+ uri = URI(uri.to_s)
53
+
54
+ yield(uri) if block_given?
55
+
56
+ raw_redirect(uri, options)
57
+ end
58
+
59
+ def raw_redirect(target, options = {}, &block)
60
+ header = {'Location' => target.to_s}
61
+ status = options[:status] || 302
62
+ body = options[:body] || redirect_body(target)
63
+
64
+ Log.debug "Redirect to: #{target}"
65
+ throw(:redirect, Response.new(body, status, header, &block))
66
+ end
67
+
68
+ def redirect_body(target)
69
+ "You are being redirected, please follow this link to: " +
70
+ "<a href='#{target}'>#{h target}</a>!"
71
+ end
72
+
73
+ def redirect_referrer(fallback = '/')
74
+ if referer = request.referer and url = request.url
75
+ referer_uri, request_uri = URI(referer), URI(url)
76
+
77
+ redirect(referer) unless referer_uri == request_uri
78
+ end
79
+
80
+ redirect(fallback)
81
+ end
82
+ alias redirect_referer redirect_referrer
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,18 @@
1
+ module Innate
2
+ module Helper
3
+ module SendFile
4
+ # Not optimally performing but convenient way to send files by their
5
+ # filename.
6
+ def send_file(filename, content_type = nil)
7
+ content_type ||= Rack::Mime.mime_type(::File.extname(filename))
8
+
9
+ response.body = ::File.readlines(filename, 'rb')
10
+ response['Content-Length'] = ::File.size(filename).to_s
11
+ response['Content-Type'] = content_type
12
+ response.status = 200
13
+
14
+ throw(:respond, response)
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/innate/log.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'innate/log/hub'
2
+ require 'innate/log/color_formatter'
3
+
4
+ module Innate
5
+ logdev, *params = options.log.params
6
+ color = options.log.color
7
+ color = Logger::ColorFormatter.color?(logdev) if color.nil?
8
+
9
+ logger = Logger.new(logdev, *params)
10
+
11
+ if color
12
+ begin
13
+ require 'win32console' if RUBY_PLATFORM =~ /win32/i
14
+
15
+ logger.formatter = Logger::ColorFormatter.new
16
+
17
+ rescue LoadError
18
+ logger.debug "For colors on windows, please `gem install win32console`"
19
+ end
20
+ end
21
+
22
+ Log = LogHub.new(logger)
23
+ end
@@ -0,0 +1,43 @@
1
+ class Logger
2
+ # Extended Formatter that supports ANSI colors.
3
+ class ColorFormatter < Formatter
4
+ LEVEL_COLOR = {
5
+ 'DEBUG' => :blue,
6
+ 'INFO' => :white,
7
+ 'WARN' => :yellow,
8
+ 'ERROR' => :red,
9
+ 'FATAL' => :red,
10
+ 'UNKNOWN' => :green,
11
+ }
12
+
13
+ COLOR_CODE = {
14
+ :reset => 0, :bold => 1, :dark => 2, :underline => 4, :blink => 5,
15
+ :negative => 7, :black => 30, :red => 31, :green => 32, :yellow => 33,
16
+ :blue => 34, :magenta => 35, :cyan => 36, :white => 37,
17
+ }
18
+
19
+ FORMAT_TIME = "%Y-%m-%d %H:%M:%S"
20
+ FORMAT_LINE = "%s [%s $%d] %5s | %s: %s\n"
21
+
22
+ def call(severity, time, program, message)
23
+ hint = severity[0,1]
24
+ time = format_time(time)
25
+ pid = $$
26
+ string = colorize(msg2str(message), severity)
27
+
28
+ FORMAT_LINE % [hint, time, pid, severity, program, string]
29
+ end
30
+
31
+ def format_time(time)
32
+ time.strftime(FORMAT_TIME)
33
+ end
34
+
35
+ def colorize(string, severity)
36
+ "\e[#{COLOR_CODE[LEVEL_COLOR[severity]]}m#{string}\e[0m"
37
+ end
38
+
39
+ def self.color?(logdev)
40
+ logdev.respond_to?(:tty?) and logdev.tty?
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,72 @@
1
+ module Innate
2
+ # Innate only provides logging via stdlib Logger to avoid bloat and
3
+ # dependencies, you may specify multiple loggers in the Log instance of LogHub
4
+ # to accomendate your needs, by default we log to $stderr to be compatible with
5
+ # CGI.
6
+ #
7
+ # Please read the documentation of logger.rb (or even better, its source) to
8
+ # get a feeling of how to use it correctly within Innate
9
+ #
10
+ # A few shortcuts:
11
+ #
12
+ # 1. Create logger for stderr/stdout
13
+ # logger = Logger.new($stdout)
14
+ # logger = Logger.new($stderr)
15
+ #
16
+ # 2. Create logger for a file
17
+ #
18
+ # logger = Logger.new('test.log')
19
+ #
20
+ # 3. Create logger for file object
21
+ #
22
+ # file = File.open('test.log', 'a+')
23
+ # logger = Logger.new(file)
24
+ #
25
+ # 4. Create logger with rotation on specified file size
26
+ #
27
+ # # 10 files history, 5 MB each
28
+ # logger = Logger.new('test.log', 10, (5 << 20))
29
+ #
30
+ # # 100 files history, 1 MB each
31
+ # logger = Logger.new('test.log', 100, (1 << 20))
32
+ #
33
+ # 5. Create a logger which ages logfiles daily/weekly/monthly
34
+ #
35
+ # logger = Logger.new('test.log', 'daily')
36
+ # logger = Logger.new('test.log', 'weekly')
37
+ # logger = Logger.new('test.log', 'monthly')
38
+
39
+ class LogHub
40
+ include Logger::Severity
41
+
42
+ attr_accessor :loggers, :program, :active
43
+
44
+ # +loggers+ should be a list of Logger instances
45
+ def initialize(*loggers)
46
+ @loggers = loggers.flatten
47
+ @program = nil
48
+ @active = true
49
+ self.level = DEBUG
50
+ end
51
+
52
+ # set level for all loggers
53
+ def level=(lvl)
54
+ @loggers.each{|l| l.level = lvl }
55
+ @level = lvl
56
+ end
57
+
58
+ def start; @active = true; end
59
+ def stop; @active = false; end
60
+
61
+ def method_missing(meth, *args, &block)
62
+ eval %~
63
+ def #{meth}(*args, &block)
64
+ return unless @active
65
+ @loggers.each{|l| l.#{meth}(*args, &block) }
66
+ end
67
+ ~
68
+
69
+ send(meth, *args, &block)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,49 @@
1
+ module Innate
2
+ module Mock
3
+ HTTP_METHODS = %w[ CONNECT DELETE GET HEAD OPTIONS POST PUT TRACE ]
4
+
5
+ HTTP_METHODS.each do |method|
6
+ (class << self; self; end).
7
+ send(:define_method, method.downcase){|*args|
8
+ mock(method, *args)
9
+ }
10
+ end
11
+
12
+ def self.mock(method, *args)
13
+ mock_request.request(method, *args)
14
+ end
15
+
16
+ def self.mock_request
17
+ Rack::MockRequest.new(Innate)
18
+ end
19
+
20
+ def self.session
21
+ yield Session.new
22
+ end
23
+
24
+ class Session
25
+ attr_accessor :cookie
26
+
27
+ def initialize
28
+ @cookie = nil
29
+ end
30
+
31
+ HTTP_METHODS.each do |method|
32
+ define_method(method.downcase){|*args|
33
+ extract_cookie(method, *args)
34
+ }
35
+ end
36
+
37
+ def extract_cookie(method, path, hash = {})
38
+ hash['HTTP_COOKIE'] ||= @cookie if @cookie
39
+ response = Mock::mock(method, path, hash)
40
+
41
+ if cookie = response['Set-Cookie']
42
+ @cookie = cookie
43
+ end
44
+
45
+ response
46
+ end
47
+ end
48
+ end
49
+ end