rango 0.2.4.1 → 0.2.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -84,3 +84,13 @@
84
84
  * Ripple support in stack generator
85
85
  * Removed Erubis support, since it doesn't support <%= block(&block) %> so it's useless for us
86
86
  * Specs are green again
87
+
88
+ = Version 0.2.5
89
+ * Conventional rendering mixin for high-level rendering methods like autorender
90
+ * Added RESTController class as a base class for REST controllers
91
+ * Added CRUDMixin with controller CRUD methods
92
+ * UrlHelper#url can work not just as url(:post, @post.id) but also as url(:post, @post)
93
+ * Action args mixin doesn't raise argument error if there are some splat or block arguments
94
+ * Template caching
95
+ * Fixed gemspec to find executables
96
+ * First simple MIME support
@@ -95,7 +95,9 @@ module Rango
95
95
  if (300..399).include?(status)
96
96
  exception = Redirection.new(absolute_uri(location))
97
97
  exception.status = status
98
- exception.headers["Set-Cookie"] = response["Set-Cookie"] # otherwise it don't save cookies
98
+ if response["Set-Cookie"]
99
+ exception.headers["Set-Cookie"] = response["Set-Cookie"] # otherwise it don't save cookies
100
+ end
99
101
  block.call(exception) unless block.nil?
100
102
  raise exception
101
103
  else
@@ -23,6 +23,12 @@ class String
23
23
  return self if self !~ /_/ && self =~ /[A-Z]+.*/
24
24
  split('_').map{|e| e.capitalize}.join
25
25
  end
26
+
27
+ # TODO: this certainly isn't the way to go,
28
+ # but we need to get & refactor extlib at first
29
+ def pluralize
30
+ "#{self}s"
31
+ end
26
32
  end
27
33
 
28
34
  class Hash
@@ -21,16 +21,8 @@ module Rango
21
21
  view = self.method(action)
22
22
  parameters = view.parameters.map! { |type, name| [type, name.to_s] }
23
23
  names = parameters.map { |type, name| name }
24
- types = parameters.map { |type, name| type }
25
24
  required = parameters.map { |type, name| name if type.eql?(:req) }.compact
26
25
 
27
- # validate types
28
- if types.include?(:rest)
29
- raise ArgumentError, "View can't have splat argument. Use just this: def #{action}(#{names[0..-2].join(", ")})"
30
- elsif types.include?(:block)
31
- raise ArgumentError, "View can't have block argument. Use just this: def #{action}(#{names[0..-2].join(", ")})"
32
- end
33
-
34
26
  # validate names
35
27
  unless (extra_keys = required - self.params.keys).empty?
