hobo 0.6 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/bin/hobo +2 -3
  2. data/hobo_files/plugin/CHANGES.txt +139 -0
  3. data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +1 -8
  4. data/hobo_files/plugin/generators/hobo_front_controller/templates/controller.rb +1 -39
  5. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +2 -2
  6. data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +27 -7
  7. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +1 -2
  8. data/hobo_files/plugin/generators/hobo_user_controller/USAGE +34 -0
  9. data/hobo_files/plugin/generators/hobo_user_controller/hobo_user_controller_generator.rb +43 -0
  10. data/hobo_files/plugin/generators/hobo_user_controller/templates/controller.rb +5 -0
  11. data/hobo_files/plugin/generators/hobo_user_controller/templates/functional_test.rb +18 -0
  12. data/hobo_files/plugin/generators/hobo_user_controller/templates/helper.rb +2 -0
  13. data/hobo_files/plugin/generators/hobo_user_controller/templates/view.rhtml +2 -0
  14. data/hobo_files/plugin/init.rb +6 -2
  15. data/hobo_files/plugin/lib/extensions.rb +28 -41
  16. data/hobo_files/plugin/lib/hobo.rb +37 -17
  17. data/hobo_files/plugin/lib/hobo/authentication_support.rb +25 -10
  18. data/hobo_files/plugin/lib/hobo/composite_model.rb +2 -2
  19. data/hobo_files/plugin/lib/hobo/controller.rb +8 -34
  20. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +1 -1
  21. data/hobo_files/plugin/lib/hobo/dryml/part_context.rb +103 -0
  22. data/hobo_files/plugin/lib/hobo/dryml/template.rb +10 -11
  23. data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +29 -72
  24. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +11 -10
  25. data/hobo_files/plugin/lib/hobo/migrations.rb +12 -0
  26. data/hobo_files/plugin/lib/hobo/model.rb +3 -3
  27. data/hobo_files/plugin/lib/hobo/model_controller.rb +3 -3
  28. data/hobo_files/plugin/lib/hobo/rapid_helper.rb +3 -3
  29. data/hobo_files/plugin/lib/hobo/user_controller.rb +80 -0
  30. data/hobo_files/plugin/tags/rapid.dryml +36 -20
  31. data/hobo_files/plugin/tags/rapid_editing.dryml +4 -5
  32. data/hobo_files/plugin/tags/rapid_forms.dryml +6 -6
  33. data/hobo_files/plugin/tags/rapid_navigation.dryml +7 -5
  34. data/hobo_files/plugin/tags/rapid_pages.dryml +116 -10
  35. data/hobo_files/plugin/tags/rapid_support.dryml +23 -0
  36. metadata +13 -5
  37. data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +0 -42
  38. data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +0 -43
  39. data/hobo_files/plugin/lib/hobo/mapping_tags.rb +0 -262
@@ -70,13 +70,40 @@ class Module
70
70
  end
71
71
 
72
72
  module Kernel
73
-
73
+
74
74
  def extract_options_from_args!(args) #:nodoc:
75
75
  args.last.is_a?(Hash) ? args.pop : {}
76
76
  end
77
77
 
78
+ def it() It.new end
79
+ alias its it
80
+
81
+ end
82
+
83
+
84
+ class It
85
+
86
+ undef_method(*(instance_methods - %w*__id__ __send__*))
87
+
88
+ def initialize
89
+ @methods = []
90
+ end
91
+
92
+ def method_missing(*args, &block)
93
+ @methods << [args, block] unless args == [:respond_to?, :to_proc]
94
+ self
95
+ end
96
+
97
+ def to_proc
98
+ lambda do |obj|
99
+ @methods.inject(obj) do |current,(args,block)|
100
+ current.send(*args, &block)
101
+ end
102
+ end
103
+ end
78
104
  end
79
105
 
106
+
80
107
  class Object
81
108
 
82
109
  def in?(array)
@@ -112,30 +139,6 @@ end
112
139
 
113
140
  module Enumerable
114
141
 
