hobo 0.6 → 0.6.1

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