36
28
  raise ArgumentError, "Following keys aren't available in params: #{extra_keys.inspect}\nAvailable keys: #{self.params.keys.inspect}"
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+
3
+ # Use with explicit or implicit rendering mixin
4
+ module Rango
5
+ module ConventionalRendering
6
+ def self.template_dirname=(template_dirname)
7
+ @@template_dirname = template_dirname
8
+ end
9
+
10
+ def self.template_dirname
11
+ @@template_dirname
12
+ rescue NameError
13
+ name = self.class.name
14
+ constant = name.gsub("::", "/")
15
+ dirname = constant.camel_case
16
+ @@template_dirname = dirname
17
+ end
18
+
19
+ def template_dirname
20
+ self.class.template_dirname
21
+ end
22
+
23
+ # @example
24
+ # def template_basename
25
+ # case request["router.action"]
26
+ # when "show"
27
+ # self.class.name.singularize
28
+ # when "index"
29
+ # else
30
+ # end
31
+ # end
32
+ #
33
+ # @api plugin
34
+ def template_basename
35
+ request["router.action"]
36
+ end
37
+
38
+ def template_path
39
+ File.join(template_dirname, template_basename)
40
+ end
41
+
42
+ def render_relative(template, context = nil)
43
+ if context
44
+ super File.join(template_dirname, template), context
45
+ else
46
+ super File.join(template_dirname, template)
47
+ end
48
+ end
49
+
50
+ def autorender(context = nil)
51
+ if context
52
+ render_relative template_basename, context
53
+ else
54
+ render_relative template_basename
55
+ end
56
+ end
57
+
58
+ def display(object)
59
+ autorender
60
+ rescue TemplateNotFound
61
+ callback = self.formats[request.action]
62
+ callback.call
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ module CRUDMixin
4
+ def index(&block)
5
+ raise ArgumentError, "You have to provide a block" if block.nil?
6
+ set_context_value(collection_name, block.call)
7
+ autorender
8
+ end
9
+
10
+ def show(&block)
11
+ raise ArgumentError, "You have to provide a block" if block.nil?
12
+ set_context_value(collection_name, block.call)
13
+ autorender
14
+ end
15
+
16
+ def new(&block)
17
+ raise ArgumentError, "You have to provide a block" if block.nil?
18
+ set_context_value(collection_name, block.call)
19
+ autorender
20
+ end
21
+
22
+ def edit(&block)
23
+ raise ArgumentError, "You have to provide a block" if block.nil?
24
+ set_context_value(collection_name, block.call)
25
+ autorender
26
+ end
27
+
28
+ def create(notice = "Created successfully", error = "Can't create", &block)
29
+ raise ArgumentError, "You have to provide a block" if block.nil?
30
+ object = block.call
31
+ if object.save
32
+ message[:notice] = notice
33
+ redirect url(named_route, object)
34
+ else
35
+ message[:error] = error
36
+ render_relative "show"
37
+ end
38
+ end
39
+
40
+ def update(notice = "Updated successfully", error = "Can't update", &block)
41
+ raise ArgumentError, "You have to provide a block" if block.nil?
42
+ object = block.call
43
+ if object.save
44
+ message[:notice] = notice
45
+ redirect url(named_route, object)
46
+ else
47
+ message[:error] = error
48
+ render_relative "show"
49
+ end
50
+ end
51
+ end
@@ -4,56 +4,55 @@ require "rango/mixins/render"
4
4
 
5
5
  module Rango
6
6
  module FormatMixin
7
- def self.extended
7
+ MIME_TYPES = Rack::Mime::MIME_TYPES
8
+ def self.included(controller)
9
+ controller.class_eval do
10
+ extend ClassMethods
11
+ extend Module.new {
12
+ def inherited(subclass)
13
+ subclass.formats = self.formats
14
+ end
15
+ }
16
+ end
8
17
  end
9
18
 
10
- def inherited
19
+ def initialize(*args)
20
+ super(*args)
21
+ set_content_type
11
22
  end
12
23
 
13
- attr_writer :format
14
- def formats
15
- @formats ||= Hash.new { |hash, format| hash[:html] if hash.has_key?(:html) }
24
+ def set_content_type # rango::controller
25
+ # accept ...
16
26
  end
17
-
18
- # format(:json) do |object|
19
- # object.to_json
20
- # end
21
- def format(format, &block)
22
- self.formats[:format] = block
23
- end
24
- end
25
- module StackRendering
26
- include Rango::RenderMixin
27
- def template_dirname
28
- self.class.name.gsub("::", "/").camel_case
29
- end
30
-
31
- def template_basename
32
- request["router.action"]
33
- end
34
-
35
- #def template_basename
36
- # case request["router.action"]
37
- # when "show"
38
- # self.class.name.singularize
39
- # when "index"
40
- # else
41
- # end
42
- #end
43
-
44
- def template_path
45
- File.join(template_dirname, template_basename)
27
+ def set_content_type
28
+ super
29
+ unless headers["Content-Type"]
30
+ format = request.router_params[:format]
31
+ unless format.nil?
32
+ mime = self.class::MIME_TYPES[format]
33
+ headers["Content-Type"] = mime
34
+ end
35
+ end
46
36
  end
47
37
 
