hobo 0.7.2 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/hobo +24 -7
- data/hobo_files/plugin/CHANGES.txt +501 -0
- data/hobo_files/plugin/generators/hobo/hobo_generator.rb +8 -6
- data/hobo_files/plugin/generators/hobo/templates/application.dryml +3 -0
- data/hobo_files/plugin/generators/hobo/templates/dryml-support.js +132 -0
- data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +4 -5
- data/hobo_files/plugin/generators/hobo_model_resource/hobo_model_resource_generator.rb +75 -0
- data/hobo_files/plugin/generators/hobo_model_resource/templates/controller.rb +7 -0
- data/hobo_files/plugin/generators/hobo_model_resource/templates/functional_test.rb +8 -0
- data/hobo_files/plugin/generators/hobo_model_resource/templates/helper.rb +2 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.js +30 -11
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css +149 -92
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +0 -48
- data/hobo_files/plugin/init.rb +45 -13
- data/hobo_files/plugin/lib/action_view_extensions/base.rb +4 -3
- data/hobo_files/plugin/lib/active_record/association_proxy.rb +18 -0
- data/hobo_files/plugin/lib/active_record/association_reflection.rb +5 -0
- data/hobo_files/plugin/lib/active_record/has_many_association.rb +7 -11
- data/hobo_files/plugin/lib/active_record/has_many_through_association.rb +8 -0
- data/hobo_files/plugin/lib/extensions/test_case.rb +1 -1
- data/hobo_files/plugin/lib/hobo.rb +38 -60
- data/hobo_files/plugin/lib/hobo/authentication_support.rb +1 -1
- data/hobo_files/plugin/lib/hobo/bundle.rb +131 -34
- data/hobo_files/plugin/lib/hobo/composite_model.rb +1 -1
- data/hobo_files/plugin/lib/hobo/controller.rb +7 -8
- data/hobo_files/plugin/lib/hobo/dev_controller.rb +21 -0
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +14 -8
- data/hobo_files/plugin/lib/hobo/dryml/dryml_support_controller.rb +13 -0
- data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +6 -7
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +207 -73
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +67 -55
- data/hobo_files/plugin/lib/hobo/dryml/template_handler.rb +53 -3
- data/hobo_files/plugin/lib/hobo/hobo_helper.rb +75 -107
- data/hobo_files/plugin/lib/hobo/model.rb +236 -429
- data/hobo_files/plugin/lib/hobo/model_controller.rb +277 -437
- data/hobo_files/plugin/lib/hobo/model_router.rb +62 -29
- data/hobo_files/plugin/lib/hobo/rapid_helper.rb +48 -9
- data/hobo_files/plugin/lib/hobo/scopes.rb +98 -0
- data/hobo_files/plugin/lib/hobo/scopes/association_proxy_extensions.rb +31 -0
- data/hobo_files/plugin/lib/hobo/scopes/automatic_scopes.rb +282 -0
- data/hobo_files/plugin/lib/hobo/scopes/defined_scope_proxy_extender.rb +88 -0
- data/hobo_files/plugin/lib/hobo/scopes/scope_reflection.rb +18 -0
- data/hobo_files/plugin/lib/hobo/scopes/scoped_proxy.rb +59 -0
- data/hobo_files/plugin/lib/hobo/undefined.rb +2 -0
- data/hobo_files/plugin/lib/hobo/user.rb +31 -14
- data/hobo_files/plugin/lib/hobo/user_controller.rb +41 -27
- data/hobo_files/plugin/taglibs/core.dryml +9 -11
- data/hobo_files/plugin/taglibs/rapid.dryml +51 -108
- data/hobo_files/plugin/taglibs/rapid_editing.dryml +25 -25
- data/hobo_files/plugin/taglibs/rapid_forms.dryml +111 -79
- data/hobo_files/plugin/taglibs/rapid_generics.dryml +74 -0
- data/hobo_files/plugin/taglibs/rapid_navigation.dryml +23 -21
- data/hobo_files/plugin/taglibs/rapid_pages.dryml +83 -169
- data/hobo_files/plugin/taglibs/rapid_plus.dryml +16 -2
- data/hobo_files/plugin/taglibs/rapid_support.dryml +3 -3
- data/hobo_files/plugin/taglibs/rapid_user_pages.dryml +104 -0
- metadata +60 -55
- data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +0 -276
- data/hobo_files/plugin/generators/hobo_migration/templates/migration.rb +0 -9
- data/hobo_files/plugin/lib/active_record/table_definition.rb +0 -34
- data/hobo_files/plugin/lib/extensions.rb +0 -375
- data/hobo_files/plugin/lib/hobo/email_address.rb +0 -12
- data/hobo_files/plugin/lib/hobo/enum_string.rb +0 -50
- data/hobo_files/plugin/lib/hobo/field_declaration_dsl.rb +0 -43
- data/hobo_files/plugin/lib/hobo/field_spec.rb +0 -68
- data/hobo_files/plugin/lib/hobo/html_string.rb +0 -7
- data/hobo_files/plugin/lib/hobo/lazy_hash.rb +0 -40
- data/hobo_files/plugin/lib/hobo/markdown_string.rb +0 -11
- data/hobo_files/plugin/lib/hobo/migrations.rb +0 -12
- data/hobo_files/plugin/lib/hobo/model_queries.rb +0 -117
- data/hobo_files/plugin/lib/hobo/password_string.rb +0 -7
- data/hobo_files/plugin/lib/hobo/percentage.rb +0 -14
- data/hobo_files/plugin/lib/hobo/predicate_dispatch.rb +0 -78
- data/hobo_files/plugin/lib/hobo/proc_binding.rb +0 -32
- data/hobo_files/plugin/lib/hobo/text.rb +0 -3
- data/hobo_files/plugin/lib/hobo/textile_string.rb +0 -25
- data/hobo_files/plugin/lib/hobo/where_fragment.rb +0 -28
data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css
CHANGED
@@ -17,11 +17,6 @@ table.new-record textarea, table.new-record input {
|
|
17
17
|
display: inline;
|
18
18
|
}
|
19
19
|
|
20
|
-
.edit-page .content-header {overflow: hidden; height: 100%;}
|
21
|
-
.edit-page .content-header h1 {float: left;}
|
22
|
-
.edit-page .content-header .delete-button {float: right;}
|
23
|
-
form .actions {margin: 30px 0;width: 100%; text-align: center;}
|
24
|
-
|
25
20
|
/**** Admin ****/
|
26
21
|
|
27
22
|
.admin-banner {
|
@@ -39,25 +34,6 @@ form .actions {margin: 30px 0;width: 100%; text-align: center;}
|
|
39
34
|
float: right;
|
40
35
|
}
|
41
36
|
|
42
|
-
/* rails error message */
|
43
|
-
.error-messages {
|
44
|
-
font-family: "Lucida Grande", arial, sans-serif;
|
45
|
-
background: #9d0018;
|
46
|
-
border: 1px solid #7a0013;
|
47
|
-
padding: 15px 30px;
|
48
|
-
color: white;
|
49
|
-
margin-bottom: 20px;
|
50
|
-
}
|
51
|
-
.error-messages h2 {
|
52
|
-
text-transform: none;
|
53
|
-
letter-spacing: normal;
|
54
|
-
color: white;
|
55
|
-
margin-bottom: 10px;
|
56
|
-
}
|
57
|
-
.error-messages li {
|
58
|
-
margin-left: 20px;
|
59
|
-
}
|
60
|
-
|
61
37
|
/********* everything below here came from hobo_rapid.css, needs looking at ********/
|
62
38
|
|
63
39
|
/**** Default styling for Rapid ***/
|
@@ -66,30 +42,6 @@ form .actions {margin: 30px 0;width: 100%; text-align: center;}
|
|
66
42
|
float: right; margin: 20px;
|
67
43
|
position: fixed; display: none; z-index: 10;
|
68
44
|
}
|
69
|
-
/*
|
70
|
-
#ajax-progress {
|
71
|
-
color: grey;
|
72
|
-
float: right;
|
73
|
-
margin: 20px;
|
74
|
-
position: fixed;
|
75
|
-
background: white;
|
76
|
-
font-family: Tahoma "sans serif";
|
77
|
-
display: none;
|
78
|
-
z-index: 10;
|
79
|
-
}
|
80
|
-
|
81
|
-
#ajax-progress div {
|
82
|
-
border: 1px dashed grey;
|
83
|
-
margin: 10px;
|
84
|
-
padding: 3px;
|
85
|
-
padding-top: -15px;
|
86
|
-
}
|
87
|
-
|
88
|
-
#ajax-progress img {
|
89
|
-
padding-left: 6px;
|
90
|
-
vertical-align: middle;
|
91
|
-
}
|
92
|
-
*/
|
93
45
|
|
94
46
|
/* Scriptaculous Autocompleter ---*/
|
95
47
|
|
data/hobo_files/plugin/init.rb
CHANGED
@@ -1,16 +1,21 @@
|
|
1
|
+
# gem dependencies
|
2
|
+
require 'hobosupport'
|
3
|
+
|
4
|
+
# Force load:
|
5
|
+
HoboFields
|
6
|
+
|
1
7
|
# Monkey patches, ooh ooh
|
2
|
-
require 'extensions'
|
3
8
|
require 'rexml'
|
4
9
|
require 'active_record/has_many_association'
|
5
10
|
require 'active_record/has_many_through_association'
|
6
|
-
require 'active_record/
|
11
|
+
require 'active_record/association_proxy'
|
12
|
+
require 'active_record/association_reflection'
|
7
13
|
require 'action_view_extensions/base'
|
8
14
|
|
9
15
|
require 'hobo'
|
10
16
|
require 'hobo/dryml'
|
11
17
|
|
12
18
|
require 'hobo/model'
|
13
|
-
require 'hobo/field_declaration_dsl'
|
14
19
|
|
15
20
|
require 'hobo/dryml/template'
|
16
21
|
require 'hobo/dryml/taglib'
|
@@ -19,16 +24,6 @@ require 'hobo/dryml/template_handler'
|
|
19
24
|
|
20
25
|
require 'extensions/test_case' if RAILS_ENV == "test"
|
21
26
|
|
22
|
-
# Rich data types
|
23
|
-
require "hobo/html_string"
|
24
|
-
require "hobo/markdown_string"
|
25
|
-
require "hobo/textile_string"
|
26
|
-
require "hobo/password_string"
|
27
|
-
require "hobo/text"
|
28
|
-
require "hobo/email_address"
|
29
|
-
require "hobo/enum_string"
|
30
|
-
require "hobo/percentage"
|
31
|
-
|
32
27
|
|
33
28
|
ActionView::Base.register_template_handler("dryml", Hobo::Dryml::TemplateHandler)
|
34
29
|
|
@@ -54,6 +49,7 @@ end
|
|
54
49
|
class ActiveRecord::Base
|
55
50
|
def self.hobo_model
|
56
51
|
include Hobo::Model
|
52
|
+
fields # force hobofields to load
|
57
53
|
end
|
58
54
|
def self.hobo_user_model
|
59
55
|
include Hobo::Model
|
@@ -64,3 +60,39 @@ end
|
|
64
60
|
# Default settings
|
65
61
|
|
66
62
|
Hobo.developer_features = RAILS_ENV.in?(["development", "test"]) if Hobo.developer_features?.nil?
|
63
|
+
|
64
|
+
|
65
|
+
module ::Hobo
|
66
|
+
# Empty class to represent the boolean type.
|
67
|
+
class Boolean; end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
if defined? HoboFields
|
72
|
+
HoboFields.never_wrap(Hobo::Undefined)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# Add support for type metadata to arrays
|
77
|
+
class ::Array
|
78
|
+
|
79
|
+
attr_accessor :member_class, :origin, :origin_attribute
|
80
|
+
|
81
|
+
def to_url_path
|
82
|
+
base_path = origin_object.try.to_url_path
|
83
|
+
"#{base_path}/#{origin_attribute}" unless base_path.blank?
|
84
|
+
end
|
85
|
+
|
86
|
+
def typed_id
|
87
|
+
origin_id = origin.try.typed_id
|
88
|
+
"#{origin_id}_#{origin_attribute}" if origin_id
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
class NilClass
|
95
|
+
def typed_id
|
96
|
+
"nil"
|
97
|
+
end
|
98
|
+
end
|
@@ -2,12 +2,13 @@ module ActionView
|
|
2
2
|
|
3
3
|
class Base
|
4
4
|
|
5
|
-
|
6
|
-
def render_file(template_path, *args)
|
5
|
+
def render_file_with_dryml(template_path, *args)
|
7
6
|
@hobo_template_path = template_path
|
8
|
-
|
7
|
+
render_file_without_dryml(template_path, *args)
|
9
8
|
end
|
10
9
|
|
10
|
+
alias_method_chain :render_file, :dryml
|
11
|
+
|
11
12
|
end
|
12
13
|
|
13
14
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class AssociationProxy #:nodoc:
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
|
8
|
+
def raise_on_type_mismatch(record)
|
9
|
+
# Don't complain if the interface type of a polymorphic association doesn't exist
|
10
|
+
klass = @reflection.klass rescue nil
|
11
|
+
unless klass.nil? || record.is_a?(klass)
|
12
|
+
raise ActiveRecord::AssociationTypeMismatch, "#{@reflection.klass} expected, got #{record.class}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -18,7 +18,7 @@ module ActiveRecord::Associations
|
|
18
18
|
record = @reflection.klass.new(attributes)
|
19
19
|
if hobo_has_many?
|
20
20
|
set_belongs_to_association_for(record)
|
21
|
-
set_reverse_association(record)
|
21
|
+
set_reverse_association(record) unless proxy_reflection.options[:as]
|
22
22
|
end
|
23
23
|
record
|
24
24
|
end
|
@@ -28,17 +28,13 @@ module ActiveRecord::Associations
|
|
28
28
|
proxy_reflection.klass
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
else
|
38
|
-
find_without_block(*args)
|
39
|
-
end
|
31
|
+
def origin
|
32
|
+
proxy_owner
|
33
|
+
end
|
34
|
+
|
35
|
+
def origin_attribute
|
36
|
+
proxy_reflection.association_name
|
40
37
|
end
|
41
|
-
alias_method_chain :find, :block
|
42
38
|
|
43
39
|
private
|
44
40
|
|
@@ -102,7 +102,7 @@ class Test::Unit::TestCase
|
|
102
102
|
def replace_objects_in_params!(hash)
|
103
103
|
hash.each do |k,v|
|
104
104
|
if v.is_a? ActiveRecord::Base
|
105
|
-
hash[k] = "@" +
|
105
|
+
hash[k] = "@" + v.typed_id
|
106
106
|
elsif v.is_a? Hash
|
107
107
|
replace_objects_in_params!(v)
|
108
108
|
end
|
@@ -5,20 +5,12 @@ module Hobo
|
|
5
5
|
class RawJs < String; end
|
6
6
|
|
7
7
|
@models = []
|
8
|
-
@field_types = HashWithIndifferentAccess.new
|
9
8
|
|
10
9
|
class << self
|
11
10
|
|
12
|
-
attr_accessor :current_theme
|
11
|
+
attr_accessor :current_theme
|
13
12
|
attr_writer :developer_features
|
14
13
|
|
15
|
-
def symbolic_type_name(type)
|
16
|
-
field_types.index(type)
|
17
|
-
end
|
18
|
-
|
19
|
-
def type_id(type)
|
20
|
-
symbolic_type_name(type) || type.name.underscore.gsub("/", "__")
|
21
|
-
end
|
22
14
|
|
23
15
|
def developer_features?
|
24
16
|
@developer_features
|
@@ -41,7 +33,7 @@ module Hobo
|
|
41
33
|
|
42
34
|
|
43
35
|
def models=(models)
|
44
|
-
@models = models
|
36
|
+
@models = models.*.name
|
45
37
|
end
|
46
38
|
|
47
39
|
|
@@ -52,7 +44,7 @@ module Hobo
|
|
52
44
|
end
|
53
45
|
@models_loaded = true
|
54
46
|
end
|
55
|
-
@models
|
47
|
+
@models.*.constantize
|
56
48
|
end
|
57
49
|
|
58
50
|
|
@@ -87,64 +79,38 @@ module Hobo
|
|
87
79
|
end
|
88
80
|
|
89
81
|
def dom_id(obj, attr=nil)
|
90
|
-
if obj.nil?
|
91
|
-
raise ArgumentError, "Tried to get dom id of nil.#{attr}" if attr
|
92
|
-
return 'nil'
|
93
|
-
end
|
94
|
-
|
95
|
-
if obj.is_a?(Array) and obj.respond_to?(:proxy_owner)
|
96
|
-
attr = obj.proxy_reflection.name
|
97
|
-
obj = obj.proxy_owner
|
98
|
-
elsif obj.is_a?(Class)
|
99
|
-
return type_id(obj)
|
100
|
-
elsif !obj.respond_to?(:typed_id)
|
101
|
-
return (if attr
|
102
|
-
dom_id(get_field(obj, attr))
|
103
|
-
elsif obj.respond_to?(:id)
|
104
|
-
"#{obj.class.name.underscore}_#{obj.id}"
|
105
|
-
else
|
106
|
-
raise ArgumentError, "Can't create dom id for #{obj.inspect}"
|
107
|
-
end)
|
108
|
-
end
|
109
82
|
attr ? "#{obj.typed_id}_#{attr}" : obj.typed_id
|
110
83
|
end
|
111
84
|
|
112
|
-
def find_by_search(query)
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
where = cols.map {|c| "(#{c} like ?)"}.join(' or ')
|
121
|
-
type = model.column_names.include?("type") ? "type" : "'#{model.name}'"
|
122
|
-
ActiveRecord::Base.send(:sanitize_sql,
|
123
|
-
["select #{type} as type, id " +
|
124
|
-
"from #{model.table_name} " +
|
125
|
-
"where #{where}"] +
|
126
|
-
["%#{query}%"] * cols.length)
|
85
|
+
def find_by_search(query, search_targets=nil)
|
86
|
+
search_targets ||=
|
87
|
+
begin
|
88
|
+
# FIXME: This should interrogate the model-router directly, there's no need to enumerate models
|
89
|
+
# By default, search all models, but filter out...
|
90
|
+
Hobo.models.select do |m|
|
91
|
+
ModelRouter.linkable?(m, :show) && # ...non-linkables
|
92
|
+
m.search_columns.any? # and models with no search-columns
|
127
93
|
end
|
128
94
|
end
|
129
|
-
end.compact.join(" union ")
|
130
|
-
|
131
|
-
rows = ActiveRecord::Base.connection.select_all(sql)
|
132
|
-
records = Hash.new {|h,k| h[k] = []}
|
133
|
-
for row in rows
|
134
|
-
records[row['type']] << row['id']
|
135
|
-
end
|
136
|
-
results = []
|
137
|
-
for type, ids in records
|
138
|
-
results.concat(type.constantize.find(:all, :conditions => "id in (#{ids * ','})"))
|
139
|
-
end
|
140
95
|
|
141
|
-
|
96
|
+
query_words = ActiveRecord::Base.connection.quote_string(query).split
|
97
|
+
|
98
|
+
search_targets.build_hash do |search_target|
|
99
|
+
conditions = query_words.map do |word|
|
100
|
+
"(" + search_target.search_columns.map { |column| %(#{column} like "%#{word}%") }.join(" or ") + ")"
|
101
|
+
end.join(" and ")
|
102
|
+
|
103
|
+
results = search_target.find(:all, :conditions => conditions)
|
104
|
+
[search_target.name, results] unless results.empty?
|
105
|
+
end
|
142
106
|
end
|
143
107
|
|
144
108
|
def add_routes(m)
|
145
109
|
Hobo::ModelRouter.add_routes(m)
|
146
110
|
end
|
147
111
|
|
112
|
+
|
113
|
+
# FIXME: This method won't be needed
|
148
114
|
def all_models
|
149
115
|
Hobo.models.map { |m| m.name.underscore }
|
150
116
|
end
|
@@ -159,6 +125,16 @@ module Hobo
|
|
159
125
|
end
|
160
126
|
|
161
127
|
|
128
|
+
def can_create_in_association?(array_or_reflection)
|
129
|
+
refl =
|
130
|
+
(array_or_reflection.is_a?(ActiveRecord::Reflection::AssociationReflection) and array_or_reflection) or
|
131
|
+
array_or_reflection.try.proxy_reflection or
|
132
|
+
(origin = array_or_reflection.try.origin and origin.send(array_or_reflection.origin_attribute).try.proxy_reflection)
|
133
|
+
|
134
|
+
refl && refl.macro == :has_many && (!refl.through_reflection) && (!refl.options[:conditions])
|
135
|
+
end
|
136
|
+
|
137
|
+
|
162
138
|
def get_field(object, field)
|
163
139
|
return nil if object.nil?
|
164
140
|
if field.to_s =~ /^\d+$/
|
@@ -213,10 +189,12 @@ module Hobo
|
|
213
189
|
return false if !can_view?(person, object, field)
|
214
190
|
|
215
191
|
if field.nil?
|
216
|
-
if
|
192
|
+
if object.has_hobo_method?(:editable_by?)
|
217
193
|
object.editable_by?(person)
|
218
|
-
|
194
|
+
elsif object.has_hobo_method?(:updatable_by?)
|
219
195
|
object.updatable_by?(person, nil)
|
196
|
+
else
|
197
|
+
false
|
220
198
|
end
|
221
199
|
|
222
200
|
else
|
@@ -333,7 +311,7 @@ module Hobo
|
|
333
311
|
else
|
334
312
|
File.join(File.dirname(__FILE__), "hobo/static_tags")
|
335
313
|
end
|
336
|
-
File.readlines(path)
|
314
|
+
File.readlines(path).*.chop
|
337
315
|
end
|
338
316
|
end
|
339
317
|
|
@@ -38,7 +38,7 @@ module Hobo
|
|
38
38
|
# skip_before_filter :login_required
|
39
39
|
#
|
40
40
|
def login_required(user_model=nil)
|
41
|
-
auth_model = user_model ||
|
41
|
+
auth_model = user_model || User.default_user_model
|
42
42
|
if current_user.guest?
|
43
43
|
username, passwd = get_auth_data
|
44
44
|
self.current_user = auth_model.authenticate(username, passwd) || nil if username && passwd && auth_model
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'extensions'
|
2
|
-
|
3
1
|
module ::Hobo
|
4
2
|
|
5
3
|
class Bundle
|
@@ -8,48 +6,93 @@ module ::Hobo
|
|
8
6
|
|
9
7
|
class << self
|
10
8
|
|
11
|
-
|
9
|
+
# Hobo::Bundle.bundles is a hash of all instantiated bundles by name
|
10
|
+
attr_accessor :bundles
|
11
|
+
|
12
|
+
# Used by subclasses, e.g MyBundle.plugin is the name of the
|
13
|
+
# plugin the bundle came from
|
14
|
+
attr_reader :plugin
|
15
|
+
|
16
|
+
attr_reader :model_declarations, :controller_declarations
|
17
|
+
|
18
|
+
attr_accessor :dirname
|
12
19
|
|
13
20
|
def inherited(base)
|
14
21
|
filename = caller[0].match(/^(.*):\d+/)[1]
|
15
|
-
dirname = filename.match(%r(^.*/plugins/[^/]+))[0]
|
16
|
-
|
22
|
+
base.dirname = filename.match(%r(^.*/plugins/[^/]+))[0]
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def load_models_and_controllers
|
27
|
+
return if models_and_controllers_loaded?
|
17
28
|
|
18
|
-
|
19
|
-
attr_accessor :models, :controllers
|
20
|
-
end
|
29
|
+
@plugin = File.basename(dirname)
|
21
30
|
|
22
|
-
|
23
|
-
|
31
|
+
@model_declarations = []
|
32
|
+
@controller_declarations = []
|
24
33
|
|
25
|
-
|
26
|
-
|
34
|
+
class_eval do
|
35
|
+
eval_ruby_files("#{dirname}/models", @models)
|
36
|
+
eval_ruby_files("#{dirname}/controllers", @controllers)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](bundle_name)
|
41
|
+
bundles[bundle_name]
|
27
42
|
end
|
28
43
|
|
44
|
+
|
45
|
+
private
|
29
46
|
|
30
47
|
def bundle_model(name, &block)
|
31
|
-
|
48
|
+
@model_declarations << [name, block]
|
32
49
|
end
|
33
50
|
|
34
51
|
|
35
52
|
def bundle_model_controller(model_name, &block)
|
36
|
-
|
53
|
+
@controller_declarations << [model_name, block]
|
37
54
|
end
|
55
|
+
|
38
56
|
|
57
|
+
def models_and_controllers_loaded?
|
58
|
+
@model_declarations
|
59
|
+
end
|
39
60
|
|
40
|
-
private
|
41
61
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
62
|
+
|
63
|
+
def eval_ruby_files(dir, filenames)
|
64
|
+
files = if filenames == [:none]
|
65
|
+
[]
|
66
|
+
elsif filenames.blank? || filenames == [:all]
|
67
|
+
Dir["#{dir}/*.rb"]
|
68
|
+
else
|
69
|
+
filenames.map { |f| "#{dir}/#{f}.rb" }
|
70
|
+
end
|
71
|
+
|
72
|
+
files.each { |f| instance_eval(File.read(f), f, 1) }
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# Declatations
|
77
|
+
|
78
|
+
def models(*models)
|
79
|
+
@models = models
|
80
|
+
end
|
81
|
+
|
82
|
+
def controllers(*controllers)
|
83
|
+
@controllers = controllers.map {|c| case c.to_s
|
84
|
+
when /controller$/, "all", "none" then c
|
85
|
+
else "#{c.to_s.pluralize}_controller"
|
86
|
+
end }
|
46
87
|
end
|
47
88
|
|
48
89
|
end
|
49
90
|
|
50
91
|
def initialize(*args)
|
92
|
+
self.class.load_models_and_controllers
|
93
|
+
|
51
94
|
options = defaults.with_indifferent_access
|
52
|
-
options.
|
95
|
+
options.recursive_update(args.extract_options!)
|
53
96
|
|
54
97
|
self.name = args.first || self.class.name.match(/[^:]+$/)[0].underscore
|
55
98
|
Bundle.bundles[name] = self
|
@@ -78,18 +121,52 @@ module ::Hobo
|
|
78
121
|
|
79
122
|
|
80
123
|
def create_models
|
81
|
-
self.class.
|
82
|
-
klass = make_class(new_name_for(name), ActiveRecord::Base)
|
83
|
-
|
124
|
+
self.class.model_declarations.each do |name, block|
|
125
|
+
klass = make_class(new_name_for(name), ActiveRecord::Base)
|
126
|
+
|
127
|
+
klass.meta_def :belongs_to_with_optional_polymorphism do |*args|
|
128
|
+
opts = args.extract_options!
|
129
|
+
|
130
|
+
if opts[:polymorphic] == :optional
|
131
|
+
if bundle.options["polymorphic_#{name}"]
|
132
|
+
opts[:polymorphic] = true
|
133
|
+
opts.delete(:class_name)
|
134
|
+
else
|
135
|
+
opts.delete(:polymorphic)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
belongs_to_without_optional_polymorphism(name, opts)
|
84
139
|
end
|
85
|
-
klass.
|
140
|
+
klass.meta_eval { alias_method_chain :belongs_to, :optional_polymorphism }
|
141
|
+
|
142
|
+
klass.class_eval { hobo_model }
|
143
|
+
|
144
|
+
# FIXME this extension breaks passing a block to belongs_to
|
145
|
+
klass.meta_def :belongs_to_with_alias do |*args|
|
146
|
+
opts = args.extract_options!
|
147
|
+
name = args.first.to_sym
|
148
|
+
|
149
|
+
alias_name = opts.delete(:alias)
|
150
|
+
|
151
|
+
belongs_to_without_alias(name, opts)
|
152
|
+
|
153
|
+
if alias_name && name != alias_name
|
154
|
+
klass.send(:alias_method, alias_name, name)
|
155
|
+
# make the aliased name available in the classes metadata
|
156
|
+
klass.reflections[alias_name] = klass.reflections[name]
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
klass.meta_eval { alias_method_chain :belongs_to, :alias }
|
161
|
+
|
162
|
+
klass.class_eval(&block)
|
86
163
|
end
|
87
164
|
end
|
88
165
|
|
89
166
|
|
90
167
|
def create_controllers
|
91
168
|
bundle = self
|
92
|
-
self.class.
|
169
|
+
self.class.controller_declarations.each do |model_name, block|
|
93
170
|
klass = make_class("#{new_name_for(model_name).to_s.pluralize}Controller", ApplicationController) do
|
94
171
|
hobo_model_controller
|
95
172
|
end
|
@@ -106,6 +183,14 @@ module ::Hobo
|
|
106
183
|
def self.feature(name, &block)
|
107
184
|
_feature(name, block)
|
108
185
|
end
|
186
|
+
|
187
|
+
def method_missing(name, *args)
|
188
|
+
if name.to_s =~ /^_.*_$/
|
189
|
+
self.class.bundle.magic_option(name)
|
190
|
+
else
|
191
|
+
super
|
192
|
+
end
|
193
|
+
end
|
109
194
|
end
|
110
195
|
|
111
196
|
klass.meta_def(:bundle) do
|
@@ -134,19 +219,23 @@ module ::Hobo
|
|
134
219
|
end
|
135
220
|
|
136
221
|
klass.class_eval(&b) if b
|
222
|
+
|
137
223
|
klass
|
138
224
|
end
|
139
225
|
|
140
226
|
|
141
227
|
def new_name_for(name)
|
142
|
-
while
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
228
|
+
while true
|
229
|
+
if renames.has_key?(name)
|
230
|
+
name = renames[name]
|
231
|
+
elsif name.to_s =~ /_.*?_/
|
232
|
+
name2 = name.to_s.gsub(/_.*?_/) { |s| new_name_for(s[1..-2]) }
|
233
|
+
# Make sure symbols stay symbols
|
234
|
+
name = name.is_a?(Symbol) ? name2.to_sym : name2
|
235
|
+
else
|
236
|
+
return name
|
237
|
+
end
|
148
238
|
end
|
149
|
-
name
|
150
239
|
end
|
151
240
|
|
152
241
|
|
@@ -169,7 +258,7 @@ module ::Hobo
|
|
169
258
|
new_name_for(name).to_s.constantize.class_eval(&block)
|
170
259
|
end
|
171
260
|
|
172
|
-
|
261
|
+
|
173
262
|
def method_missing(name, *args)
|
174
263
|
if name.to_s =~ /^_.*_$/
|
175
264
|
magic_option(name)
|
@@ -213,7 +302,15 @@ module ::Hobo
|
|
213
302
|
external_options = self.options[option_name]
|
214
303
|
external_options = {} if external_options.nil? || external_options == true
|
215
304
|
name = "#{self.name}_#{option_name}"
|
216
|
-
|
305
|
+
|
306
|
+
sub_bundle_options = external_options.merge(local_options).merge(renames)
|
307
|
+
sub_bundle = class_name.to_s.constantize.new(name, sub_bundle_options)
|
308
|
+
|
309
|
+
conflicting_renames = (renames.keys & sub_bundle.renames.keys).select { |k| renames[k] != sub_bundle.renames[k] }
|
310
|
+
unless conflicting_renames.empty?
|
311
|
+
raise ArgumentError, "Conflicting renames in included bundle '#{name}' of '#{self.name}': #{conflicting_renames * ', '}"
|
312
|
+
end
|
313
|
+
renames.update(sub_bundle.renames)
|
217
314
|
self.options["#{option_name}_bundle"] = name
|
218
315
|
end
|
219
316
|
|