manveru-innate 2009.02.06

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 (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