48
- def render(context = Hash.new)
49
- super(self.template_path, self.scope, self.context.merge!(context))
50
- end
38
+ module ClassMethods
39
+ attr_writer :format
40
+ def formats
41
+ @formats ||= Hash.new do |hash, format|
42
+ raise BadRequest, "Unsupported format"
43
+ end
44
+ end
51
45
 
52
- def display(object)
53
- render
54
- rescue TemplateNotFound
55
- callback = self.formats[request.action]
56
- callback.call
46
+ # format(:json) do |object|
47
+ # object.to_json
48
+ # end
49
+ def format(format = nil, &block)
50
+ if format
51
+ self.formats[:format] = block
52
+ else
53
+ self.formats.default_proc = block
54
+ end
55
+ end
57
56
  end
58
57
  end
59
58
 
@@ -89,6 +88,18 @@ module Rango
89
88
  def context
90
89
  @context ||= {request: self.request}
91
90
  end
91
+
92
+ def get_context_value(key)
93
+ @context[key]
94
+ end
95
+
96
+ def set_context_value(key, value)
97
+ @context[key] = value
98
+ end
99
+
100
+ def context_keys
101
+ @context.keys
102
+ end
92
103
  end
93
104
 
94
105
  module ImplicitRendering
@@ -100,5 +111,19 @@ module Rango
100
111
  def render(template) # so you can't specify context
101
112
  super template, self.scope
102
113
  end
114
+
115
+ def get_context_value(key)
116
+ instance_variable_get("@#{key}")
117
+ end
118
+
119
+ def set_context_value(key, value)
120
+ instance_variable_set("@#{key}", value)
121
+ end
122
+
123
+ def context_keys
124
+ instance_variables.map do |name|
125
+ name[1..-1].to_sym
126
+ end
127
+ end
103
128
  end
104
129
  end
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+
3
+ require "rango/controller"
4
+ require "rango/mixins/crud"
5
+ require "rango/mixins/conventional_rendering"
6
+
7
+ module Rango
8
+ class RESTController < Controller
9
+ include CRUDMixin
10
+ include ConventionalRendering
11
+ def self.object_name=(object_name)
12
+ @@object_name = object_name
13
+ end
14
+
15
+ def self.object_name
16
+ @@object_name
17
+ rescue NameError
18
+ @@object_name = begin
19
+ name = self.class.name
20
+ name.split("::").last.snake_case
21
+ end
22
+ end
23
+
24
+ # @api plugin
25
+ def object_name
26
+ self.class.object_name
27
+ end
28
+
29
+ def collection_name
30
+ @@collection_name ||= @@object_name.to_s.pluralize
31
+ end
32
+
33
+ def self.named_route=(named_route)
34
+ @@named_route = named_route
35
+ end
36
+
37
+ def self.named_route
38
+ @@named_route
39
+ rescue NameError
40
+ @@named_route = begin
41
+ name = self.class.name
42
+ name.split("::").last.snake_case
43
+ end
44
+ end
45
+
46
+ def named_route
47
+ self.class.named_route
48
+ end
49
+ end
50
+ end
@@ -8,14 +8,46 @@ end
8
8
 
9
9
  Rango::Router.implement(:usher) do |env|
10
10
  # when usher routes to the default app, then usher.params is nil
11
- env["rango.router.params"] = env["usher.params"] || Hash.new
11
+ env["rango.router.params"] = env["usher.params"] || Hash.new # TODO: nil
12
12
  end
13
13
 
14
14
  module Rango
15
15
  module UrlHelper
16
16
  # url(:login)
17
17
  def url(*args)
