hobo 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +22 -0
- data/README.txt +18 -0
- data/bin/hobo +81 -0
- data/hobo_files/plugin/CHANGES.txt +963 -0
- data/hobo_files/plugin/LICENSE.txt +22 -0
- data/hobo_files/plugin/README +4 -0
- data/hobo_files/plugin/Rakefile +11 -0
- data/hobo_files/plugin/generators/hobo/hobo_generator.rb +37 -0
- data/hobo_files/plugin/generators/hobo/templates/application.dryml +2 -0
- data/hobo_files/plugin/generators/hobo/templates/guest.rb +31 -0
- data/hobo_files/plugin/generators/hobo_front_controller/USAGE +11 -0
- data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +90 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/controller.rb +51 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/functional_test.rb +18 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/helper.rb +2 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +43 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +44 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/search.dryml +18 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +45 -0
- data/hobo_files/plugin/generators/hobo_model/USAGE +26 -0
- data/hobo_files/plugin/generators/hobo_model/hobo_model_generator.rb +38 -0
- data/hobo_files/plugin/generators/hobo_model/templates/fixtures.yml +11 -0
- data/hobo_files/plugin/generators/hobo_model/templates/migration.rb +13 -0
- data/hobo_files/plugin/generators/hobo_model/templates/model.rb +24 -0
- data/hobo_files/plugin/generators/hobo_model/templates/unit_test.rb +10 -0
- data/hobo_files/plugin/generators/hobo_model_controller/USAGE +30 -0
- data/hobo_files/plugin/generators/hobo_model_controller/hobo_model_controller_generator.rb +43 -0
- data/hobo_files/plugin/generators/hobo_model_controller/templates/controller.rb +5 -0
- data/hobo_files/plugin/generators/hobo_model_controller/templates/functional_test.rb +18 -0
- data/hobo_files/plugin/generators/hobo_model_controller/templates/helper.rb +2 -0
- data/hobo_files/plugin/generators/hobo_model_controller/templates/view.rhtml +2 -0
- data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +51 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +436 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/default_mapping.rb +11 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/banner.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_bodytop.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_01.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_02.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_03.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_04.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_bottom.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_left.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_right.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_top.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_blue.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_dblue.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_green.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_purple.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_red.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/logo.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/spinner.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_dblue.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_green.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_purple.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_red.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_01.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_02.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_03.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_04.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_bottom.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_left.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_right.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_top.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/stylesheets/application.css +390 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +104 -0
- data/hobo_files/plugin/generators/hobo_user_model/USAGE +26 -0
- data/hobo_files/plugin/generators/hobo_user_model/hobo_user_model_generator.rb +38 -0
- data/hobo_files/plugin/generators/hobo_user_model/templates/fixtures.yml +11 -0
- data/hobo_files/plugin/generators/hobo_user_model/templates/migration.rb +15 -0
- data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +58 -0
- data/hobo_files/plugin/generators/hobo_user_model/templates/unit_test.rb +10 -0
- data/hobo_files/plugin/init.rb +44 -0
- data/hobo_files/plugin/lib/action_view_extensions/base.rb +14 -0
- data/hobo_files/plugin/lib/active_record/has_many_association.rb +54 -0
- data/hobo_files/plugin/lib/active_record/has_many_through_association.rb +22 -0
- data/hobo_files/plugin/lib/active_record/table_definition.rb +34 -0
- data/hobo_files/plugin/lib/extensions.rb +245 -0
- data/hobo_files/plugin/lib/extensions/test_case.rb +130 -0
- data/hobo_files/plugin/lib/hobo.rb +353 -0
- data/hobo_files/plugin/lib/hobo/HtmlString +3 -0
- data/hobo_files/plugin/lib/hobo/authenticated_user.rb +106 -0
- data/hobo_files/plugin/lib/hobo/authentication_support.rb +108 -0
- data/hobo_files/plugin/lib/hobo/composite_model.rb +66 -0
- data/hobo_files/plugin/lib/hobo/controller.rb +134 -0
- data/hobo_files/plugin/lib/hobo/controller_helpers.rb +135 -0
- data/hobo_files/plugin/lib/hobo/core.rb +475 -0
- data/hobo_files/plugin/lib/hobo/define_tags.rb +56 -0
- data/hobo_files/plugin/lib/hobo/dryml.rb +161 -0
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +126 -0
- data/hobo_files/plugin/lib/hobo/dryml/tag_module.rb +9 -0
- data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +57 -0
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +586 -0
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +302 -0
- data/hobo_files/plugin/lib/hobo/dryml/template_handler.rb +19 -0
- data/hobo_files/plugin/lib/hobo/generator.rb +25 -0
- data/hobo_files/plugin/lib/hobo/html_string.rb +3 -0
- data/hobo_files/plugin/lib/hobo/lazy_hash.rb +28 -0
- data/hobo_files/plugin/lib/hobo/mapping_tags.rb +262 -0
- data/hobo_files/plugin/lib/hobo/markdown_string.rb +7 -0
- data/hobo_files/plugin/lib/hobo/model.rb +391 -0
- data/hobo_files/plugin/lib/hobo/model_controller.rb +676 -0
- data/hobo_files/plugin/lib/hobo/model_queries.rb +92 -0
- data/hobo_files/plugin/lib/hobo/model_support.rb +44 -0
- data/hobo_files/plugin/lib/hobo/password_string.rb +3 -0
- data/hobo_files/plugin/lib/hobo/predicate_dispatch.rb +78 -0
- data/hobo_files/plugin/lib/hobo/proc_binding.rb +32 -0
- data/hobo_files/plugin/lib/hobo/rapid.rb +447 -0
- data/hobo_files/plugin/lib/hobo/static_tags +92 -0
- data/hobo_files/plugin/lib/hobo/text.rb +3 -0
- data/hobo_files/plugin/lib/hobo/textile_string.rb +13 -0
- data/hobo_files/plugin/lib/hobo/undefined.rb +41 -0
- data/hobo_files/plugin/lib/hobo/undefined_access_error.rb +5 -0
- data/hobo_files/plugin/lib/hobo/where_fragment.rb +23 -0
- data/hobo_files/plugin/lib/rexml.rb +345 -0
- data/hobo_files/plugin/tags/core.dryml +6 -0
- data/hobo_files/plugin/tags/rapid.dryml +177 -0
- data/hobo_files/plugin/tags/rapid_editing.dryml +168 -0
- data/hobo_files/plugin/tags/rapid_navigation.dryml +95 -0
- data/hobo_files/plugin/tags/rapid_pages.dryml +175 -0
- data/hobo_files/plugin/tasks/environments.rake +19 -0
- data/hobo_files/plugin/tasks/hobo_tasks.rake +4 -0
- data/hobo_files/plugin/test/hobo_dryml_template_test.rb +7 -0
- data/hobo_files/plugin/test/hobo_test.rb +7 -0
- data/hobo_files/plugin/uninstall.rb +1 -0
- metadata +206 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
module Hobo
|
2
|
+
|
3
|
+
class ModelQueries
|
4
|
+
|
5
|
+
def initialize(model)
|
6
|
+
@model = model
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
attr_reader :model
|
11
|
+
|
12
|
+
def not_(fragment)
|
13
|
+
WhereFragment.new("not (#{fragment.to_sql})")
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def is_in(association)
|
18
|
+
association = Hobo.object_from_dom_id(association) if association.is_a? String
|
19
|
+
refl = association.proxy_reflection
|
20
|
+
raise HoboError.new("association #{refl.name} is not a collection of #{model.name.pluralize}") unless
|
21
|
+
refl.klass == model and refl.macro == :has_many
|
22
|
+
|
23
|
+
WhereFragment.new(_association_finder_sql(association))
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def not_in(association)
|
28
|
+
not_(is_in(association))
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def method_missing(name, *args)
|
33
|
+
check_column = proc do |col|
|
34
|
+
raise HoboError.new("no such column '#{col}' in query") unless
|
35
|
+
model.columns.every(:name).include? col
|
36
|
+
end
|
37
|
+
|
38
|
+
m, field = *name.to_s.match(/^(.*)_is$/)
|
39
|
+
if m
|
40
|
+
if (refl = model.reflections[field.to_sym]) and refl.macro == :belongs_to
|
41
|
+
field = refl.primary_key_name
|
42
|
+
val = args[0] && args[0].id
|
43
|
+
raise HoboError.new("don't use self in query blocks") if val == self
|
44
|
+
else
|
45
|
+
check_column[field]
|
46
|
+
val = args[0]
|
47
|
+
end
|
48
|
+
return (if val.nil?
|
49
|
+
WhereFragment.new("#{field} IS NULL")
|
50
|
+
else
|
51
|
+
WhereFragment.new("#{field} = ?", val)
|
52
|
+
end)
|
53
|
+
end
|
54
|
+
|
55
|
+
m, field = *name.to_s.match(/^(.*)_contains$/)
|
56
|
+
if m
|
57
|
+
check_column[field]
|
58
|
+
return WhereFragment.new("#{field} like ?", "%#{args[0]}%")
|
59
|
+
end
|
60
|
+
|
61
|
+
m, field = *name.to_s.match(/^(.*)_starts$/)
|
62
|
+
if m
|
63
|
+
check_column[field]
|
64
|
+
return WhereFragment.new("#{field} like ?", "#{args[0]}%")
|
65
|
+
end
|
66
|
+
|
67
|
+
m, field = *name.to_s.match(/^(.*)_ends$/)
|
68
|
+
if m
|
69
|
+
check_column[field]
|
70
|
+
return WhereFragment.new("#{field} like ?", "%#{args[0]}")
|
71
|
+
end
|
72
|
+
|
73
|
+
return WhereFragment.new(@model.send(name, *args))
|
74
|
+
end
|
75
|
+
|
76
|
+
def _association_finder_sql(assoc)
|
77
|
+
refl = assoc.proxy_reflection
|
78
|
+
if refl.through_reflection
|
79
|
+
conditions = assoc.send(:construct_conditions)
|
80
|
+
from = assoc.send(:construct_from)
|
81
|
+
joins = assoc.send(:construct_joins)
|
82
|
+
|
83
|
+
table = "#{model.table_name}"
|
84
|
+
"id in (select #{table}.id from #{table} #{joins} where #{conditions})"
|
85
|
+
else
|
86
|
+
Object.instance_method(:instance_variable_get).bind(assoc).call("@finder_sql")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Hobo
|
2
|
+
|
3
|
+
module ModelSupport
|
4
|
+
|
5
|
+
# This module provides methods common to both Hobo::Model and Hobo::CompositeModel
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods) if base.is_a? Class
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
def delegate_and_compose(*methods)
|
14
|
+
options = methods.pop
|
15
|
+
unless options.is_a?(Hash) && to = options[:to]
|
16
|
+
raise ArgumentError, ("Delegation needs a target. Supply an options hash " +
|
17
|
+
"with a :to key as the last argument (e.g. delegate :hello, :to => :greeter).")
|
18
|
+
end
|
19
|
+
use = options[:use]
|
20
|
+
|
21
|
+
methods.each do |method|
|
22
|
+
module_eval(<<-EOS, "(__COMPOSED_DELEGATION__)", 1)
|
23
|
+
def #{method}
|
24
|
+
@__#{method}_result__ ||= begin
|
25
|
+
obj = #{to}.__send__(#{method.inspect})
|
26
|
+
return nil if obj.nil?
|
27
|
+
|
28
|
+
if obj.nil?
|
29
|
+
nil
|
30
|
+
elsif obj.is_a?(Array)
|
31
|
+
obj.map {|o| self.compose_with(o, #{use.inspect})}
|
32
|
+
else
|
33
|
+
self.compose_with(obj, #{use.inspect})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
EOS
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Hobo::PredicateDispatch
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def default_method_for(method)
|
10
|
+
"_default_#{method}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def defp(name, predicate=nil, &block)
|
14
|
+
ivar = "@@#{name}_predicates"
|
15
|
+
default_method_name = default_method_for(name).to_sym
|
16
|
+
methods = ivar.in?(class_variables) && class_variable_get(ivar)
|
17
|
+
|
18
|
+
if !methods
|
19
|
+
# It's the first defp for this name...
|
20
|
+
|
21
|
+
methods = class_variable_set(ivar, [])
|
22
|
+
|
23
|
+
if name.to_s.in?(instance_methods) and predicate
|
24
|
+
# There's already a normal method with this name - it
|
25
|
+
# becomes the default.
|
26
|
+
alias_method default_method_name, name
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
if predicate
|
32
|
+
pname = "#{name}_p#{methods.length}"
|
33
|
+
methods << [pname, predicate.arity == 1, block.arity == 2]
|
34
|
+
define_method(pname, block)
|
35
|
+
define_method(pname + "_predicate", &predicate)
|
36
|
+
else
|
37
|
+
# It's the default - if there's already one, overwrite it
|
38
|
+
define_method(default_method_name, block)
|
39
|
+
end
|
40
|
+
|
41
|
+
module_eval <<-END, __FILE__, __LINE__+1
|
42
|
+
def #{name}(options={}, &block)
|
43
|
+
methods = #{ivar}
|
44
|
+
for method_name, predicate_wants_options, wants_block in methods
|
45
|
+
pred = method_name + "_predicate"
|
46
|
+
if (predicate_wants_options ? send(pred, options) : send(pred))
|
47
|
+
return (if wants_block
|
48
|
+
send(method_name, options, block)
|
49
|
+
else
|
50
|
+
send(method_name, options)
|
51
|
+
end)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
if respond_to?(:#{default_method_name})
|
55
|
+
if method(:#{default_method_name}).arity == 2
|
56
|
+
#{default_method_name}(options, block)
|
57
|
+
else
|
58
|
+
#{default_method_name}(options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
END
|
63
|
+
end
|
64
|
+
|
65
|
+
alias :p :proc
|
66
|
+
|
67
|
+
def predicate_method?(method_name)
|
68
|
+
"@@#{method_name}_predicates".in?(class_variables)
|
69
|
+
end
|
70
|
+
|
71
|
+
def predicate_has_default?(method_name)
|
72
|
+
default_method_for(method_name).in?(instance_methods)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Hobo
|
2
|
+
|
3
|
+
class ProcBinding
|
4
|
+
|
5
|
+
def self.eval(proc, self_, locals)
|
6
|
+
ProcBinding.new(self_, locals).instance_eval(&proc)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(self_, locals)
|
10
|
+
@self_ = self_
|
11
|
+
@locals = locals
|
12
|
+
locals.symbolize_keys!
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *args, &block)
|
16
|
+
if @locals.has_key?(name)
|
17
|
+
@locals[name]
|
18
|
+
else
|
19
|
+
@self_.send(name, *args, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
(Object.instance_methods +
|
26
|
+
Object.private_instance_methods +
|
27
|
+
Object.protected_instance_methods).each do |m|
|
28
|
+
ProcBinding.send(:undef_method, m) unless
|
29
|
+
%w{initialize method_missing send instance_eval}.include?(m) || m.starts_with?('_')
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,447 @@
|
|
1
|
+
module Hobo::Rapid
|
2
|
+
|
3
|
+
include Hobo::DefineTags
|
4
|
+
|
5
|
+
TYPE_NAMES = {
|
6
|
+
Hobo::HtmlString => :html,
|
7
|
+
Hobo::Text => :textarea,
|
8
|
+
TrueClass => :boolean,
|
9
|
+
FalseClass => :boolean,
|
10
|
+
Date => :date,
|
11
|
+
Time => :datetime,
|
12
|
+
Hobo::PasswordString => :password_string,
|
13
|
+
Fixnum => :integer,
|
14
|
+
BigDecimal => :integer,
|
15
|
+
Float => :float,
|
16
|
+
String => :string
|
17
|
+
}
|
18
|
+
|
19
|
+
def options_for_hobo_ajax(options)
|
20
|
+
js_options = build_callbacks(options)
|
21
|
+
|
22
|
+
js_options['asynchronous'] = false if options[:type] == :synchronous
|
23
|
+
js_options['method'] = method_option_to_s(options[:method]) if options[:method]
|
24
|
+
js_options['evalScripts'] = false if options[:script] == false
|
25
|
+
js_options['form'] = options[:form] if options[:form]
|
26
|
+
js_options['params'] = make_params_js(options[:params]) if options[:params]
|
27
|
+
js_options['resultUpdate'] = js_result_updates(options[:result_update]) if options[:result_update]
|
28
|
+
js_options['resetForm'] = false if options[:reset_form] == false
|
29
|
+
js_options['refocusForm'] = false if options[:refocus_form] == false
|
30
|
+
|
31
|
+
js_options.empty? ? nil : options_for_javascript(js_options)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def js_updates(updates)
|
36
|
+
return '[]' unless updates
|
37
|
+
updates = [updates] unless updates.is_a? Array
|
38
|
+
'[' + comma_split(updates).map{|u| js_str(u)}.join(', ') + ']'
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def js_result_updates(updates)
|
43
|
+
return '[]' unless updates
|
44
|
+
updates = [updates] unless updates.is_a? Array
|
45
|
+
pairs = comma_split(updates).omap{split(/\s*=\s*/)}
|
46
|
+
'[' + pairs.map{|p| "{id: #{js_str(p[0])}, result: #{js_str(p[1])}}"}.join(", ") + ']'
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def ajax_updater(url_or_form, message, update, options={})
|
51
|
+
options ||= {}
|
52
|
+
target = if url_or_form == :post_form
|
53
|
+
target = "this"
|
54
|
+
else
|
55
|
+
js_str(url_or_form)
|
56
|
+
end
|
57
|
+
js_options = options_for_hobo_ajax(options)
|
58
|
+
args = [target, js_str(message || "..."), js_updates(update), js_options].compact
|
59
|
+
|
60
|
+
confirm = options.delete(:confirm)
|
61
|
+
|
62
|
+
func = "Hobo.ajaxRequest(#{args * ', '})"
|
63
|
+
if confirm
|
64
|
+
"if (confirm(#{js_str(confirm)})) { #{func} }"
|
65
|
+
else
|
66
|
+
func
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def a_or_an(word)
|
72
|
+
if word =~ /^[aeiouh]/
|
73
|
+
"an #{word}"
|
74
|
+
else
|
75
|
+
"a #{word}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def no_break(s)
|
81
|
+
s = new_context { yield } if block_given?
|
82
|
+
s.gsub(' ', ' ')
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def_tag :if_current_user_in do
|
87
|
+
if_(:q => current_user_in?(this)) { tagbody.call }
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def_tag :edit, :in_place do
|
92
|
+
if can_view_this?
|
93
|
+
if not can_edit_this?
|
94
|
+
show
|
95
|
+
elsif this_parent.new_record? or in_place == false
|
96
|
+
form_field(options)
|
97
|
+
else
|
98
|
+
editor(options)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def type_name(klass)
|
105
|
+
TYPE_NAMES[klass]
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def_tag :form_field do
|
110
|
+
raise HoboError.new("Not allowed to edit") unless can_edit_this?
|
111
|
+
if this_type.respond_to?(:macro)
|
112
|
+
if this_type.macro == :belongs_to
|
113
|
+
belongs_to_field(options)
|
114
|
+
elsif this_type.macro == :has_many
|
115
|
+
has_many_field(options)
|
116
|
+
end
|
117
|
+
|
118
|
+
else
|
119
|
+
tag = type_name(this_type).to_s + "_field"
|
120
|
+
if respond_to?(tag)
|
121
|
+
options[:name] ||= param_name_for_this
|
122
|
+
tag_src = send(tag, options)
|
123
|
+
if this_parent.errors[this_field]
|
124
|
+
"<div class='field_with_errors'>#{tag_src}</div>"
|
125
|
+
else
|
126
|
+
tag_src
|
127
|
+
end
|
128
|
+
else
|
129
|
+
raise HoboError, ("No form field tag for #{this_field}:#{this_type} (this=#{this.inspect})")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def_tag :has_many_field do
|
135
|
+
raise NotImplementedError, "form field for has_many associations not implemented"
|
136
|
+
end
|
137
|
+
|
138
|
+
def_tag :belongs_to_field do
|
139
|
+
belongs_to_menu_field(options)
|
140
|
+
end
|
141
|
+
|
142
|
+
def_tag :textarea_field, :name do
|
143
|
+
text_area_tag(name, this, options)
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def_tag :boolean_field, :name do
|
148
|
+
check_box_tag(name, '1', this, options)
|
149
|
+
end
|
150
|
+
|
151
|
+
def_tag :password_string_field, :name do
|
152
|
+
password_field_tag(name, this)
|
153
|
+
end
|
154
|
+
|
155
|
+
def_tag :html_field, :name do
|
156
|
+
text_area_tag(name, this, add_classes(options, :tiny_mce))
|
157
|
+
end
|
158
|
+
|
159
|
+
def_tag :date_field do
|
160
|
+
select_date(this || Time.now, :prefix => param_name_for_this)
|
161
|
+
end
|
162
|
+
|
163
|
+
def_tag :datetime_field do
|
164
|
+
select_datetime(this || Time.now, :prefix => param_name_for_this)
|
165
|
+
end
|
166
|
+
|
167
|
+
def_tag :integer_field, :name do
|
168
|
+
text_field_tag(name, this, options)
|
169
|
+
end
|
170
|
+
|
171
|
+
def_tag :float_field, :name do
|
172
|
+
text_field_tag(name, this, options)
|
173
|
+
end
|
174
|
+
|
175
|
+
def_tag :string_field, :name do
|
176
|
+
text_field_tag(name, this, options)
|
177
|
+
end
|
178
|
+
|
179
|
+
def_tag :editor do
|
180
|
+
raise HoboError.new("Not allowed to edit") unless can_edit_this?
|
181
|
+
|
182
|
+
if this_type.respond_to?(:macro)
|
183
|
+
if this_type.macro == :belongs_to
|
184
|
+
belongs_to_editor(options)
|
185
|
+
else
|
186
|
+
has_many_editor(options)
|
187
|
+
end
|
188
|
+
else
|
189
|
+
tag = type_name(this_type).to_s + "_editor"
|
190
|
+
if respond_to?(tag)
|
191
|
+
send(tag, options)
|
192
|
+
else
|
193
|
+
raise HoboError.new("<editor> not implemented for #{this.class.name}\##{this_field} " +
|
194
|
+
"(#{this.inspect}:#{this_type})")
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
def_tag :has_many_editor do
|
201
|
+
# TODO: Implement
|
202
|
+
object_link(options)
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
def in_place_editor(kind, tag, options)
|
207
|
+
opts = add_classes(options, kind, editor_class).merge(:hobo_model_id => this_field_dom_id)
|
208
|
+
|
209
|
+
update = opts.delete(:update)
|
210
|
+
blank_message = opts.delete(:blank_message) || "(click to edit)"
|
211
|
+
|
212
|
+
display = show(:no_span => true)
|
213
|
+
opts[:hobo_blank_message] = blank_message
|
214
|
+
display = blank_message if display.blank?
|
215
|
+
opts[:hobo_update] = update if update
|
216
|
+
content_tag(tag, display, opts)
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
def_tag :string_editor do
|
221
|
+
in_place_editor "in_place_textfield_bhv", :span, options
|
222
|
+
end
|
223
|
+
|
224
|
+
def_tag :textarea_editor do
|
225
|
+
in_place_editor "in_place_textarea_bhv", :div, options
|
226
|
+
end
|
227
|
+
|
228
|
+
def_tag :html_editor do
|
229
|
+
in_place_editor "in_place_html_textarea_bhv", :div, options
|
230
|
+
end
|
231
|
+
|
232
|
+
def_tag :belongs_to_editor do
|
233
|
+
belongs_to_menu_editor(options)
|
234
|
+
end
|
235
|
+
|
236
|
+
def_tag :datetime_editor do
|
237
|
+
string_editor(options)
|
238
|
+
end
|
239
|
+
|
240
|
+
def_tag :date_editor do
|
241
|
+
string_editor(options)
|
242
|
+
end
|
243
|
+
|
244
|
+
def_tag :integer_editor do
|
245
|
+
in_place_editor "in_place_textfield_bhv", :span, options
|
246
|
+
end
|
247
|
+
|
248
|
+
def_tag :float_editor do
|
249
|
+
in_place_editor "in_place_textfield_bhv", :span, options
|
250
|
+
end
|
251
|
+
|
252
|
+
def_tag :password_string_editor do
|
253
|
+
raise HoboError, "passwords cannot be edited in place"
|
254
|
+
end
|
255
|
+
|
256
|
+
def_tag :boolean_editor do
|
257
|
+
boolean_checkbox_editor(options)
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
AJAX_ATTRS = [:before, :success, :failure, :complete, :type, :method,
|
262
|
+
:script, :form, :params, :confirm,
|
263
|
+
:reset_form, :refocus_form]
|
264
|
+
|
265
|
+
|
266
|
+
def_tag :update_button, :label, :message, :attrs, :update, :params do
|
267
|
+
raise HoboError.new("no update specified") unless update
|
268
|
+
message2 = message || label
|
269
|
+
func = ajax_updater(object_url(this), message2, update,
|
270
|
+
:params => { this.class.name.underscore => attrs }.merge(params || {}),
|
271
|
+
:method => :put)
|
272
|
+
tag :input, add_classes(options.merge(:type =>'button', :onclick => func, :value => label),
|
273
|
+
"button_input update_button update_#{this.class.name.underscore}_button")
|
274
|
+
end
|
275
|
+
|
276
|
+
|
277
|
+
def_tag :delete_button, :label, :message, :update, :ajax, :else, :image, :confirm, :fade do
|
278
|
+
fade2 = fade.nil? ? true : fade
|
279
|
+
if can_delete?(this)
|
280
|
+
opts = options.merge(if image
|
281
|
+
{ :type => "image", :src => "#{urlb}/images/#{image}" }
|
282
|
+
else
|
283
|
+
{ :type => "button" }
|
284
|
+
end)
|
285
|
+
label2 = label || "Remove"
|
286
|
+
confirm2 = confirm || "Are you sure?"
|
287
|
+
|
288
|
+
add_classes!(opts,
|
289
|
+
image ? "image_button_input" : "button_input",
|
290
|
+
"delete_button delete_#{this.class.name.underscore}_button")
|
291
|
+
url = object_url(this, "destroy")
|
292
|
+
if ajax == false
|
293
|
+
opts[:confirm] = confirm2
|
294
|
+
button_to(label2, url, opts)
|
295
|
+
else
|
296
|
+
opts[:value] = label2
|
297
|
+
opts[:onclick] = "Hobo.removeButton(this, '#{url}', #{js_updates(update)}, #{fade2 ? 'true' : 'false'})"
|
298
|
+
tag(:input, opts)
|
299
|
+
end
|
300
|
+
else
|
301
|
+
else_
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
def_tag :create_button, :model, :update, :attrs, :label, :message, :else do
|
307
|
+
raise HoboError.new("no update specified") unless update
|
308
|
+
params = attrs || {}
|
309
|
+
if model
|
310
|
+
new = (model.is_a?(String) ? model.constantize : model).new(params)
|
311
|
+
else
|
312
|
+
raise HoboError.new("invalid context for <create_button>") unless Hobo.simple_has_many_association?(this)
|
313
|
+
params[this.proxy_reflection.primary_key_name] = this.proxy_owner.id
|
314
|
+
new = this.new(params)
|
315
|
+
end
|
316
|
+
if can_create?(new)
|
317
|
+
label2 = label || "New #{new.class.name.titleize}"
|
318
|
+
message2 = message || label2
|
319
|
+
class_name = new.class.name.underscore
|
320
|
+
func = ajax_updater(object_url(new.class), message2, update,
|
321
|
+
({:params => { class_name => params }} unless params.empty?))
|
322
|
+
tag :input, add_classes(options.merge(:type =>'button', :onclick => func, :value => label2),
|
323
|
+
"button_input create_button create_#{class_name}_button")
|
324
|
+
else
|
325
|
+
else_
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
def_tag :remote_method_button, :method, :update, :result_update, :params, :label, :message do
|
331
|
+
ajax_options, html_options = options.partition_hash(AJAX_ATTRS)
|
332
|
+
|
333
|
+
message2 = message || method.titleize
|
334
|
+
func = ajax_updater(object_url(this) + "/#{method}", message2, update,
|
335
|
+
ajax_options.merge(:params => params, :result_update => result_update))
|
336
|
+
html_options.update(:type =>'button', :onclick => "var e = this; " + func, :value => label)
|
337
|
+
tag(:input, add_classes(html_options, "button_input remote_method_button #{method}_button"))
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
def_tag :hobo_rapid_javascripts, :tiny_mce do
|
342
|
+
res = javascript_include_tag("hobo_rapid")
|
343
|
+
res += "<script>"
|
344
|
+
unless Hobo.all_controllers.empty?
|
345
|
+
res += "var controllerNames = {" +
|
346
|
+
Hobo.all_controllers.map {|c| "#{c.singularize}: '#{c}'"}.join(', ') +
|
347
|
+
"}; "
|
348
|
+
end
|
349
|
+
res += "urlBase = '#{urlb}'; hoboPartPage = '#{view_name}'</script>"
|
350
|
+
|
351
|
+
if tiny_mce
|
352
|
+
res += javascript_include_tag("tiny_mce/tiny_mce_src") + %{
|
353
|
+
<script type="text/javascript">
|
354
|
+
tinyMCE.init({ mode: "textareas", editor_selector: "tiny_mce",
|
355
|
+
plugins: 'save',
|
356
|
+
theme_advanced_buttons1 : "bold, italic, separator, " +
|
357
|
+
"bullist, outdent, indent, separator, " +
|
358
|
+
"undo, redo, separator, link, unlink",
|
359
|
+
theme_advanced_buttons2 : "",
|
360
|
+
theme_advanced_buttons3 : ""
|
361
|
+
});
|
362
|
+
</script>}
|
363
|
+
end
|
364
|
+
res
|
365
|
+
end
|
366
|
+
|
367
|
+
|
368
|
+
def_tag :object_form, :message, :update, :hidden_fields, :url do
|
369
|
+
ajax_options, html_options = options.partition_hash(AJAX_ATTRS)
|
370
|
+
|
371
|
+
url2 = url || object_url(this)
|
372
|
+
if update
|
373
|
+
# add an onsubmit to convert to an ajax form if `update` is given
|
374
|
+
function = ajax_updater(:post_form, message, update, ajax_options)
|
375
|
+
html_options[:onsubmit] = [html_options[:onsubmit],
|
376
|
+
"#{function}; return false;"].compact.join("; ")
|
377
|
+
end
|
378
|
+
|
379
|
+
body, field_names = with_form_context{ tagbody.call }
|
380
|
+
body = body.to_s
|
381
|
+
|
382
|
+
hiddens = case hidden_fields
|
383
|
+
when nil
|
384
|
+
[]
|
385
|
+
when '*'
|
386
|
+
this.class.column_names - ['type']
|
387
|
+
else
|
388
|
+
comma_split(hidden_fields)
|
389
|
+
end
|
390
|
+
pname = this.class.name.underscore
|
391
|
+
hidden_tags = hiddens.map do |h|
|
392
|
+
val = this.send(h)
|
393
|
+
name = "#{pname}[#{h}]"
|
394
|
+
hidden_field_tag(name, val.to_s) if val and name.not_in?(field_names)
|
395
|
+
end
|
396
|
+
hidden_tags << hidden_field_tag("_method", "PUT") unless this.respond_to?(:new_record?) and this.new_record?
|
397
|
+
|
398
|
+
html_options[:method] = "post"
|
399
|
+
body_with_hiddens = hidden_tags.compact.join("\n") + body
|
400
|
+
|
401
|
+
form_class = this.new_record? ? "new_#{this.class.name.underscore}" : this.class.name.underscore
|
402
|
+
|
403
|
+
content_tag("form", body_with_hiddens, add_classes(html_options, form_class).merge(:action => url2))
|
404
|
+
end
|
405
|
+
|
406
|
+
|
407
|
+
def_tag :create_form, :model, :in, :update, :message, :hidden_fields do
|
408
|
+
hiddens = hidden_fields
|
409
|
+
if model
|
410
|
+
obj = (model.is_a?(String) ? model.constantize : model).new
|
411
|
+
else
|
412
|
+
raise HoboError.new("cannot create object in #{in_.inspect}") unless this.is_a? Array
|
413
|
+
obj = this.new
|
414
|
+
refl = this.proxy_reflection
|
415
|
+
if refl.macro == :has_many and !refl.through_reflection
|
416
|
+
hiddens ||= [refl.primary_key_name]
|
417
|
+
end
|
418
|
+
end
|
419
|
+
m = message || "New #{obj.class.name.titleize}"
|
420
|
+
options = add_classes(options, "create_#{obj.class.underscore.name}_form")
|
421
|
+
object_form(obj, options.merge(:message => m, :update => update, :hidden_fields => hiddens)) do
|
422
|
+
tagbody.call
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
def_tag :remote_method_form, :method, :message, :update, :result_update do
|
427
|
+
ajax_options, html_options = options.partition_hash(AJAX_ATTRS)
|
428
|
+
|
429
|
+
url = object_url(this, method)
|
430
|
+
if update || result_update || !ajax_options.empty?
|
431
|
+
# add an onsubmit to convert to an ajax form
|
432
|
+
|
433
|
+
function = ajax_updater(:post_form, message, update, ajax_options.merge(:result_update => result_update))
|
434
|
+
html_options[:onsubmit] = [html_options[:onsubmit],
|
435
|
+
"var e = this; #{function}; return false;"].compact.join("; ")
|
436
|
+
end
|
437
|
+
|
438
|
+
add_classes!(html_options, "#{this.class.name.underscore}_#{method}_form")
|
439
|
+
html_options[:method] = "post"
|
440
|
+
content_tag("form", tagbody.call, html_options.merge(:action => url))
|
441
|
+
end
|
442
|
+
|
443
|
+
def editor_class
|
444
|
+
"#{this_parent.class.name.underscore}_#{this_field}_editor"
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|