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.
- data/bin/hobo +2 -3
- data/hobo_files/plugin/CHANGES.txt +139 -0
- data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +1 -8
- data/hobo_files/plugin/generators/hobo_front_controller/templates/controller.rb +1 -39
- data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +2 -2
- data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +27 -7
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +1 -2
- data/hobo_files/plugin/generators/hobo_user_controller/USAGE +34 -0
- data/hobo_files/plugin/generators/hobo_user_controller/hobo_user_controller_generator.rb +43 -0
- data/hobo_files/plugin/generators/hobo_user_controller/templates/controller.rb +5 -0
- data/hobo_files/plugin/generators/hobo_user_controller/templates/functional_test.rb +18 -0
- data/hobo_files/plugin/generators/hobo_user_controller/templates/helper.rb +2 -0
- data/hobo_files/plugin/generators/hobo_user_controller/templates/view.rhtml +2 -0
- data/hobo_files/plugin/init.rb +6 -2
- data/hobo_files/plugin/lib/extensions.rb +28 -41
- data/hobo_files/plugin/lib/hobo.rb +37 -17
- data/hobo_files/plugin/lib/hobo/authentication_support.rb +25 -10
- data/hobo_files/plugin/lib/hobo/composite_model.rb +2 -2
- data/hobo_files/plugin/lib/hobo/controller.rb +8 -34
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +1 -1
- data/hobo_files/plugin/lib/hobo/dryml/part_context.rb +103 -0
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +10 -11
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +29 -72
- data/hobo_files/plugin/lib/hobo/hobo_helper.rb +11 -10
- data/hobo_files/plugin/lib/hobo/migrations.rb +12 -0
- data/hobo_files/plugin/lib/hobo/model.rb +3 -3
- data/hobo_files/plugin/lib/hobo/model_controller.rb +3 -3
- data/hobo_files/plugin/lib/hobo/rapid_helper.rb +3 -3
- data/hobo_files/plugin/lib/hobo/user_controller.rb +80 -0
- data/hobo_files/plugin/tags/rapid.dryml +36 -20
- data/hobo_files/plugin/tags/rapid_editing.dryml +4 -5
- data/hobo_files/plugin/tags/rapid_forms.dryml +6 -6
- data/hobo_files/plugin/tags/rapid_navigation.dryml +7 -5
- data/hobo_files/plugin/tags/rapid_pages.dryml +116 -10
- data/hobo_files/plugin/tags/rapid_support.dryml +23 -0
- metadata +13 -5
- data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +0 -42
- data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +0 -43
- 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
|
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.
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
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).
|
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 =
|
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
|
-
|
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
|
-
|
93
|
-
|
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
|
-
|
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.
|
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.
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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.
|
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
|
|
@@ -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
|