18
- Rango::Router.app.router.generator.generate(*args)
18
+ generator = Rango::Router.app.router.generator
19
+ route_name = args.shift
20
+ route = generator.usher.named_routes[route_name]
21
+ raise "No route found" if route.nil? # TODO: add RouteNotFound to usher and use it here as well
22
+ if args.empty?
23
+ generator.generate(route_name) # TODO: usher should probably have path.to_url
24
+ else
25
+ alts = route.paths.map(&:dynamic_keys) # one route can have multiple paths as /:id or /:id.:format
26
+ keys = alts.first
27
+ # FIXME: take a look at other alts as well !!!!
28
+ # keys = alts.find.with_index { |item, index| }
29
+
30
+ # TODO: optional args
31
+ keys_generator = keys.each
32
+ args_generator = args.each
33
+ opts = Hash.new
34
+
35
+ keys.length.times do |index|
36
+ key = keys_generator.next
37
+ arg = args_generator.next
38
+ if arg.respond_to?(key) # post instance
39
+ opts[key] = arg.send(key)
40
+ else # it's already a slug
41
+ opts[key] = arg
42
+ end
43
+ end
44
+
45
+ generator.generate(route_name, opts)
46
+ end
19
47
  end
20
48
  end
21
49
  end
50
+
51
+
52
+ # route = Rango::Router.app.router.generator.usher.named_routes[:admin_edit_sticker]
53
+ # p route.paths.map(&:dynamic_keys)
@@ -28,7 +28,12 @@ module Rango
28
28
  # @since 0.0.2
29
29
  def initialize(path, scope = Object.new)
30
30
  self.path = path#[scope.class.template_prefix.chomp("/"), template].join("/")
31
- self.scope = scope.extend(TemplateHelpers)
31
+ self.scope = scope
32
+ self.scope.extend(TemplateHelpers)
33
+ # this enables template caching
34
+ unless Rango.development?
35
+ self.scope.extend(Tilt::CompileSite)
36
+ end
32
37
  self.scope.template = self
33
38
  end
34
39
 
data/lib/rango/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Rango
4
- VERSION = "0.2.4.1"
4
+ VERSION = "0.2.5.1"
5
5
  end
data/rango.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  # files
19
19
  s.files = `git ls-files`.split("\n")
20
20
 
21
- Dir["bin/*"].map(&File.method(:basename))
21
+ s.executables = Dir["bin/*"].map(&File.method(:basename))
22
22
  s.default_executable = "rango"
23
23
  s.require_paths = ["lib"]
24
24
 
@@ -19,10 +19,12 @@ describe Rango::ActionArgsMixin do
19
19
  "#{post} - #{msg}"
20
20
  end
21
21
 
22
- def view_with_a_splat(*args)
22
+ def view_with_a_splat(id, *args)
23
+ id
23
24
  end
24
25
 
25
- def view_with_a_block(&block)
26
+ def view_with_a_block(id, &block)
27
+ id
26
28
  end
27
29
  end
28
30
 
@@ -31,14 +33,16 @@ describe Rango::ActionArgsMixin do
31
33
  env.merge("rango.controller.action" => action)
32
34
  end
33
35
 
34
- it "should raise argument error if there is a splat argument" do
35
- env = env_for_action(:view_with_a_splat)
36
- -> { controller.call(env) }.should raise_error(ArgumentError)
36
+ it "should ignore splat arguments" do
37
+ env = env_for_action(:view_with_a_splat, "/?id=12")
38
+ status, headers, body = controller.call(env)
39
+ body.should eql(["12"])
37
40
  end
38
41
 
39
- it "should raise argument error if there is a block argument" do
40
- env = env_for_action(:view_with_a_block)
41
- -> { controller.call(env) }.should raise_error(ArgumentError)
42
+ it "should ignore block arguments" do
43
+ env = env_for_action(:view_with_a_block, "/?id=12")
44
+ status, headers, body = controller.call(env)
45
+ body.should eql(["12"])
42
46
  end
43
47
 
44
48
  it "should raise argument error if there are arguments which doesn't match any key in params" do
@@ -54,7 +58,6 @@ describe Rango::ActionArgsMixin do
54
58
 
55
59
  it "should call a view with arguments matching params[argument]" do
56
60
  env = env_for_action(:show, "/?id=12&msg=hi") # nevadi ze je tam toho vic
57
- instance = controller.new(env)
58
61
  status, headers, body = controller.call(env)
59
62
  body.should eql(["12"])
60
63
  end