115
- def omap(method = nil, &b)
116
- if method
117
- map(&method)
118
- else
119
- map {|x| x.instance_eval(&b)}
120
- end
121
- end
122
-
123
- def oselect(method = nil, &b)
124
- if method
125
- select(&method)
126
- else
127
- select {|x| x.instance_eval(&b)}
128
- end
129
- end
130
-
131
- def ofind(method=nil, &b)
132
- if method
133
- find(&method)
134
- else
135
- find {|x| x.instance_eval(&b)}
136
- end
137
- end
138
-
139
142
  def search(not_found=nil)
140
143
  each do |x|
141
144
  val = yield(x)
@@ -144,22 +147,6 @@ module Enumerable
144
147
  not_found
145
148
  end
146
149
 
147
- def oany?(method=nil, &b)
148
- if method
149
- any?(&method)
150
- else
151
- any? {|x| x.instance_eval(&b)}
152
- end
153
- end
154
-
155
- def oall?(method=nil, &b)
156
- if method
157
- all?(&method)
158
- else
159
- all? {|x| x.instance_eval(&b)}
160
- end
161
- end
162
-
163
150
  def every(proc)
164
151
  map(&proc)
165
152
  end
@@ -16,7 +16,7 @@ module Hobo
16
16
  field_types.index(type)
17
17
  end
18
18
 
19
- def type_name(type)
19
+ def type_id(type)
20
20
  symbolic_type_name(type) || type.name.underscore.gsub("/", "__")
21
21
  end
22
22
 
@@ -52,7 +52,7 @@ module Hobo
52
52
  end
53
53
  @models_loaded = true
54
54
  end
55
- @models.omap{constantize}
55
+ @models.every(:constantize)
56
56
  end
57
57
 
58
58
 
@@ -64,21 +64,25 @@ module Hobo
64
64
  def object_from_dom_id(dom_id)
65
65
  return nil if dom_id == 'nil'
66
66
 
67
- _, name, id, attr = *dom_id.match(/^([a-z_]+)_([0-9]+(?:_[0-9]+)*)(_[a-z_]+)?$/)
67
+ _, name, id, attr = *dom_id.match(/^([a-z_]+)(?:_([0-9]+(?:_[0-9]+)*))?(?:_([a-z_]+))?$/)
68
68
  raise ArgumentError.new("invalid model-reference in dom id") unless name
69
69
  if name
70
70
  model_class = name.camelize.constantize rescue (raise ArgumentError.new("no such class in dom-id"))
71
71
  return nil unless model_class
72
- attr = attr[1..-1] if attr
73
- obj = if false and attr and model_class.reflections[attr.to_sym].klass.superclass == ActiveRecord::Base
74
- # DISABLED - Eager loading is broken - doesn't support ordering
75
- # http://dev.rubyonrails.org/ticket/3438
76
- # Don't do this for STI subclasses - it breaks!
77
- model_class.find(id, :include => attr)
78
- else
79
- model_class.find(id)
80
- end
81
- attr ? obj.send(attr) : obj
72
+
73
+ if id
74
+ obj = if false and attr and model_class.reflections[attr.to_sym].klass.superclass == ActiveRecord::Base
75
+ # DISABLED - Eager loading is broken - doesn't support ordering
76
+ # http://dev.rubyonrails.org/ticket/3438
77
+ # Don't do this for STI subclasses - it breaks!
78
+ model_class.find(id, :include => attr)
79
+ else
80
+ model_class.find(id)
81
+ end
82
+ attr ? obj.send(attr) : obj
83
+ else
84
+ model_class
85
+ end
82
86
  end
83
87
  end
84
88
 
@@ -91,6 +95,8 @@ module Hobo
91
95
  if obj.is_a?(Array) and obj.respond_to?(:proxy_owner)
92
96
  attr = obj.proxy_reflection.name
93
97
  obj = obj.proxy_owner
98
+ elsif obj.is_a?(Class)
99
+ return type_id(obj)
94
100
  elsif !obj.respond_to?(:typed_id)
