card 1.99.5 → 1.99.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/db/migrate_core_cards/20140629222005_add_email_cards.rb +1 -1
  4. data/lib/card.rb +13 -23
  5. data/lib/card/act_manager.rb +1 -0
  6. data/lib/card/act_manager/event_delay.rb +10 -5
  7. data/lib/card/act_manager/subdirector_array.rb +2 -0
  8. data/lib/card/auth/current.rb +0 -2
  9. data/lib/card/cache/prepopulate.rb +4 -4
  10. data/lib/card/env.rb +9 -53
  11. data/lib/card/env/location.rb +15 -2
  12. data/lib/card/env/request_assignments.rb +24 -0
  13. data/lib/card/env/serialization.rb +24 -0
  14. data/lib/card/env/slot_options.rb +30 -0
  15. data/lib/card/format/error.rb +6 -1
  16. data/lib/card/mod/loader/set_pattern_loader.rb +5 -1
  17. data/lib/card/model/save_helper.rb +5 -1
  18. data/lib/card/set/event/delayed_event.rb +49 -9
  19. data/lib/card/set/event/options.rb +1 -0
  20. data/lib/card/set/pattern/base.rb +18 -0
  21. data/lib/card/subcards.rb +11 -8
  22. data/lib/card/subcards/add.rb +7 -4
  23. data/lib/card/view/cache/cache_action.rb +1 -1
  24. data/lib/card/view/classy.rb +9 -9
  25. data/lib/card/view/options/voo_api.rb +0 -8
  26. data/mod/account/set/all/account.rb +2 -2
  27. data/mod/account/spec/set/right/account_spec.rb +2 -2
  28. data/mod/account/spec/set/type/signup_spec.rb +1 -1
  29. data/mod/basic_formats/set/all/head.rb +4 -2
  30. data/mod/basic_formats/set/all/rss.rb +1 -1
  31. data/mod/bootstrap/lib/card/lazy_tab.rb +37 -0
  32. data/mod/bootstrap/lib/card/tab.rb +76 -0
  33. data/mod/bootstrap/set/all/bootstrap/dropdown.rb +0 -1
  34. data/mod/bootstrap/set/all/bootstrap/tabs.rb +33 -124
  35. data/mod/bootstrap/set/all/bootstrap/tabs/tab_panel.haml +7 -0
  36. data/mod/carrierwave/set/abstract/attachment.rb +1 -1
  37. data/mod/carrierwave/set/abstract/attachment/upload_cache.rb +1 -1
  38. data/mod/core/lib/card/rule.rb +227 -0
  39. data/mod/core/set/all/actify.rb +16 -2
  40. data/mod/core/set/all/assign_attributes.rb +2 -2
  41. data/mod/core/set/all/cache.rb +4 -0
  42. data/mod/core/set/all/event_conditions.rb +58 -39
  43. data/mod/core/set/all/fetch.rb +1 -1
  44. data/mod/core/set/all/fetch_helper.rb +5 -13
  45. data/mod/core/set/all/initialize.rb +6 -6
  46. data/mod/core/set/all/name_events.rb +2 -3
  47. data/mod/core/set/all/rename.rb +9 -5
  48. data/mod/core/set/all/rules.rb +3 -228
  49. data/mod/core/set/all/tabs.rb +4 -3
  50. data/mod/core/set/all/update_read_rules.rb +1 -1
  51. data/mod/core/set_pattern/08_type_plus_right.rb +1 -2
  52. data/mod/core/spec/set/all/event_conditions_spec.rb +3 -3
  53. data/mod/core/spec/set/all/fetch_helper_spec.rb +6 -0
  54. data/mod/core/spec/set/all/references_spec.rb +9 -0
  55. data/mod/core/spec/set/all/rules_spec.rb +1 -1
  56. data/mod/developer/set/right/debug.rb +13 -14
  57. data/mod/edit/set/all/bridge.rb +7 -7
  58. data/mod/edit/set/all/bridge/tab_visibility.rb +2 -2
  59. data/mod/follow/set/all/follow/followed_by.rb +1 -2
  60. data/mod/follow/set/all/follow/follower_ids.rb +15 -3
  61. data/mod/follow/set/all/notify.rb +1 -5
  62. data/mod/follow/set/type_plus_right/user/follow.rb +4 -7
  63. data/mod/google_analytics/set/all/google_analytics.rb +1 -1
  64. data/mod/history/lib/card/act.rb +1 -1
  65. data/mod/history/set/all/history.rb +23 -17
  66. data/mod/history/set/all/history/acts.rb +0 -4
  67. data/mod/machines/file/all_script_machine_output/file.js +69 -38418
  68. data/mod/machines/file/all_style_machine_output/file.css +1 -1
  69. data/mod/machines/file/script_html5shiv_printshiv_machine_output/file.js +1 -1
  70. data/mod/machines/lib/javascript/decko/filter_items.js.coffee +4 -4
  71. data/mod/machines/lib/javascript/decko/nest_editor_name.js.coffee +1 -1
  72. data/mod/machines/lib/javascript/decko/selectable_filtered_content.js.coffee +12 -0
  73. data/mod/machines/lib/javascript/decko/slotter.js.coffee +1 -1
  74. data/mod/machines/lib/stylesheets/style_cards.scss +1 -1
  75. data/mod/machines/set/abstract/machine/output_update.rb +12 -4
  76. data/mod/machines/set/self/script_decko.rb +1 -1
  77. data/mod/pointer/set/abstract/00_paging_params.rb +1 -3
  78. data/mod/pointer/set/abstract/02_pointer/events.rb +1 -0
  79. data/mod/recaptcha/set/all/recaptcha.rb +1 -1
  80. data/mod/recaptcha/set/self/admin_info.rb +2 -3
  81. data/mod/rules/set/type/set.rb +1 -1
  82. data/mod/search/set/abstract/00_filter_helper.rb +2 -4
  83. data/mod/search/set/abstract/02_search_params.rb +1 -1
  84. data/mod/search/set/abstract/03_filter.rb +2 -0
  85. data/mod/search/set/abstract/03_filter/selectable_filtered_content.haml +2 -0
  86. data/mod/search/template/abstract/search/checkbox_item.haml +2 -3
  87. data/mod/settings/set/right/read.rb +1 -1
  88. data/mod/standard/set/all/rich_html/error.rb +1 -1
  89. data/mod/standard/set/all/rich_html/menu.rb +1 -1
  90. data/mod/standard/set/all/rich_html/show.rb +8 -0
  91. data/mod/tinymce_editor/set/all/reference_editor/nest_editor.rb +6 -8
  92. metadata +13 -5
  93. data/lib/card/tasks/card/seed.rake +0 -0
@@ -48,6 +48,7 @@ class Card
48
48
  opts = normalize_opts stage_or_opts, opts
49
49
  process_stage_opts opts
50
50
  process_action_opts opts
51
+ process_delayed_job_opts opts
51
52
  opts
52
53
  end
53
54
 
@@ -134,6 +134,24 @@ class Card
134
134
  end
135
135
  end
136
136
  end
137
+
138
+ module Helper
139
+ private
140
+
141
+ def left_type card
142
+ card.superleft&.type_name || quick_type(card.name.left_name)
143
+ end
144
+
145
+ def quick_type name
146
+ if name.present?
147
+ card = Card.fetch name, skip_modules: true, new: {}
148
+ card.include_set_modules if card.new? && name.to_name.junction?
149
+ card&.type_name
150
+ else
151
+ "RichText"
152
+ end
153
+ end
154
+ end
137
155
  end
138
156
  end
139
157
  end
@@ -63,16 +63,19 @@ class Card
63
63
  @keys.send method, *args
64
64
  end
65
65
 
66
- def each_card
67
- # fetch all cards first to avoid side effects
68
- # e.g. deleting a user adds follow rules and +*account to subcards
69
- # for deleting but deleting follow rules can remove +*account from the
70
- # cache if it belongs to the rule cards
71
- cards = @keys.map do |key|
66
+ # fetch all cards first to avoid side effects
67
+ # e.g. deleting a user adds follow rules and +*account to subcards
68
+ # for deleting but deleting follow rules can remove +*account from the
69
+ # cache if it belongs to the rule cards
70
+ def cards
71
+ @keys.map do |key|
72
72
  fetch_subcard key
73
- end
73
+ end.compact
74
+ end
75
+
76
+ def each_card
74
77
  cards.each do |card|
