hassox-pancake 0.1.6

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 (125) hide show
  1. data/LICENSE +20 -0
  2. data/README.textile +95 -0
  3. data/Rakefile +56 -0
  4. data/TODO +18 -0
  5. data/bin/jeweler +19 -0
  6. data/bin/pancake-gen +17 -0
  7. data/bin/rubyforge +19 -0
  8. data/lib/pancake.rb +48 -0
  9. data/lib/pancake/bootloaders.rb +180 -0
  10. data/lib/pancake/configuration.rb +140 -0
  11. data/lib/pancake/core_ext/class.rb +44 -0
  12. data/lib/pancake/core_ext/object.rb +22 -0
  13. data/lib/pancake/core_ext/symbol.rb +15 -0
  14. data/lib/pancake/errors.rb +61 -0
  15. data/lib/pancake/generators.rb +8 -0
  16. data/lib/pancake/generators/base.rb +12 -0
  17. data/lib/pancake/generators/flat_generator.rb +17 -0
  18. data/lib/pancake/generators/short_generator.rb +17 -0
  19. data/lib/pancake/generators/stack_generator.rb +17 -0
  20. data/lib/pancake/generators/templates/common/dotgitignore +22 -0
  21. data/lib/pancake/generators/templates/common/dothtaccess +17 -0
  22. data/lib/pancake/generators/templates/flat/%stack_name%/%stack_name%.rb.tt +8 -0
  23. data/lib/pancake/generators/templates/flat/%stack_name%/.gitignore +21 -0
  24. data/lib/pancake/generators/templates/flat/%stack_name%/config.ru.tt +12 -0
  25. data/lib/pancake/generators/templates/flat/%stack_name%/pancake.init.tt +1 -0
  26. data/lib/pancake/generators/templates/flat/%stack_name%/public/.empty_directory +0 -0
  27. data/lib/pancake/generators/templates/flat/%stack_name%/tmp/.empty_directory +0 -0
  28. data/lib/pancake/generators/templates/short/%stack_name%/.gitignore +21 -0
  29. data/lib/pancake/generators/templates/short/%stack_name%/LICENSE.tt +20 -0
  30. data/lib/pancake/generators/templates/short/%stack_name%/README.tt +7 -0
  31. data/lib/pancake/generators/templates/short/%stack_name%/Rakefile.tt +48 -0
  32. data/lib/pancake/generators/templates/short/%stack_name%/VERSION.tt +1 -0
  33. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%.rb.tt +5 -0
  34. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/%stack_name%.rb.tt +6 -0
  35. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config.ru.tt +12 -0
  36. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/gems/cache/.empty_directory +0 -0
  37. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
  38. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
  39. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
  40. data/lib/pancake/generators/templates/short/%stack_name%/pancake.init.tt +1 -0
  41. data/lib/pancake/generators/templates/short/%stack_name%/spec/%stack_name%_spec.rb.tt +7 -0
  42. data/lib/pancake/generators/templates/short/%stack_name%/spec/spec_helper.rb.tt +9 -0
  43. data/lib/pancake/generators/templates/stack/%stack_name%/.gitignore +21 -0
  44. data/lib/pancake/generators/templates/stack/%stack_name%/LICENSE.tt +20 -0
  45. data/lib/pancake/generators/templates/stack/%stack_name%/README.tt +7 -0
  46. data/lib/pancake/generators/templates/stack/%stack_name%/Rakefile.tt +48 -0
  47. data/lib/pancake/generators/templates/stack/%stack_name%/VERSION.tt +1 -0
  48. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%.rb.tt +3 -0
  49. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config.ru.tt +12 -0
  50. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/environments/development.rb.tt +18 -0
  51. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/environments/production.rb.tt +18 -0
  52. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/router.rb.tt +6 -0
  53. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/gems/cache/.empty_directory +0 -0
  54. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
  55. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
  56. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
  57. data/lib/pancake/generators/templates/stack/%stack_name%/pancake.init.tt +1 -0
  58. data/lib/pancake/generators/templates/stack/%stack_name%/spec/%stack_name%_spec.rb.tt +7 -0
  59. data/lib/pancake/generators/templates/stack/%stack_name%/spec/spec_helper.rb.tt +9 -0
  60. data/lib/pancake/hooks/inheritable_inner_classes.rb +60 -0
  61. data/lib/pancake/hooks/on_inherit.rb +34 -0
  62. data/lib/pancake/master.rb +87 -0
  63. data/lib/pancake/middleware.rb +354 -0
  64. data/lib/pancake/mime_types.rb +265 -0
  65. data/lib/pancake/mixins/publish.rb +125 -0
  66. data/lib/pancake/mixins/publish/action_options.rb +104 -0
  67. data/lib/pancake/mixins/render.rb +61 -0
  68. data/lib/pancake/mixins/request_helper.rb +92 -0
  69. data/lib/pancake/mixins/stack_helper.rb +44 -0
  70. data/lib/pancake/mixins/url.rb +10 -0
  71. data/lib/pancake/more/controller.rb +4 -0
  72. data/lib/pancake/more/controller/base.rb +34 -0
  73. data/lib/pancake/paths.rb +218 -0
  74. data/lib/pancake/router.rb +99 -0
  75. data/lib/pancake/stack/app.rb +10 -0
  76. data/lib/pancake/stack/bootloader.rb +79 -0
  77. data/lib/pancake/stack/configuration.rb +44 -0
  78. data/lib/pancake/stack/middleware.rb +0 -0
  79. data/lib/pancake/stack/router.rb +18 -0
  80. data/lib/pancake/stack/stack.rb +57 -0
  81. data/lib/pancake/stacks/short.rb +2 -0
  82. data/lib/pancake/stacks/short/controller.rb +105 -0
  83. data/lib/pancake/stacks/short/stack.rb +194 -0
  84. data/spec/helpers/helpers.rb +20 -0
  85. data/spec/helpers/matchers.rb +25 -0
  86. data/spec/pancake/bootloaders_spec.rb +109 -0
  87. data/spec/pancake/configuration_spec.rb +177 -0
  88. data/spec/pancake/fixtures/foo_stack/pancake.init +0 -0
  89. data/spec/pancake/fixtures/paths/controllers/controller1.rb +0 -0
  90. data/spec/pancake/fixtures/paths/controllers/controller2.rb +0 -0
  91. data/spec/pancake/fixtures/paths/controllers/controller3.rb +0 -0
  92. data/spec/pancake/fixtures/paths/models/model1.rb +0 -0
  93. data/spec/pancake/fixtures/paths/models/model2.rb +0 -0
  94. data/spec/pancake/fixtures/paths/models/model3.rb +0 -0
  95. data/spec/pancake/fixtures/paths/stack/controllers/controller1.rb +0 -0
  96. data/spec/pancake/fixtures/paths/stack/models/model3.rb +0 -0
  97. data/spec/pancake/fixtures/paths/stack/views/view1.erb +0 -0
  98. data/spec/pancake/fixtures/paths/stack/views/view1.rb +0 -0
  99. data/spec/pancake/fixtures/paths/stack/views/view2.erb +0 -0
  100. data/spec/pancake/fixtures/paths/stack/views/view2.haml +0 -0
  101. data/spec/pancake/fixtures/render_templates/context_template.html.erb +1 -0
  102. data/spec/pancake/fixtures/render_templates/erb_template.html.erb +1 -0
  103. data/spec/pancake/fixtures/render_templates/erb_template.json.erb +1 -0
  104. data/spec/pancake/fixtures/render_templates/haml_template.html.haml +1 -0
  105. data/spec/pancake/fixtures/render_templates/haml_template.xml.haml +1 -0
  106. data/spec/pancake/hooks/on_inherit_spec.rb +65 -0
  107. data/spec/pancake/inheritance_spec.rb +100 -0
  108. data/spec/pancake/middleware_spec.rb +401 -0
  109. data/spec/pancake/mime_types_spec.rb +234 -0
  110. data/spec/pancake/mixins/publish_spec.rb +94 -0
  111. data/spec/pancake/mixins/render_spec.rb +55 -0
  112. data/spec/pancake/mixins/stack_helper_spec.rb +46 -0
  113. data/spec/pancake/pancake_spec.rb +31 -0
  114. data/spec/pancake/paths_spec.rb +210 -0
  115. data/spec/pancake/stack/app_spec.rb +28 -0
  116. data/spec/pancake/stack/bootloader_spec.rb +41 -0
  117. data/spec/pancake/stack/middleware_spec.rb +0 -0
  118. data/spec/pancake/stack/router_spec.rb +266 -0
  119. data/spec/pancake/stack/stack_configuration_spec.rb +101 -0
  120. data/spec/pancake/stack/stack_spec.rb +55 -0
  121. data/spec/pancake/stacks/short/controller_spec.rb +287 -0
  122. data/spec/pancake/stacks/short/router_spec.rb +132 -0
  123. data/spec/pancake/stacks/short/stack_spec.rb +40 -0
  124. data/spec/spec_helper.rb +21 -0
  125. metadata +238 -0