95
101
  if attr
96
102
  return dom_id(get_field(obj, attr))
@@ -133,7 +139,13 @@ module Hobo
133
139
  end
134
140
 
135
141
  def add_routes(map)
136
- ActiveRecord::Base.connection.reconnect! unless ActiveRecord::Base.connection.active?
142
+ begin
143
+ ActiveRecord::Base.connection.reconnect! unless ActiveRecord::Base.connection.active?
144
+ rescue
145
+ # No database, no routes
146
+ return
147
+ end
148
+
137
149
  require "#{RAILS_ROOT}/app/controllers/application" unless Object.const_defined? :ApplicationController
138
150
  require "#{RAILS_ROOT}/app/assemble.rb" if File.exists? "#{RAILS_ROOT}/app/assemble.rb"
139
151
 
@@ -150,9 +162,8 @@ module Hobo
150
162
  map.connect "#{web_name}/:id", :controller => web_name, :action => 'show'
151
163
 
152
164
  elsif controller < Hobo::ModelController
153
-
154
165
  map.resources web_name, :collection => { :completions => :get }
155
-
166
+
156
167
  for collection in controller.collections
157
168
  new_method = Hobo.simple_has_many_association?(model.reflections[collection])
158
169
  Hobo.add_collection_routes(map, web_name, collection, new_method)
@@ -173,6 +184,15 @@ module Hobo
173
184
  :action => view.to_s,
174
185
  :conditions => { :method => :get })
175
186
  end
187
+
188
+ if controller < Hobo::UserController
189
+ map.named_route("#{web_name.singularize}_login", "#{web_name.singularize}_login",
190
+ :controller => web_name, :action => 'login')
191
+ map.named_route("#{web_name.singularize}_logout", "#{web_name.singularize}_logout",
192
+ :controller => web_name, :action => 'logout')
193
+ map.named_route("#{web_name.singularize}_signup", "#{web_name.singularize}_signup",
194
+ :controller => web_name, :action => 'signup')
195
+ end
176
196
  end
177
197
  end
178
198
  end
@@ -337,7 +357,7 @@ module Hobo
337
357
  else
338
358
  File.join(File.dirname(__FILE__), "hobo/static_tags")
339
359
  end
340
- File.readlines(path).omap{chop}
360
+ File.readlines(path).every(:chop)
341
361
  end
342
362
  end
343
363
 
@@ -36,12 +36,17 @@ module Hobo
36
36
  #
37
37
  # skip_before_filter :login_required
38
38
  #
39
- def login_required
40
- if current_user.guest?
39
+ def login_required(user_model=nil)
40
+ if current_user.guest?
41
+ auth_model = user_model || UserController.user_models.first
41
42
  username, passwd = get_auth_data
42
- self.current_user = Hobo.user_model.authenticate(username, passwd) || :false if username && passwd
43
+ self.current_user = auth_model.authenticate(username, passwd) || :false if username && passwd && auth_model
44
+ end
45
+ if logged_in? && authorized? && (user_model.nil? || current_user.is_a?(user_model))
46
+ true
47
+ else
48
+ access_denied
43
49
  end
44
- logged_in? && authorized? ? true : access_denied
45
50
  end
46
51
 
47
52
  # Redirect as appropriate when an access request fails.
@@ -84,16 +89,22 @@ module Hobo
84
89
  # When called with before_filter :login_from_cookie will check for an :auth_token
85
90
  # cookie and log the user back in if apropriate
86
91
  def login_from_cookie
87
- return unless cookies[:auth_token] && !logged_in?
92
+ return unless (token = cookies[:auth_token]) && !logged_in?
88
93
 
89
- user = Hobo.user_model.find_by_remember_token(cookies[:auth_token])
94
+ user_model = token[:user_model].constantize
95
+ user = user_model.find_by_remember_token(token)
90
96
  if user && user.remember_token?
91
97
  user.remember_me