File without changes
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "../spec_helper"
4
+
5
+ require "rack/mock"
6
+ require "rango/rest_controller"
7
+
8
+ class TestRestController < Rango::RESTController
9
+ end
10
+
11
+ describe Rango::RESTController do
12
+ before(:each) do
13
+ env = Rack::MockRequest.env_for("/")
14
+ @controller = TestRestController.new(env)
15
+ end
16
+ end
data/spec/rango_spec.rb CHANGED
@@ -5,14 +5,15 @@ require "rango"
5
5
 
6
6
 
7
7
  describe Rango do
8
- it "should has logger" do
9
- #Rango.logger.should respond_to?(:debug)
10
- #Rango.logger.should respond_to?(:info)
11
- #Rango.logger.should respond_to?(:warn)
12
- #Rango.logger.should respond_to?(:error)
13
- #Rango.logger.should respond_to?(:fatal)
8
+ it "should have logger" do
9
+ Rango.logger.should respond_to?(:debug)
10
+ Rango.logger.should respond_to?(:info)
11
+ Rango.logger.should respond_to?(:warn)
12
+ Rango.logger.should respond_to?(:error)
13
+ Rango.logger.should respond_to?(:fatal)
14
14
  end
15
15
 
16
+ # what about logger vs. extlib logger (fatal!)
16
17
  describe ".boot" do
17
18
  # TODO
18
19
  end
@@ -43,6 +43,7 @@ gem "rack-router"#, git: "git://github.com/carllerche/rack-router.git"
43
43
  gem "tilt"#, git: "git://github.com/rtomayko/tilt.git"
44
44
  gem "haml"#, git: "git://github.com/nex3/haml.git"
45
45
  gem "helpers"#, git: "git://github.com/botanicus/helpers.git"
46
+ gem "formidable"#, git: "git://github.com/botanicus/formidable.git"
46
47
  gem "pupu"#, git: "git://github.com/botanicus/pupu.git"
47
48
  gem "media-path"#, git: "git://github.com/botanicus/media-path.git" # for asset helpers
48
49
 
@@ -96,7 +97,7 @@ end
96
97
  group(:test, :cucumber) do
97
98
  gem "rspec"#, git: "git://github.com/dchelimsky/rspec.git"
98
99
  gem "rack-test", require: "rack/test"#, git: "git://github.com/brynary/rack-test.git"
99
- gem "webrat"#, git: "git://github.com/brynary/webrat.git"
100
+ gem "capybara"#, git: "git://github.com/jnicklas/capybara.git"
100
101
  end
101
102
 
102
103
  group(:cucumber) do
@@ -13,5 +13,16 @@ require "multigiri/minify"
13
13
  Rango.after_boot(:rackup) do
14
14
  <%= @name.camel_case %>.rackup do
15
15
  use Rango::Middlewares::Basic
16
+
17
+ use Multigiri::HTML do
18
+ use Multigiri::HTML5::Forms
19
+ use Multigiri::HTML5::Hidden
20
+ use Multigiri::EmailObfuscator
21
+ # use Multigiri::GoogleAnalytics, :my_tracking_code
22
+ use Multigiri::DefaultAttributes
23
+ if Rango.development?
24
+ use Multigiri::LinkChecker, SimpleLogger::Logger.new("log/links.log")
25
+ end
26
+ end
16
27
  end
17
28
  end
metadata CHANGED
@@ -5,16 +5,16 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 4
8
+ - 5
9
9
  - 1
10
- version: 0.2.4.1
10
+ version: 0.2.5.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - "Jakub \xC5\xA0\xC5\xA5astn\xC3\xBD aka Botanicus"
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain:
17
- date: 2010-03-23 00:00:00 +00:00
17
+ date: 2010-05-08 00:00:00 +01:00
18
18
  default_executable: rango
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -47,8 +47,8 @@ dependencies:
47
47
  version_requirements: *id002
48
48
  description: ""
49
49
  email: stastny@101ideas.cz
50
- executables: []
51
-
50
+ executables:
51
+ - rango
52
52
  extensions: []
53
53
 
54
54
  extra_rdoc_files: []