@@ -0,0 +1,125 @@
1
+ require File.join(File.dirname(__FILE__), "publish", "action_options")
2
+ module Pancake
3
+ module Mixins
4
+ module Publish
5
+
6
+ def self.extended(base)
7
+ base.class_eval do
8
+ class_inheritable_accessor :actions, :formats
9
+ self.actions = {}
10
+ self.formats = [:html]
11
+ end
12
+ end
13
+
14
+ # Accepts a list of symbols representing the formats each action in the
15
+ # controller will return. By default the list consists only of :html
16
+ #
17
+ # provides :html, :xml
18
+ #
19
+ # @api public
20
+ def provides(*args)
21
+ self.formats = args
22
+ end
23
+
24
+ # Causes the next method added immediately after it’s call to be defined
25
+ # as an action. It also generates an instance of ActionOptions, which
26
+ # encapsulates all the parameters the action accepts/expects and also
27
+ # the formats that it can potentially return.
28
+ #
29
+ # publish :id => as_integer(:req)
30
+ # def show
31
+ # ...
32
+ # end
33
+ #
34
+ # The example above publishes the action "show" and configures the
35
+ # following options for it:
36
+ #
37
+ # - The parameter 'id' will be coerced into an Integer
38
+ # - It also must not be blank, it is required.
39
+ #
40
+ # The publish declaration can also create much more sophisticated
41
+ # constraints. You can declare a parameter as optional, give it a default
42
+ # value if it is missing.
43
+ #
44
+ # Here, 'start' and 'end' are required — the default — and are coerced into
45
+ # date values. The 'page' parameter is optional, with the default being 1.
46
+ #
47
+ # publish :start => as_date, :end => as_date :page => as_integer(1)
48
+ #
49
+ # For a list of the built in coercions look in the API for methods prefixed
50
+ # with 'as_'. These are intended to be used in your publish declarations.
51
+ #
52
+ # #publish can also handle the specification of formats for an action. It
53
+ # accepts an additional two options, :provides and :only_provides.
54
+ #
55
+ # To the list of globally declared formats, :provides adds additional
56
+ # formats to an action.
57
+ #
58
+ # publish :provides => :xml
59
+ #
60
+ # The :only_provides option overrides any globally declared formats for an
61
+ # action.
62
+ #
63
+ # publish :only_provides => [:json, :xml]
64
+ #
65
+ # @api public
66
+ def publish(opts = {})
67
+ @pending_publication_opts = opts
68
+ end
69
+
70
+ # Used when declaring configuration for parameters in the publish
71
+ # declaration. It returns an array of the type, default value an
72
+ # additional options.
73
+ #
74
+ # @param type [Object] The
75
+ #
76
+ # The current supported types are
77
+ # - :integer
78
+ # - :date
79
+ # - :string
80
+ #
81
+ # For details on the options supported by each type, please see the
82
+ # corresponding methods declared in the ActionOptions class. These methods
83
+ # are named as the type, prefixed with 'validate_and_coerce_'
84
+ #
85
+ #
86
+ # @example
87
+ # Here is an example where we declare an integer and that it is required
88
+ #
89
+ # as(:integer, :req)
90
+ #
91
+ # Or that it is optional, but has a default value if it is missing.
92
+ #
93
+ # as(:integer, 21)
94
+ #
95
+ # @api public
96
+ def as(type, default = :req, opts = {})
97
+ [type, default, opts]
98
+ end
99
+
100
+ # Takes a parameters hash, and validates each entry against the options
101
+ # defined for this action. It will flag required params when missing,
102
+ # insert defaults or coerce values into the desired type. It mutates
103
+ # the params hash itself.
104
+ #
105
+ # @api private
106
+ def validate_and_coerce_params(action, params)
107
+ actions[action].validate_and_coerce(params)
108
+ end
109
+
110
+
111
+ # This hook is used in conjunction with the #publish method to expose
112
+ # instance methods as actions. Obviously, it should never be called
113
+ # directly :)
114
+ #
115
+ # @api private
116
+ def method_added(name)
117
+ super
118
+ if @pending_publication_opts
119
+ self.actions[name.to_s] = ActionOptions.new(formats, @pending_publication_opts)
120
+ @pending_publication_opts = nil
121
+ end
122
+ end
123
+ end # Publish
124
+ end # Mixins
125
+ end # Pancake
@@ -0,0 +1,104 @@
1
+ module Pancake
2
+ module Mixins
3
+ module Publish
4
+ class ActionOptions
5
+ attr_reader :params, :formats, :default
6
+ CONFIG_OPTIONS = [:provides, :only_provides]
7
+
8
+ # Generates a new instance of the class. This instance encapsulates the
9
+ # options and logic needed to validate the parameters being input into
10
+ # a controller action. First argument is a list of symbols for the
11
+ # formats it supports. The second is hash of options. Typically this
12
+ # hash is generated by the publish declaration in a controller.
13
+ #
14
+ # TODO: Allow params to be grouped together
15
+ #
16
+ # @api private
17
+ def initialize(default_formats, opts)
18
+ # Extract the params — excluding configuration — and turn the keys into
19
+ # strings. Additionally, check to see if someone has passed in a a raw
20
+ # value rather than using one of our as_* methods.
21
+ @params = opts.inject({}) do |memo, opt|
22
+ unless CONFIG_OPTIONS.include? opt[0]
23
+ unless opt[1].is_a?(Array)
24
+ raise "Parameter values must be specified with an as_* method. You passed in a #{opt[1].class.to_s}"
25
+ end
26
+ memo[opt[0].to_s] = opt[1]
27
+ end
28
+ memo
29
+ end
30
+ @formats = extract_format_options(default_formats, opts)
31
+ end
32
+
33
+ # Accepts a hash of parameters and replaces each entry with it’s
34
+ # coerced or default value. If an expected param is missing, it will
35
+ # raise an error.
36
+ #
37
+ # TODO: Flag missing params rather than just raising an error
38
+ # TODO: Allow defaults to be dynamically generated, i.e. method call
39
+ #
40
+ # @api private
41
+ def validate_and_coerce(incoming_params)
42
+ missing = []
43
+ params.each do |name, config|
44
+ type, default, opts = config
45
+ value = incoming_params[name]
46
+
47
+ if !value.nil? && value != ""
48
+ incoming_params[name] = send("validate_and_coerce_#{type}", value, opts)
49
+ else
50
+ if default == :req
51
+ missing << [name, type]
52
+ elsif default != :opt
53
+ incoming_params[name] = default
54
+ end
55
+ end
56
+ end
57
+ [incoming_params, missing]
58
+ end
59
+
60
+ private
61
+
62
+ # Accepts a string and turns it into an integer. The opts argument is
63
+ # only there to satisfy the interface. it is not used.
64
+ #
65
+ # @api private
66
+ def validate_and_coerce_integer(value, opts)
67
+ value.to_i
68
+ end
69
+
70
+ # Accepts a string and turns it into a date. The opts argument is
71
+ # only there to satisfy the interface. it is not used.
72
+ #
73
+ # @api private
74
+ def validate_and_coerce_date(value, opts)
75
+ Date.parse(value)
76
+ end
77
+
78
+ # Turns the incoming value into a string. The opts argument is
79
+ # only there to satisfy the interface. it is not used.
80
+ #
81
+ # @api private
82
+ def validate_and_coerce_string(value, opts)
83
+ value.to_s
84
+ end
85
+
86
+ # Extracts the format options from a hash. It is used to compile the
87
+ # list of formats that an action can provide. It accepts an array of
88
+ # symbols representing the base formats and will add to or overwrite
89
+ # them based on the options in the hash.
90
+ #
91
+ # @api private
92
+ def extract_format_options(defaults, opts)
93
+ @formats = if opts[:provides]
94
+ defaults + [opts[:provides]].flatten
95
+ elsif opts[:only_provides]
96
+ [opts[:only_provides]].flatten
97
+ else
98
+ defaults
99
+ end
100
+ end
101
+ end # ActionOptions
102
+ end # Publish
103
+ end # Mixins
104
+ end # Pancake
@@ -0,0 +1,61 @@
1
+ module Pancake
2
+ module Mixins
3
+ module Render
4
+ class TemplateNotFound < Pancake::Errors::NotFound; end
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ extend Pancake::Mixins::Render::ClassMethods
9
+ include Pancake::Mixins::Render::InstanceMethods
10
+
11
+ unless ancestors.include?(Pancake::Paths)
12
+ extend Pancake::Paths
13
+ end
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ def _view_cache
20
+ @_view_cache ||= {}
21
+ end
22
+
23
+ def _find_template(name)
24
+ renderer = _view_cache[name]
25
+ return renderer if renderer
26
+
27
+ renderer_path = unique_paths_for(:views, :invert => true).detect do |path|
28
+ path.last =~ %r[(#{name})\.\w+$]
29
+ end
30
+
31
+ raise TemplateNotFound unless renderer_path
32
+ _view_cache[name] = Tilt.new(renderer_path.join)
33
+ end
34
+ end # ClassMethods
35
+
36
+ module InstanceMethods
37
+ def render(*args)
38
+ opts = Hash === args.last ? args.pop : {}
39
+ name = args.shift
40
+
41
+ return opts[:text] if opts[:text]
42
+
43
+ # Get the template name to use
44
+ template = _template_name_for(name, opts)
45
+
46
+ # get the render template for that name
47
+ renderer = self.class._find_template(template)
48
+
49
+ # Render the results
50
+ renderer.render(self, opts)
51
+ end
52
+
53
+ private
54
+ def _template_name_for(name, opts)
55
+ opts[:format] ||= params[:format] || :html
56
+ "#{name}.#{opts[:format]}"
57
+ end
58
+ end # InstanceMethods
59
+ end # Render
60
+ end # Mixins
61
+ end # Pancake
@@ -0,0 +1,92 @@
1
+ module Pancake
2
+ module Mixins
3
+ # Some helpers for requests that come in handy for applications that
4
+ # are part of stacks
5
+ module RequestHelper
6
+
7
+ # A setter for the rack environment
8
+ # @api public
9
+ def env=(env)
10
+ @env = env
11
+ end
12
+
13
+ # An accessor for the rack environment variable
14
+ # @api public
15
+ def env
16
+ @env
17
+ end
18
+
19
+ def headers
20
+ @headers ||= {}
21
+ end
22
+
23
+ def status
24
+ @status ||= 200
25
+ end
26
+
27
+ def status=(st)
28
+ @status = st
29
+ end
30
+
31
+ # Generate a url for the current stacks router.
32
+ #
33
+ # @example
34
+ # class MyApp
35
+ # router do |r|
36
+ # r.add("/foo").name(:foo)
37
+ # end
38
+ #
39
+ # include Pancake::RequestHelper
40
+ # # snip
41
+ # def call(env)
42
+ # @env = env
43
+ # url(:foo) # => "/foo"
44
+ # end
45
+ # end
46
+ #
47
+ # @see Usher
48
+ # @see Pancake::Router
49
+ # @api public
50
+ # @author Daniel Neighman
51
+ def url(name, opts = {})
52
+ konfig = request.env[Pancake::Router::CONFIGURATION_KEY]
53
+ konfig.router.generate(name, opts)
54
+ end
55
+
56
+ # Generate a url for any registered configuration with a router
57
+ #
58
+ # @example
59
+ # # an application declared with MyApp.stackup(:app_name =>
60
+ #:some_app)
61
+ # url_for(:some_app, :my_named_route)
62
+ #
63
+ # # An application with no name specified
64
+ # url_for(MyApp, :my_named_route)
65
+ #
66
+ # @see Usher
67
+ # @see Pancake::Router
68
+ # @api public
69
+ # @author Daniel Neighman
70
+ def url_for(app_name, name_or_opts, opts = {})
71
+ if konfig = Pancake.configuration.configs[app_name]
72
+ konfig.router.generate(name_or_opts, opts)
73
+ else
74
+ raise Pancake::Errors::UnknownConfiguration
75
+ end
76
+ end
77
+
78
+ # A handy request method that gets hold of the current request
79
+ # object for the current rack request.
80
+ # Any including class _must_ provide an +env+ method that exposes
81
+ # the rack request environment
82
+ #
83
+ # @see Rack::Request
84
+ # @api public
85
+ # @author Daniel Neighman
86
+ def request
87
+ @request ||= Rack::Request.new(env)
88
+ end
89
+
90
+ end # RequestHelper
91
+ end # Mixins
92
+ end # Pancake
@@ -0,0 +1,44 @@
1
+ module Pancake
2
+ module Mixins
3
+ module StackHelper
4
+ def self.included(base)
5
+ base.class_inheritable_accessor :_stack_class
6
+ base.extend ClassMethods
7
+ base.class_eval do
8
+ include ::Pancake::Mixins::StackHelper::InstanceMethods
9
+ end
10
+ base.stack_class
11
+ end
12
+
13
+ module ClassMethods
14
+ def stack_class
15
+ return @_stack_class if @_stack_class
16
+ klass = nil
17
+ ns = name.split("::")
18
+ until ns.empty? || klass
19
+ r = Object.full_const_get(ns.join("::"))
20
+ if r.ancestors.include?(::Pancake::Stack)
21
+ klass = r
22
+ else
23
+ ns.pop
24
+ end
25
+ end
26
+ if klass.nil?
27
+ raise "#{name} is not from a stack" unless _stack_class
28
+ else
29
+ self._stack_class = r
30
+ end
31
+ _stack_class
32
+ end
33
+ end
34
+
35
+
36
+ module InstanceMethods
37
+ def stack_class
38
+ self.class.stack_class
39
+ end
40
+ end
41
+
42
+ end # StackHelper
43
+ end # Mixins
44
+ end # Pancake
@@ -0,0 +1,10 @@
1
+ module Pancake
2
+ module Url
3
+ module Generation
4
+ def url(name, opts = {})
5
+ konfig = request.env[Pancake::Router::CONFIGURATION_KEY]
6
+ konfig.router.generate(name, opts)
7
+ end
8
+ end # Generation
9
+ end # Url
10
+ end # Pancake
@@ -0,0 +1,4 @@
1
+ path = File.dirname(__FILE__)
2
+ %w(
3
+ controller/base
4
+ ).each {|file| require File.join(path, file)}