92
- self.current_user = user
93
- cookies[:auth_token] = { :value => self.current_user.remember_token ,
94
- :expires => self.current_user.remember_token_expires_at }
98
+ current_user = user
99
+ create_auth_cookie
95
100
  end
96
101
  end
102
+
103
+ def create_auth_cookie
104
+ cookies[:auth_token] = { :value => current_user.remember_token ,
105
+ :expires => current_user.remember_token_expires_at,
106
+ :user_model => current_user.model }
107
+ end
97
108
 
98
109
  private
99
110
  @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
@@ -101,7 +112,11 @@ module Hobo
101
112
  def get_auth_data
102
113
  auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
103
114
  auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
104
- return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
115
+ username, pw = if auth_data && auth_data[0] == 'Basic'
116
+ Base64.decode64(auth_data[1]).split(':')[0..1]
117
+ else
118
+ [nil, nil]
119
+ end
105
120
  end
106
121
 
107
122
  end
@@ -12,7 +12,7 @@ module Hobo
12
12
  end
13
13
 
14
14
  def compose(*models)
15
- @models = models.omap{to_s.camelize}
15
+ @models = models.map &it.to_s.camelize
16
16
  attr_reader *models
17
17
  CompositeModel.composites ||= {}
18
18
  CompositeModel.composites[@models.sort] = self.name
@@ -57,7 +57,7 @@ module Hobo
57
57
 
58
58
  def id
59
59
  objects = self.class.models.map {|m| instance_variable_get("@#{m.underscore}")}
60
- objects.omap{id}.join("_")
60
+ objects.every(:id).join("_")
61
61
  end
62
62
 
63
63
  end
@@ -56,7 +56,6 @@ module Hobo
56
56
 
57
57
 
58
58
  def ajax_update_response(this, part_page, render_specs, results={})
59
- before_ajax if respond_to? :before_ajax
60
59
  add_variables_to_assigns
61
60
  renderer = Hobo::Dryml.page_renderer(@template, [], part_page) if part_page
62
61
 
@@ -64,45 +63,20 @@ module Hobo
64
63
  page << "var _update = typeof Hobo == 'undefined' ? Element.update : Hobo.updateElement;"
65
64
  for spec in render_specs
66
65
  function = spec[:function] || "_update"
67
-
68
- if spec[:as] or spec[:part]
69
- obj = if spec[:object] == "this" or spec[:object].blank?
70
- this
71
- elsif spec[:object] == "nil"
72
- nil
73
- else
74
- Hobo.object_from_dom_id(spec[:object])
75
- end
76
-
77
- if spec[:as]
78
- part_content = render(:partial => (Hobo::ModelController.find_partial(obj.class, spec[:as])),
79
- :locals => { :this => obj })
80
- page.call(function, spec[:id], part_content)
81
-
82
- elsif spec[:part]
83
- dom_id = spec[:id] || spec[:part]
84
- part_content = renderer.call_part(dom_id, spec[:part], obj)
85
- page.call(function, dom_id, part_content)
86
- end
87
-
66
+ dom_id = spec[:id]
67
+
68
+ if spec[:part_context]
69
+ part_name, part_this, locals = Dryml::PartContext.unmarshal(spec[:part_context], this, session)
70
+ part_content = renderer.call_part(dom_id, part_name, part_this, *locals)
71
+ page.call(function, dom_id, part_content)
88
72
  elsif spec[:result]
89
73
  result = results[spec[:result].to_sym]
90
74
  page.call(function, spec[:id], result)
91
-
92
75
  else
93
76
  # spec didn't specify any action :-/
94
77
  end
95
78
  end
96
- if renderer
97
- renderer.part_contexts.each_pair do |dom_id, p|
98
- part_id, model_id = p
99
- page.assign "hoboParts.#{dom_id}", [part_id, model_id]
100
-
101
- # not sure why this isn't happending automatically
102
- # but it's messing up ARTS, so chuck a newline in
103
- page << "\n"
104
- end
105
- end
79
+ page << renderer.part_contexts_storage if renderer
106
80
  end
107
81
  end
108
82
 
