hobo 0.7.3 → 0.7.4
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/hobo +1 -1
- data/hobo_files/plugin/CHANGES.txt +302 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +2 -9
- data/hobo_files/plugin/generators/hobo_model/templates/model.rb +1 -1
- data/hobo_files/plugin/generators/hobo_model_resource/hobo_model_resource_generator.rb +0 -2
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.js +76 -46
- data/hobo_files/plugin/generators/hobo_rapid/templates/lowpro.js +25 -18
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css +29 -11
- data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +2 -2
- data/hobo_files/plugin/init.rb +0 -1
- data/hobo_files/plugin/lib/active_record/has_many_association.rb +3 -0
- data/hobo_files/plugin/lib/hobo.rb +12 -8
- data/hobo_files/plugin/lib/hobo/bundle.rb +1 -1
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +1 -1
- data/hobo_files/plugin/lib/hobo/dryml/parser/attribute.rb +41 -0
- data/hobo_files/plugin/lib/hobo/dryml/parser/base_parser.rb +253 -0
- data/hobo_files/plugin/lib/hobo/dryml/parser/document.rb +26 -0
- data/hobo_files/plugin/lib/hobo/dryml/parser/element.rb +27 -0
- data/hobo_files/plugin/lib/hobo/dryml/parser/elements.rb +45 -0
- data/hobo_files/plugin/lib/hobo/dryml/parser/source.rb +58 -0
- data/hobo_files/plugin/lib/hobo/dryml/parser/text.rb +13 -0
- data/hobo_files/plugin/lib/hobo/dryml/parser/tree_parser.rb +67 -0
- data/hobo_files/plugin/lib/hobo/dryml/scoped_variables.rb +10 -5
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +48 -27
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +28 -13
- data/hobo_files/plugin/lib/hobo/hobo_helper.rb +3 -1
- data/hobo_files/plugin/lib/hobo/model.rb +70 -10
- data/hobo_files/plugin/lib/hobo/model_controller.rb +49 -34
- data/hobo_files/plugin/lib/hobo/model_router.rb +10 -2
- data/hobo_files/plugin/lib/hobo/rapid_helper.rb +1 -0
- data/hobo_files/plugin/lib/hobo/scopes.rb +15 -0
- data/hobo_files/plugin/lib/hobo/scopes/apply_scopes.rb +23 -0
- data/hobo_files/plugin/lib/hobo/scopes/association_proxy_extensions.rb +4 -2
- data/hobo_files/plugin/lib/hobo/scopes/automatic_scopes.rb +34 -7
- data/hobo_files/plugin/lib/hobo/scopes/defined_scope_proxy_extender.rb +3 -1
- data/hobo_files/plugin/lib/hobo/scopes/scoped_proxy.rb +1 -5
- data/hobo_files/plugin/taglibs/rapid.dryml +33 -24
- data/hobo_files/plugin/taglibs/rapid_editing.dryml +6 -5
- data/hobo_files/plugin/taglibs/rapid_forms.dryml +37 -31
- data/hobo_files/plugin/taglibs/rapid_generics.dryml +68 -27
- data/hobo_files/plugin/taglibs/rapid_navigation.dryml +5 -8
- data/hobo_files/plugin/taglibs/rapid_pages.dryml +71 -47
- data/hobo_files/plugin/taglibs/rapid_plus.dryml +4 -5
- data/hobo_files/plugin/taglibs/rapid_support.dryml +11 -4
- metadata +23 -6
- data/hobo_files/plugin/lib/rexml.rb +0 -443
@@ -21,12 +21,15 @@
|
|
21
21
|
</def>
|
22
22
|
|
23
23
|
|
24
|
+
<def tag="main-nav"><magic-nav class="main-nav"/></def>
|
25
|
+
|
26
|
+
|
24
27
|
<!--- Account Navigation (log in / out / signup) -->
|
25
28
|
|
26
29
|
<def tag="account-nav">
|
27
30
|
<ul with="¤t_user" class="account-nav" param>
|
28
31
|
<if test="&logged_in?">
|
29
|
-
<li class='nav-item' param="logged-in-as">Logged in as <view:login/></li>
|
32
|
+
<li class='nav-item' param="logged-in-as"><a to="¤t_user">Logged in as <view:login/></a></li>
|
30
33
|
<li class='nav-item' param="account"><a action="account">Account</a></li>
|
31
34
|
<li class='nav-item' param="log-out"><a href="&logout_url">Log out</a></li>
|
32
35
|
</if>
|
@@ -42,13 +45,7 @@
|
|
42
45
|
<!--- Pagination Navigation -->
|
43
46
|
|
44
47
|
<def tag="page-nav" attrs="params">
|
45
|
-
|
46
|
-
<page-n-of-count/> -
|
47
|
-
<first-page-link params="¶ms" param>|<</first-page-link>
|
48
|
-
<previous-page-link params="¶ms" param>Previous</previous-page-link>
|
49
|
-
<next-page-link params="¶ms" param>Next</next-page-link>
|
50
|
-
<last-page-link params="¶ms" param>>|</last-page-link>
|
51
|
-
</if>
|
48
|
+
<%= will_paginate this, attributes.symbolize_keys.reverse_merge(:inner_window => 2, :prev_label => "« Prev") %>
|
52
49
|
</def>
|
53
50
|
|
54
51
|
|
@@ -1,9 +1,9 @@
|
|
1
|
-
<def tag="base-page" attrs="title, doctype">
|
2
|
-
<%
|
1
|
+
<def tag="base-page" attrs="title, full-title, doctype">
|
2
|
+
<% full_title ||= "#{title} : #{app_name}" %>
|
3
3
|
<doctype param version="&doctype || 'HTML 4.01 STRICT'"/>
|
4
4
|
<html>
|
5
5
|
<head param>
|
6
|
-
<title param><%=
|
6
|
+
<title param><%= full_title.gsub(/<.*?>/, '') %></title>
|
7
7
|
<do param="stylesheets">
|
8
8
|
<stylesheet name="reset"/>
|
9
9
|
<stylesheet name="hobo-rapid"/>
|
@@ -17,7 +17,7 @@
|
|
17
17
|
</head>
|
18
18
|
|
19
19
|
<body onload="Hobo.applyEvents();" param/>
|
20
|
-
</html>
|
20
|
+
</html>
|
21
21
|
</def>
|
22
22
|
|
23
23
|
|
@@ -30,7 +30,7 @@
|
|
30
30
|
<live-search param if="&defined_route? :site_search"/>
|
31
31
|
<nav param>
|
32
32
|
<account-nav if="&Hobo::User.default_user_model" param/>
|
33
|
-
<
|
33
|
+
<main-nav param/>
|
34
34
|
</nav>
|
35
35
|
</header>
|
36
36
|
<section class="page-content" param="content">
|
@@ -41,6 +41,7 @@
|
|
41
41
|
<footer class="content-footer" param="content-footer"/>
|
42
42
|
</section>
|
43
43
|
<footer class="page-footer" param/>
|
44
|
+
<part-contexts-javascripts/>
|
44
45
|
</body:>
|
45
46
|
</base-page>
|
46
47
|
</def>
|
@@ -65,17 +66,17 @@
|
|
65
66
|
<def tag="index-page">
|
66
67
|
<set model="&this.try.member_class || self.model"/>
|
67
68
|
<set model-name="&model.name.titleize"/>
|
68
|
-
<page title="All #{type_name :with => model, :
|
69
|
+
<page title="All #{type_name :with => model, :plural => true}" merge>
|
69
70
|
<body: class="index-page #{type_id model}" param/>
|
70
71
|
<content-header: param>
|
71
|
-
<heading param><type-name with="&model"/></heading>
|
72
|
-
<p class="note" if param>There <count
|
72
|
+
<heading param><type-name with="&model" plural/></heading>
|
73
|
+
<p class="note" if param>There <count prefix="are"/></p>
|
73
74
|
</content-header:>
|
74
75
|
|
75
76
|
<content-body: param>
|
76
77
|
<nav param="top-pagination-nav"><page-nav/></nav>
|
77
78
|
|
78
|
-
<collection param
|
79
|
+
<collection param><empty-message:></empty-message:></collection>
|
79
80
|
|
80
81
|
<nav param="bottom-pagination-nav"><page-nav/></nav>
|
81
82
|
</content-body>
|
@@ -86,7 +87,7 @@
|
|
86
87
|
<if with="&model.user_new current_user">
|
87
88
|
<section class="create-new">
|
88
89
|
<h2>New <type-name/></h2>
|
89
|
-
<form><field-list/><submit label="Create
|
90
|
+
<form><field-list param="new-field-list"/><submit label="Create"/></form>
|
90
91
|
</section>
|
91
92
|
</if>
|
92
93
|
</else>
|
@@ -97,7 +98,7 @@
|
|
97
98
|
|
98
99
|
<def tag="new-page">
|
99
100
|
<page title="New #{type_name}" merge>
|
100
|
-
<body: class="new-page #{type_name
|
101
|
+
<body: class="new-page #{type_name :dasherize => true}" param/>
|
101
102
|
<content-header: param>
|
102
103
|
<heading param>New <type-name title/></heading>
|
103
104
|
</content-header>
|
@@ -106,7 +107,7 @@
|
|
106
107
|
<error-messages param/>
|
107
108
|
|
108
109
|
<form param>
|
109
|
-
<field-list
|
110
|
+
<field-list param/>
|
110
111
|
<div class="actions" param="actions">
|
111
112
|
<submit label="Create #{type_name}" param/><do param="back-link"> or <a>Cancel</a></do>
|
112
113
|
</div>
|
@@ -117,61 +118,75 @@
|
|
117
118
|
|
118
119
|
|
119
120
|
<def tag="show-page" attrs="primary-collection">
|
120
|
-
<set primary-collection-name="&(primary_collection || self.primary_collection_name).to_s"/>
|
121
|
+
<set primary-collection-name="&(primary_collection || self.primary_collection_name).to_s" model="&this.class"/>
|
122
|
+
<set boolean-fields="&model.columns.select {|c| c.type == :boolean }.*.name"/>
|
123
|
+
<set skip-fields="&boolean_fields + [model.name_attribute, model.primary_content_attribute, model.creator_attribute, model.dependent_on.first].compact"/>
|
124
|
+
<set flags="&boolean_fields.map {|f| f.titleize if this.send(f)}.compact.join(', ')"/>
|
125
|
+
|
121
126
|
<page merge title="#{name :no_wrapper => true}">
|
127
|
+
|
122
128
|
<body: class="show-page #{type_name :dasherize => true}" param/>
|
129
|
+
|
123
130
|
<content-header: param>
|
124
131
|
<if with="&this.dependent_on.reject{|x| x.is_a?(Hobo::User)}.first">
|
125
|
-
<div class="container"><a/></div>
|
132
|
+
<div class="container"><a>« <name/></a></div>
|
126
133
|
</if>
|
127
134
|
|
128
|
-
<heading param
|
129
|
-
|
135
|
+
<heading param><this/></heading>
|
136
|
+
|
137
|
+
<div class="flags"><flags/></div>
|
130
138
|
|
139
|
+
<creation-details param/>
|
131
140
|
<do field="&primary_collection_name" if="&primary_collection_name" param="primary-collection-count">
|
132
141
|
<association-count class="primary-collection-count" part="primary-collection-count"/>
|
133
142
|
</do>
|
134
143
|
|
135
|
-
<a action="edit" class="edit" if="&can_edit?" param="edit-link">Edit <type-name/></a>
|
144
|
+
<a action="edit" class="edit" if="&linkable?(:edit) && can_edit?" param="edit-link">Edit <type-name/></a>
|
136
145
|
</content-header>
|
137
146
|
|
138
147
|
<content-body: param>
|
139
148
|
<primary-content param/>
|
140
149
|
|
141
|
-
<field-list skip="&
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
<
|
154
|
-
<
|
155
|
-
<
|
156
|
-
<
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
150
|
+
<field-list skip="&skip_fields" skip-associations="has_many" param/>
|
151
|
+
|
152
|
+
<with-primary-collection name="&primary_collection_name" if="&primary_collection_name">
|
153
|
+
<section class="primary-collection">
|
154
|
+
<h2 param="primary-collection-title"><primary-collection-name.titleize/></h2>
|
155
|
+
|
156
|
+
<do param="primary-collection">
|
157
|
+
<collection part="primary-collection">
|
158
|
+
<card:><delete-button: update="primary-collection-count"></delete-button:></card:>
|
159
|
+
</collection>
|
160
|
+
</do>
|
161
|
+
|
162
|
+
<do with="&@this.send(primary_collection_name)">
|
163
|
+
<if test="&can_create?" param="primary-collection-add">
|
164
|
+
<nav class="new-link"><a action="new" if="&linkable?(:new) && can_create?"/></nav>
|
165
|
+
<else>
|
166
|
+
<section class="create-new" with="&new_for_current_user">
|
167
|
+
<h2>Add <A-or-An word="&primary_collection_name.singularize.titleize"/></h2>
|
168
|
+
<form update="primary-collection, primary-collection-count"
|
169
|
+
message="Adding #{primary_collection_name.singularize.titleize}..." reset-form>
|
170
|
+
<field-list skip="#{@this.class.reverse_reflection(@this.send(primary_collection_name).proxy_reflection.name).name}"
|
171
|
+
skip-associations="has_many" param="primary-collection-field-list"/>
|
172
|
+
<submit label="Add"/>
|
173
|
+
</form>
|
174
|
+
</section>
|
175
|
+
</else>
|
176
|
+
</if>
|
177
|
+
</do>
|
178
|
+
</section>
|
179
|
+
</with-primary-collection>
|
167
180
|
</content-body:>
|
181
|
+
|
168
182
|
<aside: param>
|
169
183
|
<section class="preview-collections">
|
170
|
-
<with-fields fields="&non_through_collections - [primary_collection_name.
|
184
|
+
<with-fields fields="&non_through_collections - [(primary_collection_name.to_sym unless primary_collection_name.blank?)]">
|
171
185
|
<collection-preview class="#{this_field.dasherize}"/>
|
172
186
|
</with-fields>
|
173
187
|
</section>
|
174
188
|
</aside:>
|
189
|
+
|
175
190
|
</page>
|
176
191
|
</def>
|
177
192
|
|
@@ -187,7 +202,7 @@
|
|
187
202
|
<content-body: param>
|
188
203
|
<error-messages param/>
|
189
204
|
<form param>
|
190
|
-
<field-list
|
205
|
+
<field-list param/>
|
191
206
|
<div class="actions" param="actions">
|
192
207
|
<submit label="Save Changes" param/><do param="back-link"> or <a>Cancel</a></do>
|
193
208
|
</div>
|
@@ -211,8 +226,7 @@
|
|
211
226
|
<error-messages/>
|
212
227
|
|
213
228
|
<form param>
|
214
|
-
<field-list skip="#{@association.origin.class.reverse_reflection(association_name.to_sym).name}"
|
215
|
-
skip-associations="has_many" param/>
|
229
|
+
<field-list skip="#{@association.origin.class.reverse_reflection(association_name.to_sym).name}" param/>
|
216
230
|
<div class="actions" param="actions">
|
217
231
|
<submit label="Create #{association_name.singularize.titleize}" param/>
|
218
232
|
<do param="back-link"> or <a with="&@association.origin">Cancel</a></do>
|
@@ -329,3 +343,13 @@
|
|
329
343
|
|
330
344
|
<def tag="default-page-title"><%= t = this.to_s; ; "#{t.blank? ? '' : t + ' - '}#{app_name}" %></def>
|
331
345
|
|
346
|
+
|
347
|
+
<def tag="with-primary-collection" attrs="name"><%
|
348
|
+
ivar = "@#{this.class.name.underscore}_#{name}"
|
349
|
+
|
350
|
+
if (collection = instance_variable_get(ivar))
|
351
|
+
%><do with="&collection" param="default"/><%
|
352
|
+
else
|
353
|
+
%><do field="&name" param="default"/><%
|
354
|
+
end
|
355
|
+
%></def>
|
@@ -1,5 +1,6 @@
|
|
1
1
|
<def tag="table-plus" attrs="sort-field, sort-direction, sort-columns" >
|
2
2
|
<% sort_field ||= @sort_field; sort_direction ||= @sort_direction; sort_columns ||= {} %>
|
3
|
+
<% sort_columns['this'] ||= this.member_class.name_attribute %>
|
3
4
|
<div class="table-plus" merge-attrs="&attributes - attrs_for(:with_fields) - attrs_for(:table)">
|
4
5
|
<div class="header" param="header">
|
5
6
|
<div class="search">
|
@@ -12,7 +13,7 @@
|
|
12
13
|
</div>
|
13
14
|
</div>
|
14
15
|
|
15
|
-
<table merge-attrs="&attributes & (attrs_for(:table) + attrs_for(:with_fields))" merge-params>
|
16
|
+
<table merge-attrs="&attributes & (attrs_for(:table) + attrs_for(:with_fields))" empty merge-params>
|
16
17
|
<field-heading-row:>
|
17
18
|
<with-field-names merge-attrs="&all_attributes & attrs_for(:with_fields)">
|
18
19
|
<% col = sort_columns[scope.field_path] || scope.field_path
|
@@ -32,11 +33,9 @@
|
|
32
33
|
<th if="&all_parameters[:controls]" class="controls"/>
|
33
34
|
</field-heading-row>
|
34
35
|
</table>
|
35
|
-
<
|
36
|
-
<do param="empty-message"/>
|
37
|
-
</else>
|
36
|
+
<do param="empty-message" if="empty?"/>
|
38
37
|
|
39
|
-
<nav class="page">
|
38
|
+
<nav class="page" if="&this.respond_to? :page_count">
|
40
39
|
<page-nav param/>
|
41
40
|
</nav>
|
42
41
|
</div>
|
@@ -1,12 +1,13 @@
|
|
1
1
|
<def tag="with-fields" attrs="fields, associations, skip, skip-associations, include-timestamps, force-all"><%
|
2
|
+
fields.nil? || associations.nil? or raise ArgumentError, "with-fields -- specify either fields or associations but not both"
|
3
|
+
|
2
4
|
field_names = if associations == "has_many"
|
3
5
|
this.class.reflections.values.select { |refl| refl.macro == :has_many }.map { |refl| refl.name.to_s }
|
4
6
|
|
5
7
|
elsif fields.nil? || fields == "*" || fields.is_a?(Class)
|
6
8
|
klass = fields.is_a?(Class) ? fields : this.class
|
7
9
|
columns = klass.content_columns.*.name
|
8
|
-
columns -= %w{created_at updated_at created_on updated_on} unless
|
9
|
-
include_timestamps
|
10
|
+
columns -= %w{created_at updated_at created_on updated_on} unless include_timestamps
|
10
11
|
|
11
12
|
if skip_associations == "has_many"
|
12
13
|
assocs = this.class.reflections.values.reject {|r| r.macro == :has_many }.map &its.name.to_s
|
@@ -22,7 +23,13 @@
|
|
22
23
|
end
|
23
24
|
field_names -= comma_split(skip) if skip
|
24
25
|
field_names = field_names.select {|f| can_view?(this, f)} unless force_all
|
25
|
-
field_names.each do |field|
|
26
|
+
field_names.each do |field|
|
27
|
+
if field == "this"
|
28
|
+
%><do param="default"/><%
|
29
|
+
else
|
30
|
+
%><with field="&field"><do param="default"/></with><%
|
31
|
+
end
|
32
|
+
end
|
26
33
|
%></def>
|
27
34
|
|
28
35
|
<def tag="with-field-names" attrs="fields, skip, skip-associations, include-timestamps"><%=
|
@@ -47,7 +54,7 @@
|
|
47
54
|
field_names -= comma_split(skip) if skip
|
48
55
|
scope.new_scope do
|
49
56
|
field_names.map do |n|
|
50
|
-
scope.field_name = n.to_s.gsub("." , "_")
|
57
|
+
scope.field_name = n == "this" ? this.member_class.name : n.to_s.gsub("." , "_")
|
51
58
|
scope.field_path = n
|
52
59
|
parameters.default
|
53
60
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hobo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Locke
|
@@ -9,10 +9,18 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-04-07 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hobosupport
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.7.4
|
23
|
+
version:
|
16
24
|
description:
|
17
25
|
email: tom@hobocentral.net
|
18
26
|
executables:
|
@@ -151,6 +159,15 @@ files:
|
|
151
159
|
- hobo_files/plugin/lib/hobo/dryml
|
152
160
|
- hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb
|
153
161
|
- hobo_files/plugin/lib/hobo/dryml/dryml_support_controller.rb
|
162
|
+
- hobo_files/plugin/lib/hobo/dryml/parser
|
163
|
+
- hobo_files/plugin/lib/hobo/dryml/parser/attribute.rb
|
164
|
+
- hobo_files/plugin/lib/hobo/dryml/parser/base_parser.rb
|
165
|
+
- hobo_files/plugin/lib/hobo/dryml/parser/document.rb
|
166
|
+
- hobo_files/plugin/lib/hobo/dryml/parser/element.rb
|
167
|
+
- hobo_files/plugin/lib/hobo/dryml/parser/elements.rb
|
168
|
+
- hobo_files/plugin/lib/hobo/dryml/parser/source.rb
|
169
|
+
- hobo_files/plugin/lib/hobo/dryml/parser/text.rb
|
170
|
+
- hobo_files/plugin/lib/hobo/dryml/parser/tree_parser.rb
|
154
171
|
- hobo_files/plugin/lib/hobo/dryml/part_context.rb
|
155
172
|
- hobo_files/plugin/lib/hobo/dryml/scoped_variables.rb
|
156
173
|
- hobo_files/plugin/lib/hobo/dryml/tag_parameters.rb
|
@@ -168,6 +185,7 @@ files:
|
|
168
185
|
- hobo_files/plugin/lib/hobo/model_support.rb
|
169
186
|
- hobo_files/plugin/lib/hobo/rapid_helper.rb
|
170
187
|
- hobo_files/plugin/lib/hobo/scopes
|
188
|
+
- hobo_files/plugin/lib/hobo/scopes/apply_scopes.rb
|
171
189
|
- hobo_files/plugin/lib/hobo/scopes/association_proxy_extensions.rb
|
172
190
|
- hobo_files/plugin/lib/hobo/scopes/automatic_scopes.rb
|
173
191
|
- hobo_files/plugin/lib/hobo/scopes/defined_scope_proxy_extender.rb
|
@@ -180,7 +198,6 @@ files:
|
|
180
198
|
- hobo_files/plugin/lib/hobo/user.rb
|
181
199
|
- hobo_files/plugin/lib/hobo/user_controller.rb
|
182
200
|
- hobo_files/plugin/lib/hobo.rb
|
183
|
-
- hobo_files/plugin/lib/rexml.rb
|
184
201
|
- hobo_files/plugin/LICENSE.txt
|
185
202
|
- hobo_files/plugin/Rakefile
|
186
203
|
- hobo_files/plugin/README
|
@@ -226,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
226
243
|
version:
|
227
244
|
requirements: []
|
228
245
|
|
229
|
-
rubyforge_project:
|
246
|
+
rubyforge_project: hobo
|
230
247
|
rubygems_version: 1.0.1
|
231
248
|
signing_key:
|
232
249
|
specification_version: 2
|
@@ -1,443 +0,0 @@
|
|
1
|
-
# Extensions to XML Parsing
|
2
|
-
#
|
3
|
-
# 1. Hobo needs to process XML as transparently as possibe. In the
|
4
|
-
# case of tags that are not defined (i.e. html tags), they should pass
|
5
|
-
# through just as they were in the dryml source. Recontructing the
|
6
|
-
# tags from the DOM is not good enough. The extensions to REXML in
|
7
|
-
# here allow Hobo to use the original start tag source in the output.
|
8
|
-
#
|
9
|
-
# 2. some fixes/extras to allow error messages with line numbers.
|
10
|
-
#
|
11
|
-
# 3. Attributes without a RHS are allowed. They are returned as having
|
12
|
-
# a value of +true+ (the Ruby value, not the string 'true')
|
13
|
-
#
|
14
|
-
# 1 and 2 are achieved by adding two instance variables to Element
|
15
|
-
# nodes : @start_tag_source and @source_offset
|
16
|
-
#
|
17
|
-
# So cool that Ruby allows us to redefine a method. Such a shame the method
|
18
|
-
# we needed to change happened to be 200 lines long :-(
|
19
|
-
|
20
|
-
require 'rexml/document'
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
module REXML
|
25
|
-
module Parsers
|
26
|
-
|
27
|
-
class TreeParser
|
28
|
-
def initialize( source, build_context = Document.new )
|
29
|
-
@build_context = build_context
|
30
|
-
@parser = Parsers::BaseParser.new(source)
|
31
|
-
@parser.dryml_mode = build_context.context[:dryml_mode]
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class BaseParser
|
36
|
-
|
37
|
-
DRYML_NAME_STR= "#{NCNAME_STR}(?::(?:#{NCNAME_STR})?)?"
|
38
|
-
DRYML_ATTRIBUTE_PATTERN = /\s*(#{NAME_STR})(?:\s*=\s*(["'])(.*?)\2)?/um
|
39
|
-
DRYML_TAG_MATCH = /^<((?>#{DRYML_NAME_STR}))\s*((?>\s+#{NAME_STR}(?:\s*=\s*(["']).*?\3)?)*)\s*(\/)?>/um
|
40
|
-
DRYML_CLOSE_MATCH = /^\s*<\/(#{DRYML_NAME_STR})\s*>/um
|
41
|
-
|
42
|
-
attr_writer :dryml_mode
|
43
|
-
def dryml_mode?
|
44
|
-
@dryml_mode
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
def pull
|
49
|
-
if @closed
|
50
|
-
x, @closed = @closed, nil
|
51
|
-
return [ :end_element, x, false ]
|
52
|
-
end
|
53
|
-
return [ :end_document ] if empty?
|
54
|
-
return @stack.shift if @stack.size > 0
|
55
|
-
@source.read if @source.buffer.size<2
|
56
|
-
if @document_status == nil
|
57
|
-
@source.consume(/^\s*/um)
|
58
|
-
word = @source.match(/(<[^>]*)>/um)
|
59
|
-
word = word[1] unless word.nil?
|
60
|
-
case word
|
61
|
-
when COMMENT_START
|
62
|
-
return [ :comment, @source.match(COMMENT_PATTERN, true)[1] ]
|
63
|
-
when XMLDECL_START
|
64
|
-
results = @source.match(XMLDECL_PATTERN, true)[1]
|
65
|
-
version = VERSION.match(results)
|
66
|
-
version = version[1] unless version.nil?
|
67
|
-
encoding = ENCODING.match(results)
|
68
|
-
encoding = encoding[1] unless encoding.nil?
|
69
|
-
@source.encoding = encoding
|
70
|
-
standalone = STANDALONE.match(results)
|
71
|
-
standalone = standalone[1] unless standalone.nil?
|
72
|
-
return [ :xmldecl, version, encoding, standalone]
|
73
|
-
when INSTRUCTION_START
|
74
|
-
return [ :processing_instruction, *@source.match(INSTRUCTION_PATTERN, true)[1,2] ]
|
75
|
-
when DOCTYPE_START
|
76
|
-
md = @source.match(DOCTYPE_PATTERN, true)
|
77
|
-
identity = md[1]
|
78
|
-
close = md[2]
|
79
|
-
identity =~ IDENTITY
|
80
|
-
name = $1
|
81
|
-
raise REXML::ParseException("DOCTYPE is missing a name") if name.nil?
|
82
|
-
pub_sys = $2.nil? ? nil : $2.strip
|
83
|
-
long_name = $3.nil? ? nil : $3.strip
|
84
|
-
uri = $4.nil? ? nil : $4.strip
|
85
|
-
args = [ :start_doctype, name, pub_sys, long_name, uri ]
|
86
|
-
if close == ">"
|
87
|
-
@document_status = :after_doctype
|
88
|
-
@source.read if @source.buffer.size<2
|
89
|
-
md = @source.match(/^\s*/um, true)
|
90
|
-
@stack << [ :end_doctype ]
|
91
|
-
else
|
92
|
-
@document_status = :in_doctype
|
93
|
-
end
|
94
|
-
return args
|
95
|
-
else
|
96
|
-
@document_status = :after_doctype
|
97
|
-
@source.read if @source.buffer.size<2
|
98
|
-
md = @source.match(/\s*/um, true)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
if @document_status == :in_doctype
|
102
|
-
md = @source.match(/\s*(.*?>)/um)
|
103
|
-
case md[1]
|
104
|
-
when SYSTEMENTITY
|
105
|
-
match = @source.match(SYSTEMENTITY, true)[1]
|
106
|
-
return [ :externalentity, match ]
|
107
|
-
|
108
|
-
when ELEMENTDECL_START
|
109
|
-
return [ :elementdecl, @source.match(ELEMENTDECL_PATTERN, true)[1] ]
|
110
|
-
|
111
|
-
when ENTITY_START
|
112
|
-
match = @source.match(ENTITYDECL, true).to_a.compact
|
113
|
-
match[0] = :entitydecl
|
114
|
-
ref = false
|
115
|
-
if match[1] == '%'
|
116
|
-
ref = true
|
117
|
-
match.delete_at 1
|
118
|
-
end
|
119
|
-
# Now we have to sort out what kind of entity reference this is
|
120
|
-
if match[2] == 'SYSTEM'
|
121
|
-
# External reference
|
122
|
-
match[3] = match[3][1..-2] # PUBID
|
123
|
-
match.delete_at(4) if match.size > 4 # Chop out NDATA decl
|
124
|
-
# match is [ :entity, name, SYSTEM, pubid(, ndata)? ]
|
125
|
-
elsif match[2] == 'PUBLIC'
|
126
|
-
# External reference
|
127
|
-
match[3] = match[3][1..-2] # PUBID
|
128
|
-
match[4] = match[4][1..-2] # HREF
|
129
|
-
# match is [ :entity, name, PUBLIC, pubid, href ]
|
130
|
-
else
|
131
|
-
match[2] = match[2][1..-2]
|
132
|
-
match.pop if match.size == 4
|
133
|
-
# match is [ :entity, name, value ]
|
134
|
-
end
|
135
|
-
match << '%' if ref
|
136
|
-
return match
|
137
|
-
when ATTLISTDECL_START
|
138
|
-
md = @source.match(ATTLISTDECL_PATTERN, true)
|
139
|
-
raise REXML::ParseException.new("Bad ATTLIST declaration!", @source) if md.nil?
|
140
|
-
element = md[1]
|
141
|
-
contents = md[0]
|
142
|
-
|
143
|
-
pairs = {}
|
144
|
-
values = md[0].scan(ATTDEF_RE)
|
145
|
-
values.each do |attdef|
|
146
|
-
unless attdef[3] == "#IMPLIED"
|
147
|
-
attdef.compact!
|
148
|
-
val = attdef[3]
|
149
|
-
val = attdef[4] if val == "#FIXED "
|
150
|
-
pairs[attdef[0]] = val
|
151
|
-
end
|
152
|
-
end
|
153
|
-
return [ :attlistdecl, element, pairs, contents ]
|
154
|
-
when NOTATIONDECL_START
|
155
|
-
md = nil
|
156
|
-
if @source.match(PUBLIC)
|
157
|
-
md = @source.match(PUBLIC, true)
|
158
|
-
elsif @source.match(SYSTEM)
|
159
|
-
md = @source.match(SYSTEM, true)
|
160
|
-
else
|
161
|
-
raise REXML::ParseException.new("error parsing notation: no matching pattern", @source)
|
162
|
-
end
|
163
|
-
return [ :notationdecl, md[1], md[2], md[3] ]
|
164
|
-
when CDATA_END
|
165
|
-
@document_status = :after_doctype
|
166
|
-
@source.match(CDATA_END, true)
|
167
|
-
return [ :end_doctype ]
|
168
|
-
end
|
169
|
-
end
|
170
|
-
begin
|
171
|
-
if @source.buffer[0] == ?<
|
172
|
-
if @source.buffer[1] == ?/
|
173
|
-
last_tag, line_no = @tags.pop
|
174
|
-
#md = @source.match_to_consume('>', CLOSE_MATCH)
|
175
|
-
md = @source.match(dryml_mode? ? DRYML_CLOSE_MATCH : CLOSE_MATCH, true)
|
176
|
-
|
177
|
-
valid_end_tag = if dryml_mode?
|
178
|
-
last_tag =~ /^#{Regexp.escape(md[1])}(:.*)?/
|
179
|
-
else
|
180
|
-
last_tag == md[1]
|
181
|
-
end
|
182
|
-
raise REXML::ParseException.new("Missing end tag for "+
|
183
|
-
"'#{last_tag}' (line #{line_no}) (got \"#{md[1]}\")",
|
184
|
-
@source) unless valid_end_tag
|
185
|
-
return [ :end_element, last_tag, true ]
|
186
|
-
elsif @source.buffer[1] == ?!
|
187
|
-
md = @source.match(/\A(\s*[^>]*>)/um)
|
188
|
-
raise REXML::ParseException.new("Malformed node", @source) unless md
|
189
|
-
if md[0][2] == ?-
|
190
|
-
md = @source.match(COMMENT_PATTERN, true)
|
191
|
-
return [ :comment, md[1] ] if md
|
192
|
-
else
|
193
|
-
md = @source.match(CDATA_PATTERN, true)
|
194
|
-
return [ :cdata, md[1] ] if md
|
195
|
-
end
|
196
|
-
raise REXML::ParseException.new("Declarations can only occur "+
|
197
|
-
"in the doctype declaration.", @source)
|
198
|
-
elsif @source.buffer[1] == ??
|
199
|
-
md = @source.match(INSTRUCTION_PATTERN, true)
|
200
|
-
return [ :processing_instruction, md[1], md[2] ] if md
|
201
|
-
raise REXML::ParseException.new("Bad instruction declaration",
|
202
|
-
@source)
|
203
|
-
else
|
204
|
-
# Get the next tag
|
205
|
-
md = @source.match(dryml_mode? ? DRYML_TAG_MATCH : TAG_MATCH, true)
|
206
|
-
raise REXML::ParseException.new("malformed XML: missing tag start", @source) unless md
|
207
|
-
attrs = []
|
208
|
-
if md[2].size > 0
|
209
|
-
attrs = md[2].scan(dryml_mode? ? DRYML_ATTRIBUTE_PATTERN : ATTRIBUTE_PATTERN)
|
210
|
-
raise REXML::ParseException.new("error parsing attributes: [#{attrs.join ', '}], excess = \"#$'\"",
|
211
|
-
@source) if $' and $'.strip.size > 0
|
212
|
-
end
|
213
|
-
|
214
|
-
if md[4]
|
215
|
-
@closed = md[1]
|
216
|
-
else
|
217
|
-
cl = @source.current_line
|
218
|
-
@tags.push([md[1], cl && cl[2]])
|
219
|
-
end
|
220
|
-
attributes = {}
|
221
|
-
attrs.each { |a,b,c| attributes[a] = (c || true) }
|
222
|
-
return [ :start_element, md[1], attributes, md[0],
|
223
|
-
@source.respond_to?(:last_match_offset) && @source.last_match_offset ]
|
224
|
-
end
|
225
|
-
else
|
226
|
-
md = @source.match(TEXT_PATTERN, true)
|
227
|
-
if md[0].length == 0
|
228
|
-
@source.match(/(\s+)/, true)
|
229
|
-
end
|
230
|
-
#return [ :text, "" ] if md[0].length == 0
|
231
|
-
# unnormalized = Text::unnormalize(md[1], self)
|
232
|
-
# return PullEvent.new(:text, md[1], unnormalized)
|
233
|
-
return [ :text, md[1] ]
|
234
|
-
end
|
235
|
-
rescue REXML::ParseException
|
236
|
-
raise
|
237
|
-
rescue Exception, NameError => error
|
238
|
-
raise REXML::ParseException.new("Exception parsing", @source, self, (error ? error : $!))
|
239
|
-
end
|
240
|
-
return [ :dummy ]
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
class TreeParser
|
245
|
-
def parse
|
246
|
-
tag_stack = []
|
247
|
-
in_doctype = false
|
248
|
-
entities = nil
|
249
|
-
begin
|
250
|
-
while true
|
251
|
-
event = @parser.pull
|
252
|
-
case event[0]
|
253
|
-
when :end_document
|
254
|
-
return
|
255
|
-
when :start_element
|
256
|
-
tag_stack.push(event[1])
|
257
|
-
# find the observers for namespaces
|
258
|
-
@build_context = @build_context.add_element(event[1], event[2])
|
259
|
-
@build_context.start_tag_source = event[3]
|
260
|
-
@build_context.source_offset = event[4]
|
261
|
-
when :end_element
|
262
|
-
tag_stack.pop
|
263
|
-
@build_context.has_end_tag = event[2]
|
264
|
-
@build_context = @build_context.parent
|
265
|
-
when :text
|
266
|
-
if not in_doctype
|
267
|
-
if @build_context[-1].instance_of? Text
|
268
|
-
@build_context[-1] << event[1]
|
269
|
-
else
|
270
|
-
@build_context.add(
|
271
|
-
Text.new(event[1], @build_context.whitespace, nil, true)
|
272
|
-
) unless (
|
273
|
-
event[1].strip.size==0 and
|
274
|
-
@build_context.ignore_whitespace_nodes
|
275
|
-
)
|
276
|
-
end
|
277
|
-
end
|
278
|
-
when :comment
|
279
|
-
c = Comment.new(event[1])
|
280
|
-
@build_context.add(c)
|
281
|
-
when :cdata
|
282
|
-
c = CData.new(event[1])
|
283
|
-
@build_context.add(c)
|
284
|
-
when :processing_instruction
|
285
|
-
@build_context.add(Instruction.new(event[1], event[2]))
|
286
|
-
when :end_doctype
|
287
|
-
in_doctype = false
|
288
|
-
entities.each { |k,v| entities[k] = @build_context.entities[k].value }
|
289
|
-
@build_context = @build_context.parent
|
290
|
-
when :start_doctype
|
291
|
-
doctype = DocType.new(event[1..-1], @build_context)
|
292
|
-
@build_context = doctype
|
293
|
-
entities = {}
|
294
|
-
in_doctype = true
|
295
|
-
when :attlistdecl
|
296
|
-
n = AttlistDecl.new(event[1..-1])
|
297
|
-
@build_context.add(n)
|
298
|
-
when :externalentity
|
299
|
-
n = ExternalEntity.new(event[1])
|
300
|
-
@build_context.add(n)
|
301
|
-
when :elementdecl
|
302
|
-
n = ElementDecl.new(event[1])
|
303
|
-
@build_context.add(n)
|
304
|
-
when :entitydecl
|
305
|
-
entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/
|
306
|
-
@build_context.add(Entity.new(event))
|
307
|
-
when :notationdecl
|
308
|
-
n = NotationDecl.new(*event[1..-1])
|
309
|
-
@build_context.add(n)
|
310
|
-
when :xmldecl
|
311
|
-
x = XMLDecl.new(event[1], event[2], event[3])
|
312
|
-
@build_context.add(x)
|
313
|
-
end
|
314
|
-
end
|
315
|
-
rescue REXML::Validation::ValidationException
|
316
|
-
raise
|
317
|
-
rescue
|
318
|
-
raise ParseException.new($!.message, @parser.source, @parser, $!)
|
319
|
-
end
|
320
|
-
end
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
class Document
|
325
|
-
|
326
|
-
attr_accessor :default_attribute_value
|
327
|
-
|
328
|
-
end
|
329
|
-
|
330
|
-
class Element
|
331
|
-
|
332
|
-
def dryml_name
|
333
|
-
expanded_name.sub(/:.*/, "")
|
334
|
-
end
|
335
|
-
|
336
|
-
attr_accessor :start_tag_source, :source_offset
|
337
|
-
|
338
|
-
attr_writer :has_end_tag
|
339
|
-
def has_end_tag?
|
340
|
-
@has_end_tag
|
341
|
-
end
|
342
|
-
|
343
|
-
def parameter_tag?
|
344
|
-
expanded_name =~ /:$/
|
345
|
-
end
|
346
|
-
|
347
|
-
end
|
348
|
-
|
349
|
-
class Attribute
|
350
|
-
|
351
|
-
def initialize_with_dryml(first, second=nil, parent=nil)
|
352
|
-
initialize_without_dryml(first, second, parent)
|
353
|
-
if first.is_a?(String) && second == true
|
354
|
-
@value = true
|
355
|
-
end
|
356
|
-
end
|
357
|
-
alias_method_chain :initialize, :dryml
|
358
|
-
|
359
|
-
def value_with_dryml
|
360
|
-
if has_rhs?
|
361
|
-
value_without_dryml
|
362
|
-
else
|
363
|
-
element.document.default_attribute_value
|
364
|
-
end
|
365
|
-
end
|
366
|
-
alias_method_chain :value, :dryml
|
367
|
-
|
368
|
-
def to_string_with_dryml
|
369
|
-
if has_rhs?
|
370
|
-
to_string_without_dryml
|
371
|
-
else
|
372
|
-
@expanded_name
|
373
|
-
end
|
374
|
-
end
|
375
|
-
alias_method_chain :to_string, :dryml
|
376
|
-
|
377
|
-
def has_rhs?
|
378
|
-
@value != true
|
379
|
-
end
|
380
|
-
|
381
|
-
end
|
382
|
-
|
383
|
-
end
|
384
|
-
|
385
|
-
module Hobo::Dryml
|
386
|
-
|
387
|
-
|
388
|
-
# A REXML source that keeps track of where in the buffer it is
|
389
|
-
class RexSource < REXML::Source
|
390
|
-
|
391
|
-
def initialize(src)
|
392
|
-
super(src)
|
393
|
-
@buffer_offset = 0
|
394
|
-
end
|
395
|
-
|
396
|
-
attr_reader :last_match_offset
|
397
|
-
|
398
|
-
def remember_match(m)
|
399
|
-
if m
|
400
|
-
@last_match = m
|
401
|
-
@last_match_offset = @buffer_offset + m.begin(0)
|
402
|
-
@orig[@last_match_offset..@last_match_offset+m[0].length] == @buffer[m.begin(0)..m.end(0)]
|
403
|
-
end
|
404
|
-
m
|
405
|
-
end
|
406
|
-
|
407
|
-
def advance_buffer(md)
|
408
|
-
@buffer = md.post_match
|
409
|
-
@buffer_offset += md.end(0)
|
410
|
-
end
|
411
|
-
|
412
|
-
def scan(pattern, cons=false)
|
413
|
-
raise '!'
|
414
|
-
return nil if @buffer.nil?
|
415
|
-
rv = @buffer.scan(pattern)
|
416
|
-
if cons and rv.size > 0
|
417
|
-
advance_buffer(Regexp.last_match)
|
418
|
-
end
|
419
|
-
rv
|
420
|
-
end
|
421
|
-
|
422
|
-
def consume(pattern)
|
423
|
-
md = remember_match(pattern.match(@buffer))
|
424
|
-
if md
|
425
|
-
advance_buffer(md)
|
426
|
-
@buffer
|
427
|
-
end
|
428
|
-
end
|
429
|
-
|
430
|
-
def match(pattern, cons=false)
|
431
|
-
md = remember_match(pattern.match(@buffer))
|
432
|
-
advance_buffer(md) if cons and md
|
433
|
-
return md
|
434
|
-
end
|
435
|
-
|
436
|
-
def current_line
|
437
|
-
pos = last_match_offset || 0
|
438
|
-
[0, 0, @orig[0..pos].count("\n") + 1]
|
439
|
-
end
|
440
|
-
|
441
|
-
end
|
442
|
-
|
443
|
-
end
|