75
- yield(card) if card
78
+ yield card
76
79
  end
77
80
  end
78
81
 
@@ -73,7 +73,7 @@ class Card
73
73
  end
74
74
 
75
75
  def initialize_by_attributes name, attributes
76
- Card.assign_or_initialize_by name, attributes, local_only: true
76
+ Card.assign_or_newish name, attributes, local_only: true
77
77
  end
78
78
 
79
79
  # TODO: this method already exists as card instance method in
@@ -105,12 +105,15 @@ class Card
105
105
  def multi_add args
106
106
  args.each_pair do |key, val|
107
107
  case val
108
- when String then new_by_attributes key, content: val
108
+ when String, Array, Integer
109
+ new_by_attributes key, content: val
109
110
  when Card
110
111
  val.name = absolutize_subcard_name key
111
112
  new_by_card val
112
- when nil then next
113
- else new_by_attributes key, val
113
+ when nil
114
+ next
115
+ else
116
+ new_by_attributes key, val
114
117
  end
115
118
  end
116
119
  end
@@ -113,7 +113,7 @@ class Card
113
113
  # @return [True/False]
114
114
  def clean_enough_to_cache?
115
115
  # requested_view == ok_view && !card.unknown? && !card.db_content_changed?
116
- requested_view == ok_view && !card.db_content_changed?
116
+ requested_view == ok_view && card.view_cache_clean?
117
117
  end
118
118
  end
119
119
  end
@@ -97,6 +97,15 @@ class Card
97
97
 
98
98
  private
99
99
 
100
+ def ancestor_extra_classes klass, space
101
+ if parent
102
+ parent_space = space == :self ? :self_format : :ancestor_format
103
+ parent.deep_extra_classes(klass, parent_space)
104
+ else
105
+ next_format_ancestor&.deep_extra_classes(klass, :ancestor_format)
106
+ end
107
+ end
108
+
100
109
  def storage_voo scope
101
110
  # When we climb up the voo tree and cross a nest boundary then we can jump only
102
111
  # to the root voo of the parent format. Hence we have to add classes to the root
@@ -117,15 +126,6 @@ class Card
117
126
  [classes, class_list(:single_use).delete(klass)]
118
127
  end
119
128
 
120
- def ancestor_extra_classes klass, space
121
- if parent
122
- parent_space = space == :self ? :self_format : :ancestor_format
123
- parent.deep_extra_classes(klass, parent_space)
124
- else
125
- next_format_ancestor&.deep_extra_classes(klass, :ancestor_format)
126
- end
127
- end
128
-
129
129
  def ok_types space
130
130
  case space
131
131
  when :ancestor_format then [:public]
@@ -52,14 +52,6 @@ class Card
52
52
  normalized_options.merge(view: requested_view).slice(*Options.slot_keys)
53
53
  end
54
54
 
55
- # def inherit key
56
- # if live_options.key? key
57
- # live_options[key]
58
- # elsif (ancestor = next_ancestor)
59
- # ancestor.inherit key
60
- # end
61
- # end
62
-
63
55
  # ACCESSOR_HELPERS
64
56
  # methods that follow the normalize_#{key} pattern are called by accessors
65
57
  # (arguably that should be done during normalization!)
@@ -34,8 +34,8 @@ def fetch_read_rules
34
34
  return [] if id == WagnBotID # always_ok, so not needed
35
35
 
36
36
  ([AnyoneID] + parties).each_with_object([]) do |party_id, rule_ids|
37
- next unless self.class.read_rule_cache[party_id]
38
- rule_ids.concat self.class.read_rule_cache[party_id]
37
+ next unless (cache = Card::Rule.read_rule_cache[party_id])
38
+ rule_ids.concat cache
39
39
  end
40
40
  end
41
41
 
@@ -65,7 +65,7 @@ RSpec.describe Card::Set::Right::Account do
65
65
 
66
66
  it "contains deck title" do
67
67
  body = @mail.parts[0].body.raw_source
68
- expect(body).to match(Card.global_setting(:title))
68
+ expect(body).to match(Card::Rule.global_setting(:title))
69
69
  end
70
70
 
71
71
  it "contains link to verify account" do
@@ -111,7 +111,7 @@ RSpec.describe Card::Set::Right::Account do
111
111
 
112
112
  it "contains deck title" do
113
113
  body = @mail.parts[0].body.raw_source
114
- expect(body).to match(Card.global_setting(:title))
114
+ expect(body).to match(Card::Rule.global_setting(:title))
115
115
  end
116
116
 
117
117
  it "contains password reset link" do
@@ -64,7 +64,7 @@ RSpec.describe Card::Set::Type::Signup do
64
64
  it "sends email with an appropriate link" do
65
65
  @mail = ActionMailer::Base.deliveries.last
66
66
  body = @mail.parts[0].body.raw_source
67
- expect(body).to match(Card.global_setting(:title))
67
+ expect(body).to match(Card::Rule.global_setting(:title))
68
68
  end
69
69
 
70
70
  it "notifies someone" do
@@ -1,6 +1,8 @@
1
1
  format do
2
2
  view :page_title, unknown: true, perms: :none do
3
- [(safe_name if card.name.present?), Card.global_setting(:title)].compact.join " - "
3
+ title_parts = [Card::Rule.global_setting(:title)]
4
+ title_parts.unshift safe_name if card.name.present?
5
+ title_parts.join " - "
4
6
  end
5
7
  end
6
8
 
@@ -126,7 +128,7 @@ format :html do
126
128
 
127
129
  def mod_js_configs
128
130
  mod_js_config.map do |codename, js_decko_function|
129
- config_json = escape_javascript Card.global_setting(codename)
131
+ config_json = escape_javascript Card::Rule.global_setting(codename)
130
132
  "decko.#{js_decko_function}('#{config_json}')"
131
133
  end
132
134
  end
@@ -43,7 +43,7 @@ format :rss do
43
43
  end
44
44
 
45
45
  view :feed_title do
46
- Card.global_setting(:title) + " : " + card.name.gsub(/^\*/, "")
46
+ Card::Rule.global_setting(:title) + " : " + card.name.gsub(/^\*/, "")
47
47
  end
48
48
 
49
49
  view :feed_item do
@@ -0,0 +1,37 @@
1
+ class Card
2
+ class LazyTab < Tab
3
+ def url
4
+ @url ||= (config_hash? && @config[:path]) || format.path(view: view)
5
+ end
6
+
7
+ def view
8
+ @view ||= (config_hash? && @config[:view]) || @config
9
+ end
10
+
11
+ def tab_button
12
+ if url
13
+ super
14
+ else
15
+ wrap_with(:li, label, role: "presentation")
16
+ end
17
+ end
18
+
19
+ def button_attrib
20
+ @button_attrib ||= super.merge("data-url" => url.html_safe)
21
+ end
22
+
23
+ def tab_button_link
24
+ add_class button_attrib, "load" unless active?
25
+ super
26
+ end
27
+
28
+ def content
29
+ @content ||= ""
30
+ end
31
+
32
+ def tab_pane args=nil, &block
33
+ @content = yield if active? && block_given?
34
+ super
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,76 @@
1
+ class Card
2
+ class Tab
3
+ attr_reader :format, :name, :label, :content, :button_attrib
4
+
5
+ class << self
6
+ def tab_objects format, tab_hash, active_name, klass=nil
7
+ klass ||= Card::Tab
8
+ active_name ||= tab_hash.keys.first
9
+ tab_hash.map do |name, config|
10
+ klass.new format, name, active_name, config
11
+ end
12
+ end
13
+ end
14
+
15
+ delegate :add_class, :wrap_with, :unique_id, :link_to, to: :format
16
+
17
+ def initialize format, name, active_name, config
18
+ @format = format
19
+ @name = name
20
+ @active_name = active_name
21
+ @config = config
22
+ end
23
+
24
+ def tab_button
25
+ add_class button_attrib, "active" if active?
26
+ wrap_with :li, tab_button_link,
27
+ role: :presentation,
28
+ class: "nav-item tab-li-#{name}"
29
+ end
30
+
31
+ def tab_pane args=nil
32
+ pane_attr = { role: :tabpanel, id: tab_id }
33
+ pane_attr.merge! args if args.present?
34
+ add_class pane_attr, "tab-pane tab-pane-#{name}"
35
+ add_class pane_attr, "active" if active?
36
+ wrap_with :div, content, pane_attr
37
+ end
38
+
39
+ private
40
+
41
+ def config_hash?
42
+ @config.is_a? Hash
43
+ end
44
+
45
+ def label
46
+ @label ||= (config_hash? && @config[:title]) || name
47
+ end
48
+
49
+ def content
50
+ @content ||= config_hash? ? @config[:content] : @config
51
+ end
52
+
53
+ def button_attrib
54
+ @button_attrib ||= (config_hash? && @config[:button_attr]) || {}
55
+ end
56
+
57
+ def tab_button_link
58
+ add_class button_attrib, "nav-link"
59
+
60
+ link_to label, button_attrib.merge(
61
+ path: "##{tab_id}",
62
+ role: "tab",
63
+ "data-toggle" => "tab",
64
+ "data-tab-name" => name
65
+ )
66
+ end
67
+
68
+ def tab_id
69
+ @tab_id ||= "#{unique_id}-#{name.to_name.safe_key}"
70
+ end
71
+
72
+ def active?
73
+ name == @active_name
74
+ end
75
+ end
76
+ end
@@ -66,7 +66,6 @@ format :html do
66
66
  def dropdown_list_item item, active_test, active
67
67
  return unless item
68
68
 
69
-
70
69
  if item.is_a? Array
71
70
  [dropdown_header(item.first), dropdown_array_list(item.second)]
72
71
  else
@@ -1,131 +1,40 @@
1
-
2
-
3
1
  format :html do
4
- # @param tab_type [String] 'tabs' or 'pills'
5
- # @param tabs [Hash] keys are the labels, values the content for the tabs
2
+ # @param tab_hash [Hash] keys are the tab names
3
+ # Each value can be either a String or a Hash.
4
+ # If a Hash can contain the following keys:
5
+ # :title - the label to appear in the clickable tab nav.
6
+ # if title is not specified, the key is used
7
+ # :content - body of tab pane
8
+ # :button_attr - attributes for button link in tab nav.
9
+ #
10
+ # If using lazy loading (see :load below), the following options also apply
11
+ # :path - explicit path to use for tab pane
12
+ # :view - card view from which to auto-construct path (if missing, uses key)
13
+ #
14
+ # If the value is a String, it is treated as the tab content for static tabs and
15
+ # the view for lazy tabs
16
+ #
6
17
  # @param active_name [String] label of the tab that should be active at the
18
+ #
19
+ # @param [Hash] args options
20
+ # @option args [String] :tab_type ('tabs') use pills or tabs
21
+ # @option args [Hash] :panel_attr html args used for the panel div
22
+ # @option args [Hash] :pane_attr html args used for the pane div
23
+ # @option args [Hash] :load. `:lazy` for lazy-loading tabs
24
+ #
25
+ # @param [Block] block content of the active tab (for lazy-loading)
7
26
  # beginning (default is the first)
27
+ #
8
28
  # @return [HTML] bootstrap tabs element with all content preloaded
