hobo 0.8.8 → 0.8.9
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/CHANGES.txt +34 -0
- data/Rakefile +30 -24
- data/bin/hobo +30 -10
- data/doctest/hobo/hobo_helper.rdoctest +92 -0
- data/doctest/hobo/lifecycles.rdoctest +261 -0
- data/doctest/scopes.rdoctest +387 -0
- data/dryml_generators/rapid/forms.dryml.erb +3 -3
- data/dryml_generators/rapid/pages.dryml.erb +4 -4
- data/lib/active_record/viewhints_validations_interceptor.rb +1 -1
- data/lib/hobo.rb +1 -1
- data/lib/hobo/accessible_associations.rb +3 -3
- data/lib/hobo/authentication_support.rb +1 -1
- data/lib/hobo/dryml.rb +10 -0
- data/lib/hobo/dryml/taglib.rb +3 -5
- data/lib/hobo/hobo_helper.rb +3 -1
- data/lib/hobo/include_in_save.rb +1 -0
- data/lib/hobo/lifecycles/actions.rb +6 -2
- data/lib/hobo/model.rb +1 -1
- data/lib/hobo/model_controller.rb +34 -12
- data/lib/hobo/permissions.rb +1 -1
- data/lib/hobo/rapid_helper.rb +3 -0
- data/lib/hobo/scopes/association_proxy_extensions.rb +8 -2
- data/lib/hobo/scopes/automatic_scopes.rb +3 -3
- data/lib/hobo/user_controller.rb +2 -1
- data/rails_generators/hobo/hobo_generator.rb +1 -1
- data/rails_generators/hobo/templates/application.dryml +0 -2
- data/rails_generators/hobo_admin_site/hobo_admin_site_generator.rb +45 -0
- data/rails_generators/hobo_admin_site/templates/admin.css +2 -0
- data/rails_generators/hobo_admin_site/templates/application.dryml +1 -0
- data/rails_generators/hobo_admin_site/templates/controller.rb +13 -0
- data/rails_generators/hobo_admin_site/templates/site_taglib.dryml +32 -0
- data/rails_generators/hobo_admin_site/templates/users_index.dryml +5 -0
- data/rails_generators/hobo_front_controller/hobo_front_controller_generator.rb +7 -1
- data/rails_generators/hobo_front_controller/templates/index.dryml +16 -0
- data/rails_generators/hobo_rapid/hobo_rapid_generator.rb +31 -1
- data/rails_generators/hobo_rapid/templates/hobo-rapid.js +5 -3
- data/rails_generators/hobo_rapid/templates/lowpro.js +40 -21
- data/rails_generators/hobo_rapid/templates/themes/clean/public/images/101-3B5F87-ACD3E6.png +0 -0
- data/rails_generators/hobo_rapid/templates/themes/clean/public/images/30-3E547A-242E42.png +0 -0
- data/rails_generators/hobo_rapid/templates/themes/clean/public/images/30-DBE1E5-FCFEF5.png +0 -0
- data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +12 -4
- data/rails_generators/hobo_subsite/hobo_subsite_generator.rb +1 -1
- data/rails_generators/hobo_user_controller/hobo_user_controller_generator.rb +22 -0
- data/rails_generators/hobo_user_controller/templates/accept_invitation.dryml +5 -0
- data/rails_generators/hobo_user_controller/templates/controller.rb +22 -0
- data/rails_generators/hobo_user_model/hobo_user_model_generator.rb +17 -1
- data/rails_generators/hobo_user_model/templates/invite.erb +9 -0
- data/rails_generators/hobo_user_model/templates/mailer.rb +15 -0
- data/rails_generators/hobo_user_model/templates/model.rb +31 -4
- data/taglibs/rapid_core.dryml +25 -6
- data/taglibs/rapid_forms.dryml +65 -24
- data/taglibs/rapid_lifecycles.dryml +1 -1
- data/taglibs/rapid_navigation.dryml +2 -2
- data/taglibs/rapid_plus.dryml +4 -3
- metadata +151 -210
- data/Manifest +0 -155
- data/hobo.gemspec +0 -46
- data/rails_generators/hobo_rapid/templates/themes/clean/public/images/100-3B5F87-ACD3E6.png +0 -0
@@ -95,10 +95,10 @@ module Hobo
|
|
95
95
|
if options[:accessible]
|
96
96
|
class_eval %{
|
97
97
|
def #{name}_with_accessible=(array_or_hash)
|
98
|
-
|
99
|
-
self.#{name}_without_accessible =
|
98
|
+
__items = Hobo::AccessibleAssociations.prepare_has_many_assignment(#{name}, :#{name}, array_or_hash)
|
99
|
+
self.#{name}_without_accessible = __items
|
100
100
|
# ensure the loaded array contains any changed records
|
101
|
-
self.#{name}.proxy_target[0..-1] =
|
101
|
+
self.#{name}.proxy_target[0..-1] = __items
|
102
102
|
end
|
103
103
|
}, __FILE__, __LINE__ - 7
|
104
104
|
alias_method_chain :"#{name}=", :accessible
|
@@ -67,7 +67,7 @@ module Hobo
|
|
67
67
|
accepts.xml do
|
68
68
|
headers["Status"] = "Unauthorized"
|
69
69
|
headers["WWW-Authenticate"] = %(Basic realm="Web Password")
|
70
|
-
render :text => "
|
70
|
+
render :text => "Couldn't authenticate you", :status => '401 Unauthorized'
|
71
71
|
end
|
72
72
|
end
|
73
73
|
false
|
data/lib/hobo/dryml.rb
CHANGED
@@ -41,6 +41,16 @@ module Hobo
|
|
41
41
|
DrymlGenerator.enable
|
42
42
|
end
|
43
43
|
|
44
|
+
|
45
|
+
def precompile_taglibs
|
46
|
+
Dir.chdir(RAILS_ROOT) do
|
47
|
+
taglibs = Dir["vendor/plugins/**/taglibs/**/*.dryml"] + Dir["app/views/taglibs/**/*.dryml"]
|
48
|
+
taglibs.each do |f|
|
49
|
+
Hobo::Dryml::Taglib.get(:template_dir => File.dirname(f), :src => File.basename(f).remove(".dryml"))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
44
54
|
|
45
55
|
def clear_cache
|
46
56
|
@renderer_classes = {}
|
data/lib/hobo/dryml/taglib.rb
CHANGED
@@ -9,16 +9,14 @@ module Hobo
|
|
9
9
|
class << self
|
10
10
|
|
11
11
|
def get(options)
|
12
|
-
|
13
|
-
taglib = @cache[
|
12
|
+
src_file = taglib_filename(options)
|
13
|
+
taglib = @cache[src_file]
|
14
14
|
if taglib
|
15
15
|
taglib.reload
|
16
16
|
else
|
17
|
-
src_file = taglib_filename(options)
|
18
17
|
bundle = options[:bundle] && Bundle.bundles[options[:bundle]]
|
19
|
-
|
20
18
|
taglib = Taglib.new(src_file, bundle)
|
21
|
-
@cache[
|
19
|
+
@cache[src_file] = taglib
|
22
20
|
end
|
23
21
|
taglib
|
24
22
|
end
|
data/lib/hobo/hobo_helper.rb
CHANGED
@@ -162,9 +162,10 @@ module Hobo
|
|
162
162
|
# TODO: Calls to respond_to? in here can cause the full collection hiding behind a scoped collection to get loaded
|
163
163
|
res = []
|
164
164
|
empty = true
|
165
|
-
scope.new_scope(:repeat_collection => enum, :even_odd => 'odd') do
|
165
|
+
scope.new_scope(:repeat_collection => enum, :even_odd => 'odd', :repeat_item => nil) do
|
166
166
|
if enum.respond_to?(:each_pair)
|
167
167
|
enum.each_pair do |key, value|
|
168
|
+
scope.repeat_item = value
|
168
169
|
empty = false;
|
169
170
|
self.this_key = key;
|
170
171
|
new_object_context(value) { res << yield }
|
@@ -173,6 +174,7 @@ module Hobo
|
|
173
174
|
else
|
174
175
|
index = 0
|
175
176
|
enum.each do |e|
|
177
|
+
scope.repeat_item = e
|
176
178
|
empty = false;
|
177
179
|
if enum == this
|
178
180
|
new_field_context(index, e) { res << yield }
|
data/lib/hobo/include_in_save.rb
CHANGED
@@ -16,6 +16,7 @@ module Hobo
|
|
16
16
|
def validate_included_in_save
|
17
17
|
if included_in_save
|
18
18
|
included_in_save._?.each_pair do |association, records|
|
19
|
+
next if self.class.reflections[association].options[:validate]==false
|
19
20
|
added = false
|
20
21
|
records.each do |record|
|
21
22
|
# we want to call valid? on each one, but only add the error to self once
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# We need to be able to eval an expression outside of the Hobo module
|
2
|
-
# so that, e.g. "
|
2
|
+
# so that, e.g. "User" doesn't eval to "Hobo::User"
|
3
3
|
# (Ruby determines this constant lookup scope lexically)
|
4
4
|
def __top_level_eval__(obj, expr)
|
5
5
|
obj.instance_eval(expr)
|
@@ -124,7 +124,11 @@ module Hobo
|
|
124
124
|
def get_state(record, state)
|
125
125
|
case state
|
126
126
|
when Proc
|
127
|
-
state.
|
127
|
+
if state.arity == 1
|
128
|
+
state.call(record)
|
129
|
+
else
|
130
|
+
record.instance_eval(&state)
|
131
|
+
end
|
128
132
|
when String
|
129
133
|
eval(state, record.instance_eval { binding })
|
130
134
|
else
|
data/lib/hobo/model.rb
CHANGED
@@ -111,7 +111,7 @@ module Hobo
|
|
111
111
|
self.this = find_instance(opts)
|
112
112
|
raise Hobo::PermissionDeniedError unless @this.method_callable_by?(current_user, method)
|
113
113
|
if got_block
|
114
|
-
instance_eval(&block)
|
114
|
+
this.with_acting_user(current_user) { instance_eval(&block) }
|
115
115
|
else
|
116
116
|
@this.send(method)
|
117
117
|
end
|
@@ -355,9 +355,18 @@ module Hobo
|
|
355
355
|
|
356
356
|
def re_render_form(default_action=nil)
|
357
357
|
if params[:page_path]
|
358
|
+
@invalid_record = this
|
358
359
|
controller, view = Controller.controller_and_view_for(params[:page_path])
|
359
360
|
view = default_action if view == Dryml::EMPTY_PAGE
|
360
|
-
|
361
|
+
|
362
|
+
# Hack fix for Bug 477. See also bug 489.
|
363
|
+
if self.class.name == "#{controller.camelize}Controller" && view == "index"
|
364
|
+
params['action'] = 'index'
|
365
|
+
self.action_name = 'index'
|
366
|
+
index
|
367
|
+
else
|
368
|
+
render :template => "#{controller}/#{view}"
|
369
|
+
end
|
361
370
|
else
|
362
371
|
render :action => default_action
|
363
372
|
end
|
@@ -499,7 +508,7 @@ module Hobo
|
|
499
508
|
options = args.extract_options!
|
500
509
|
self.this ||= args.first || new_for_create
|
501
510
|
this.user_update_attributes(current_user, options[:attributes] || attribute_parameters || {})
|
502
|
-
create_response(:new, &b)
|
511
|
+
create_response(:new, options, &b)
|
503
512
|
end
|
504
513
|
|
505
514
|
|
@@ -508,7 +517,7 @@ module Hobo
|
|
508
517
|
owner, association = find_owner_and_association(owner)
|
509
518
|
self.this ||= args.first || association.new
|
510
519
|
this.user_update_attributes(current_user, options[:attributes] || attribute_parameters || {})
|
511
|
-
create_response(:"new_for_#{owner}", &b)
|
520
|
+
create_response(:"new_for_#{owner}", options, &b)
|
512
521
|
end
|
513
522
|
|
514
523
|
|
@@ -529,10 +538,13 @@ module Hobo
|
|
529
538
|
t
|
530
539
|
end
|
531
540
|
|
541
|
+
def flash_notice(message)
|
542
|
+
flash[:notice] = message unless request.xhr?
|
543
|
+
end
|
532
544
|
|
533
545
|
|
534
546
|
def create_response(new_action, options={}, &b)
|
535
|
-
|
547
|
+
flash_notice "The #{@this.class.name.titleize.downcase} was created successfully" if valid?
|
536
548
|
|
537
549
|
response_block(&b) or
|
538
550
|
if valid?
|
@@ -567,7 +579,7 @@ module Hobo
|
|
567
579
|
|
568
580
|
|
569
581
|
def update_response(in_place_edit_field=nil, options={}, &b)
|
570
|
-
|
582
|
+
flash_notice "Changes to the #{@this.class.name.titleize.downcase} were saved" if valid?
|
571
583
|
|
572
584
|
response_block(&b) or
|
573
585
|
if valid?
|
@@ -603,8 +615,8 @@ module Hobo
|
|
603
615
|
options = args.extract_options!
|
604
616
|
self.this ||= args.first || find_instance
|
605
617
|
this.user_destroy(current_user)
|
606
|
-
|
607
|
-
destroy_response(&b)
|
618
|
+
flash_notice "The #{model.name.titleize.downcase} was deleted"
|
619
|
+
destroy_response(options, &b)
|
608
620
|
end
|
609
621
|
|
610
622
|
|
@@ -619,8 +631,8 @@ module Hobo
|
|
619
631
|
|
620
632
|
# --- Lifecycle Actions --- #
|
621
633
|
|
622
|
-
def creator_page_action(name, &b)
|
623
|
-
self.this
|
634
|
+
def creator_page_action(name, options={}, &b)
|
635
|
+
self.this ||= model.new
|
624
636
|
this.exempt_from_edit_checks = true
|
625
637
|
@creator = model::Lifecycle.creator(name)
|
626
638
|
raise Hobo::PermissionDeniedError unless @creator.allowed?(current_user)
|
@@ -693,8 +705,17 @@ module Hobo
|
|
693
705
|
def hobo_completions(attribute, finder, options={})
|
694
706
|
options = options.reverse_merge(:limit => 10, :param => :query, :query_scope => "#{attribute}_contains")
|
695
707
|
finder = finder.limit(options[:limit]) unless finder.send(:scope, :find, :limit)
|
696
|
-
|
697
|
-
|
708
|
+
|
709
|
+
begin
|
710
|
+
finder = finder.send(options[:query_scope], params[options[:param]])
|
711
|
+
items = finder.find(:all).select { |r| r.viewable_by?(current_user) }
|
712
|
+
rescue TypeError # must be a list of methods instead
|
713
|
+
items = []
|
714
|
+
options[:query_scope].each do |qscope|
|
715
|
+
finder2 = finder.send(qscope, params[options[:param]])
|
716
|
+
items += finder2.find(:all).select { |r| r.viewable_by?(current_user) }
|
717
|
+
end
|
718
|
+
end
|
698
719
|
render :text => "<ul>\n" + items.map {|i| "<li>#{i.send(attribute)}</li>\n"}.join + "</ul>"
|
699
720
|
end
|
700
721
|
|
@@ -717,6 +738,7 @@ module Hobo
|
|
717
738
|
|
718
739
|
def permission_denied(error)
|
719
740
|
self.this = true # Otherwise this gets sent user_view
|
741
|
+
logger.info "Hobo: Permission Denied!"
|
720
742
|
@permission_error = error
|
721
743
|
if "permission_denied".in?(self.class.superclass.instance_methods)
|
722
744
|
super
|
data/lib/hobo/permissions.rb
CHANGED
@@ -330,7 +330,7 @@ module Hobo
|
|
330
330
|
end
|
331
331
|
|
332
332
|
|
333
|
-
# Add some singleton methods to +record+
|
333
|
+
# Add some singleton methods to +record+ to give the effect that +attribute+ is unknown. That is,
|
334
334
|
# attempts to access the attribute will result in a Hobo::UndefinedAccessError
|
335
335
|
def unknownify_attribute(attr)
|
336
336
|
metaclass.class_eval do
|
data/lib/hobo/rapid_helper.rb
CHANGED
@@ -7,9 +7,15 @@ module Hobo
|
|
7
7
|
AssociationProxyExtensions = classy_module do
|
8
8
|
|
9
9
|
def scope_conditions(reflection)
|
10
|
-
scope_name = reflection.options[:scope]
|
11
|
-
|
10
|
+
scope_name = reflection.options[:scope]
|
11
|
+
return nil if scope_name.nil?
|
12
|
+
target_class = reflection.klass
|
13
|
+
return nil if target_class.nil?
|
14
|
+
if scope_name.respond_to? :map
|
15
|
+
combine_conditions(*(scope_name.map {|scope| target_class.send(*scope).scope(:find)[:conditions]}))
|
16
|
+
else
|
12
17
|
target_class.send(scope_name).scope(:find)[:conditions]
|
18
|
+
end
|
13
19
|
end
|
14
20
|
|
15
21
|
|
@@ -178,21 +178,21 @@ module Hobo
|
|
178
178
|
def_scope :conditions => ["#{column_sql(col)} <> ?", true]
|
179
179
|
|
180
180
|
# published_before(time)
|
181
|
-
elsif name =~ /^(.*)_before$/ && (col = column("#{$1}_at")) && col.type.in?([:date, :datetime, :time, :timestamp])
|
181
|
+
elsif name =~ /^(.*)_before$/ && (col = column("#{$1}_at") || column("#{$1}_date") || column("#{$1}_on")) && col.type.in?([:date, :datetime, :time, :timestamp])
|
182
182
|
|
183
183
|
def_scope do |time|
|
184
184
|
{ :conditions => ["#{column_sql(col)} < ?", time] }
|
185
185
|
end
|
186
186
|
|
187
187
|
# published_after(time)
|
188
|
-
elsif name =~ /^(.*)_after$/ && (col = column("#{$1}_at")) && col.type.in?([:date, :datetime, :time, :timestamp])
|
188
|
+
elsif name =~ /^(.*)_after$/ && (col = column("#{$1}_at") || column("#{$1}_date") || column("#{$1}_on")) && col.type.in?([:date, :datetime, :time, :timestamp])
|
189
189
|
|
190
190
|
def_scope do |time|
|
191
191
|
{ :conditions => ["#{column_sql(col)} > ?", time] }
|
192
192
|
end
|
193
193
|
|
194
194
|
# published_between(time1, time2)
|
195
|
-
elsif name =~ /^(.*)_between$/ && (col = column("#{$1}_at")) && col.type.in?([:date, :datetime, :time, :timestamp])
|
195
|
+
elsif name =~ /^(.*)_between$/ && (col = column("#{$1}_at") || column("#{$1}_date") || column("#{$1}_on")) && col.type.in?([:date, :datetime, :time, :timestamp])
|
196
196
|
|
197
197
|
def_scope do |time1, time2|
|
198
198
|
{ :conditions => ["#{column_sql(col)} >= ? AND #{column_sql(col)} <= ?", time1, time2] }
|
data/lib/hobo/user_controller.rb
CHANGED
@@ -13,7 +13,8 @@ module Hobo
|
|
13
13
|
end
|
14
14
|
|
15
15
|
filter_parameter_logging "password"
|
16
|
-
skip_before_filter :login_required, :only => [:login, :signup, :forgot_password, :
|
16
|
+
skip_before_filter :login_required, :only => [:login, :signup, :forgot_password, :reset_password, :do_reset_password,
|
17
|
+
:accept_invitation, :do_accept_invitation]
|
17
18
|
|
18
19
|
include_taglib "rapid_user_pages", :plugin => "hobo"
|
19
20
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
class HoboAdminSiteGenerator < HoboSubsiteGenerator
|
4
|
+
|
5
|
+
default_options :rapid => true
|
6
|
+
|
7
|
+
def manifest
|
8
|
+
m = super
|
9
|
+
|
10
|
+
m.template "admin.css", File.join('public/stylesheets/admin.css')
|
11
|
+
if invite_only?
|
12
|
+
m.dependency "hobo_model_controller", ["admin/user"]
|
13
|
+
m.template "users_index.dryml", "app/views/admin/users/index.dryml"
|
14
|
+
end
|
15
|
+
m
|
16
|
+
end
|
17
|
+
|
18
|
+
def invite_only?
|
19
|
+
options[:invite_only]
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def banner
|
25
|
+
"Usage: #{$0} #{spec.name} [--make-front-site | --no-front-site] [--invite-only]"
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_options!(opt)
|
29
|
+
opt.separator ''
|
30
|
+
opt.separator 'Options:'
|
31
|
+
opt.on("--make-front-site", "rename application.dryml to front_site.dryml") do |v|
|
32
|
+
options[:make_front_site] = true
|
33
|
+
end
|
34
|
+
opt.on("--no-front-site", "do not rename application.dryml to front_site.dryml ") do |v|
|
35
|
+
options[:make_front_site] = false
|
36
|
+
end
|
37
|
+
opt.on("--no-rapid", "don't include Rapid features in the subsite taglib") do |v|
|
38
|
+
options[:rapid] = false
|
39
|
+
end
|
40
|
+
opt.on("--invite-only", "Add features for an invite only website") do |v|
|
41
|
+
options[:invite_only] = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<!-- Global taglib - these tags are shared across all subsites -->
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<!-- Tag definitions for the <%= subsite_name %> subsite -->
|
2
|
+
|
3
|
+
<% if options[:rapid] -%>
|
4
|
+
<include src="rapid" plugin="hobo"/>
|
5
|
+
|
6
|
+
<include src="taglibs/auto/<%= file_name %>/rapid/cards"/>
|
7
|
+
<include src="taglibs/auto/<%= file_name %>/rapid/pages"/>
|
8
|
+
<include src="taglibs/auto/<%= file_name %>/rapid/forms"/>
|
9
|
+
|
10
|
+
<set-theme name="clean"/>
|
11
|
+
<% end -%>
|
12
|
+
|
13
|
+
<def tag="app-name"><%= app_name %></def>
|
14
|
+
|
15
|
+
<extend tag="page">
|
16
|
+
<old-page merge>
|
17
|
+
<append-stylesheets:>
|
18
|
+
<stylesheet name="admin"/>
|
19
|
+
</append-stylesheets:>
|
20
|
+
<footer:>
|
21
|
+
<a href="#{base_url}/">View Site</a>
|
22
|
+
</footer:>
|
23
|
+
</old-page>
|
24
|
+
</extend>
|
25
|
+
<% if invite_only? -%>
|
26
|
+
|
27
|
+
<extend tag="card" for="User">
|
28
|
+
<old-card merge>
|
29
|
+
<append-header:><%%= h this.state.titleize %></append-header:>
|
30
|
+
</old-card>
|
31
|
+
</extend>
|
32
|
+
<% end -%>
|
@@ -62,10 +62,14 @@ class HoboFrontControllerGenerator < Rails::Generator::NamedBase
|
|
62
62
|
return unless File.exists?(index_path)
|
63
63
|
File.unlink(index_path)
|
64
64
|
end
|
65
|
+
|
66
|
+
def invite_only?
|
67
|
+
options[:invite_only]
|
68
|
+
end
|
65
69
|
|
66
70
|
protected
|
67
71
|
def banner
|
68
|
-
"Usage: #{$0} #{spec.name} <controller-name> [--add-routes] [--delete-index]"
|
72
|
+
"Usage: #{$0} #{spec.name} <controller-name> [--add-routes] [--delete-index] [--invite-only]"
|
69
73
|
end
|
70
74
|
|
71
75
|
def add_options!(opt)
|
@@ -75,6 +79,8 @@ class HoboFrontControllerGenerator < Rails::Generator::NamedBase
|
|
75
79
|
"Modify config/routes.rb to support the front controller") { |v| options[:add_routes] = true }
|
76
80
|
opt.on("--delete-index",
|
77
81
|
"Delete public/index.html") { |v| options[:delete_index] = true }
|
82
|
+
opt.on("--invite-only",
|
83
|
+
"Add features for an invite only website") { |v| options[:invite_only] = true }
|
78
84
|
end
|
79
85
|
|
80
86
|
end
|