@@ -140,7 +114,7 @@ module Hobo
140
114
 
141
115
  # Store the given user in the session.
142
116
  def current_user=(new_user)
143
- session[:user] = (new_user.nil? || new_user.is_a?(Symbol)) ? nil : new_user.id
117
+ session[:user] = (new_user.nil? || new_user.is_a?(Symbol)) ? nil : new_user.typed_id
144
118
  @current_user = new_user
145
119
  end
146
120
 
@@ -50,7 +50,7 @@ module Hobo::Dryml
50
50
  ("def render_page(__page_this__, __local_assigns__); " +
51
51
  "#{locals} new_object_context(__page_this__) do " +
52
52
  src +
53
- "; _erbout; end + part_contexts_js; end")
53
+ "; _erbout; end + part_contexts_storage_tag; end")
54
54
  end
55
55
 
56
56
 
@@ -0,0 +1,103 @@
1
+ module Hobo
2
+
3
+ module Dryml
4
+
5
+ # Raised when the part context fails its integrity check.
6
+ class PartContext
7
+
8
+ class TamperedWithPartContext < StandardError; end
9
+
10
+ class Id < String; end
11
+
12
+ class << self
13
+ attr_accessor :secret, :digest
14
+ end
15
+ self.digest = 'SHA1'
16
+
17
+
18
+ def self.client_side_storage(contexts, session)
19
+ return "" if contexts.empty?
20
+
21
+ contexts.map do |dom_id, context|
22
+ code = context.marshal(session).split("\n").map{|line| "'#{line}\\n'"}.join(" +\n ")
23
+ "hoboParts.#{dom_id} = (#{code})\n"
24
+ end.join
25
+ end
26
+
27
+
28
+ def initialize(part_name, this_id, locals)
29
+ @part_name = part_name
30
+ @this_id = this_id
31
+ @locals = locals.map {|l| pre_marshal(l) }
32
+ end
33
+
34
+
35
+ def pre_marshal(x)
36
+ if x.is_a?(ActiveRecord::Base) && x.respond_to?(:typed_id)
37
+ Id.new(x.typed_id)
38
+ else
39
+ x
40
+ end
41
+ end
42
+
43
+
44
+ def marshal(session)
45
+ context = [@part_name, @this_id, @locals]
46
+ puts "STORE: " + context.inspect
47
+ data = Base64.encode64(Marshal.dump(context)).strip
48
+ "#{data}--#{self.class.generate_digest(data, session)}"
49
+ end
50
+
51
+
52
+ class << self
53
+
54
+ # Generate the HMAC keyed message digest. Uses SHA1 by default.
55
+ def generate_digest(data, session)
56
+ secret = self.secret || ActionController::Base.cached_session_options.first[:secret]
57
+ key = secret.respond_to?(:call) ? secret.call(session) : secret
58
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(digest), key, data)
59
+ end
60
+
61
+
62
+ # Unmarshal part context to a hash and verify its integrity.
63
+ def unmarshal(client_store, this, session)
64
+ data, digest = CGI.unescape(client_store).strip.split('--')
65
+
66
+ raise TamperedWithPartContext unless digest == generate_digest(data, session)
67
+
68
+ part_name, this_id, locals = Marshal.load(Base64.decode64(data))
69
+ puts "RESTORE: " + [part_name, this_id, locals].inspect
70
+
71
+ x = [part_name, restore_part_this(this_id, this), restore_locals(locals)]
72
+ puts this_id, x.inspect
73
+ x
74
+ end
75
+
76
+ def restore_part_this(this_id, this)
77
+ if this_id == "this" or this_id.blank?
78
+ this
79
+ elsif this_id == "nil"
80
+ nil
81
+ else
82
+ Hobo.object_from_dom_id(this_id)
83
+ end
84
+ end
85
+
86
+
87
+ def restore_locals(locals)
88
+ locals.map do |l|
89
+ if l.is_a?(Id)
90
+ Hobo.object_from_dom_id(l)
91
+ else
92
+ l
93
+ end
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+
103
+ end