@@ -96,6 +96,8 @@ files:
96
96
  - lib/rango/mini.rb
97
97
  - lib/rango/mini_render.rb
98
98
  - lib/rango/mixins/action_args.rb
99
+ - lib/rango/mixins/conventional_rendering.rb
100
+ - lib/rango/mixins/crud.rb
99
101
  - lib/rango/mixins/filters.rb
100
102
  - lib/rango/mixins/logger.rb
101
103
  - lib/rango/mixins/message.rb
@@ -114,6 +116,7 @@ files:
114
116
  - lib/rango/rack/middlewares/encoding.rb
115
117
  - lib/rango/rack/middlewares/static.rb
116
118
  - lib/rango/rack/request.rb
119
+ - lib/rango/rest_controller.rb
117
120
  - lib/rango/router.rb
118
121
  - lib/rango/router/adapters/crudtree.rb
119
122
  - lib/rango/router/adapters/rack_mount.rb
@@ -156,6 +159,7 @@ files:
156
159
  - spec/rango/mini_spec.rb
157
160
  - spec/rango/mixins/action_args_spec.rb
158
161
  - spec/rango/mixins/chainable_spec.rb
162
+ - spec/rango/mixins/conventional_rendering_spec.rb
159
163
  - spec/rango/mixins/filters_spec.rb
160
164
  - spec/rango/mixins/http_caching_spec.rb
161
165
  - spec/rango/mixins/logger_spec.rb
@@ -169,6 +173,7 @@ files:
169
173
  - spec/rango/rack/middlewares/encoding_spec.rb
170
174
  - spec/rango/rack/middlewares/static_spec.rb
171
175
  - spec/rango/rack/request_spec.rb
176
+ - spec/rango/rest_controller_spec.rb
172
177
  - spec/rango/router/adapters/crudtree_spec.rb
173
178
  - spec/rango/router/adapters/rack_mount_spec.rb
174
179
  - spec/rango/router/adapters/urlmap_spec.rb
@@ -276,13 +281,14 @@ has_rdoc: true
276
281
  homepage: http://github.com/botanicus/rango
277
282
  licenses: []
278
283
 
279
- post_install_message: "[\e[32mVersion 0.2.4\e[0m] Removed all helpers, if you need some, use http://github.com/botanicus/helpers\n\
280
- [\e[32mVersion 0.2.4\e[0m] [FEATURE] Added rango/mailer with mail helper for sending e-mails\n\
281
- [\e[32mVersion 0.2.4\e[0m] Rango.root & Rango.media_path are now instances of Pathname rather than String\n\
282
- [\e[32mVersion 0.2.4\e[0m] rango/environments are no longer optional\n\
283
- [\e[32mVersion 0.2.4\e[0m] Ripple support in stack generator\n\
284
- [\e[32mVersion 0.2.4\e[0m] Removed Erubis support, since it doesn't support <%= block(&block) %> so it's useless for us\n\
285
- [\e[32mVersion 0.2.4\e[0m] Specs are green again\n"
284
+ post_install_message: "[\e[32mVersion 0.2.5\e[0m] Conventional rendering mixin for high-level rendering methods like autorender\n\
285
+ [\e[32mVersion 0.2.5\e[0m] Added RESTController class as a base class for REST controllers\n\
286
+ [\e[32mVersion 0.2.5\e[0m] Added CRUDMixin with controller CRUD methods\n\
287
+ [\e[32mVersion 0.2.5\e[0m] UrlHelper#url can work not just as url(:post, @post.id) but also as url(:post, @post)\n\
288
+ [\e[32mVersion 0.2.5\e[0m] Action args mixin doesn't raise argument error if there are some splat or block arguments\n\
289
+ [\e[32mVersion 0.2.5\e[0m] Template caching\n\
290
+ [\e[32mVersion 0.2.5\e[0m] Fixed gemspec to find executables\n\
291
+ [\e[32mVersion 0.2.5\e[0m] First simple MIME support\n"
286
292
  rdoc_options: []
287
293
 
288
294
  require_paths: