hobo 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/hobo +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
|