9
- def static_tabs tabs, active_name=nil, tab_type="tabs", args={}
10
- tab_buttons = ""
11
- tab_panes = ""
12
- tabs.each do |tab_name, tab_content|
13
- active_name ||= tab_name
14
- active_tab = (tab_name == active_name)
15
- id = tab_id tab_name
16
- tab_content, button_attr =
17
- if tab_content.is_a?(Hash)
18
- [tab_content[:content], tab_content[:button_attr]]
19
- else
20
- [tab_content, {}]
21
- end
22
- tab_buttons += tab_button(id, tab_name, active_tab, button_attr)
23
- tab_panes += tab_pane(id, tab_name, tab_content, active_tab, args[:pane])
24
- end
25
- tab_panel tab_buttons, tab_panes, tab_type
26
- end
27
-
28
- # @param [Hash] tabs keys are the views, values the title unless you pass a
29
- # hash as value
30
- # @option tabs [String] :title
31
- # @option tabs [path] :path
32
- # @option tabs [Symbol] :view
33
- # @option tabs [HTML] :html if present use value as inner html for li tag and
34
- # ignore the other tab options
35
- # @param [String] active_name label of the tab that should be active at the
36
- # beginning
37
- # @param [String] active_content content of the active tab
38
- # can also be passed via a block
39
- # @param [Hash] args options
40
- # @option args [String] :type ('tabs') use pills or tabs
41
- # @option args [Hash] :panel_args html args used for the panel div
42
- # @option args [Hash] :pane_args html args used for the pane div
43
- # @return [HTML] bootstrap tabs element with content only for the active
44
- # tab; other tabs get loaded via ajax when selected
45
- def lazy_loading_tabs tabs, active_name, active_content="", args={}, &block
46
- tab_buttons = ""
47
- tab_panes = ""
48
- standardize_tabs(tabs, active_name) do |tab_name, url, id, active_tab|
49
- tab_buttons += lazy_tab_button tab_name, id, url, active_tab
50
- tab_panes += lazy_tab_pane id, tab_name, active_tab, active_content,
51
- args[:pane_args], &block
52
- end
53
- tab_type = args.delete(:type) || "tabs"
54
- tab_panel tab_buttons, tab_panes, tab_type, args[:panel_args]
55
- end
56
-
57
- def lazy_tab_button tab_name, id, url, active_tab
58
- return wrap_with(:li, tab_name, role: "presentation") unless url
59
- tab_button(
60
- id, tab_name, active_tab,
61
- "data-url" => url.html_safe,
62
- class: (active_tab ? nil : "load")
29
+ def tabs tab_hash, active_name=nil, args={}, &block
30
+ klass = args[:load] == :lazy ? Card::LazyTab : Card::Tab
31
+ args.reverse_merge!(
32
+ panel_attr: {},
33
+ pane_attr: {},
34
+ tab_type: "tabs",
35
+ block: block,
36
+ tab_objects: Card::Tab.tab_objects(self, tab_hash, active_name, klass)
63
37
  )
64
- end
65
-
66
- def lazy_tab_pane id, tab_name, active_tab, active_content, args
67
- tab_content =
68
- if active_tab
69
- block_given? ? yield : active_content
70
- else
71
- ""
72
- end
73
- tab_pane(id, tab_name, tab_content, active_tab, args)
74
- end
75
-
76
- def standardize_tabs tabs, active_name
77
- tabs.each do |tab_view_name, tab_details|
78
- tab_title, url = tab_title_and_url(tab_details, tab_view_name)
79
- active_tab = (active_name == tab_view_name)
80
- yield tab_title, url, tab_id(tab_view_name), active_tab
81
- end
82
- end
83
-
84
- def tab_title_and_url tab_details, tab_view_name
85
- if tab_details.is_a? Hash
86
- tab_details[:html] ||
87
- [tab_details[:title], tab_details[:path] || path(tab_details[:view])]
88
- else
89
- [tab_details, path(view: tab_view_name)]
90
- end
91
- end
92
-
93
- def tab_panel tab_buttons, tab_panes, tab_type="tabs", args=nil
94
- args ||= {}
95
- add_class args, "tabbable"
96
- args.reverse_merge! role: "tabpanel"
97
- wrap_with :div, args do
98
- [
99
- wrap_with(:ul, tab_buttons, class: "nav nav-#{tab_type}",
100
- role: "tablist"),
101
- wrap_with(:div, tab_panes, class: "tab-content")
102
- ]
103
- end
104
- end
105
-
106
- def tab_id tab_name
107
- "#{unique_id}-#{tab_name.to_name.safe_key}"
108
- end
109
-
110
- def tab_button target, text, active=false, link_attr={}
111
- add_class link_attr, "active" if active
112
- link = tab_button_link "##{target}", text, link_attr
113
- li_args = { role: :presentation, class: "nav-item tab-li-#{target}" }
114
- wrap_with :li, link, li_args
115
- end
116
-
117
- def tab_button_link target, text, link_attr={}
118
- add_class link_attr, "nav-link"
119
- link_to text, link_attr.merge(
120
- path: target, role: "tab", "data-toggle" => "tab"
121
- )
122
- end
123
-
124
- def tab_pane id, tab_name, content, active=false, args=nil
125
- pane_args = { role: :tabpanel, id: id }
126
- pane_args.merge! args if args.present?
127
- add_class pane_args, "tab-pane tab-pane-#{tab_name.to_name.safe_key}"
128
- add_class pane_args, "active" if active
129
- wrap_with :div, content, pane_args
38
+ haml :tab_panel, args
130
39
  end
131
40
  end