card 1.95.3 → 1.96.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/card.rb +10 -2
  4. data/lib/card/migration/import.rb +1 -1
  5. data/lib/card/set.rb +1 -1
  6. data/lib/card/set/event.rb +63 -65
  7. data/lib/card/set/event/callbacks.rb +21 -0
  8. data/lib/card/set/event/delayed_event.rb +11 -16
  9. data/lib/card/set/event/options.rb +78 -0
  10. data/lib/card/set/format.rb +5 -93
  11. data/lib/card/set/format/abstract_format.rb +104 -0
  12. data/mod/Modfile +1 -0
  13. data/mod/basic_formats/set/all/all_csv.rb +1 -1
  14. data/mod/basic_formats/set/all/json.rb +61 -4
  15. data/mod/basic_formats/spec/set/all/json_spec.rb +51 -1
  16. data/mod/basic_formats/spec/shared_context/json_shared_context.rb +40 -0
  17. data/mod/bootstrap/lib/stylesheets/style_bootstrap_cards.scss +17 -1
  18. data/mod/bootstrap/set/abstract/bootswatch_theme.rb +2 -12
  19. data/mod/bootstrap/set/abstract/bootswatch_theme/html_views.rb +31 -0
  20. data/mod/bootstrap/set/type/customized_bootswatch_skin.rb +12 -0
  21. data/mod/bootstrap/set/type/customized_bootswatch_skin/core.haml +5 -0
  22. data/mod/bootstrap/set/type/customized_bootswatch_skin/html_views.rb +36 -0
  23. data/mod/bootstrap/set/type_plus_right/customized_bootswatch_skin/colors.rb +14 -10
  24. data/mod/bootstrap/set/type_plus_right/customized_bootswatch_skin/colors/_colorpicker.haml +1 -1
  25. data/mod/bootstrap/set/type_plus_right/customized_bootswatch_skin/colors/editor.haml +13 -9
  26. data/mod/core/chunk/link.rb +8 -0
  27. data/mod/core/set/abstract/code_file.rb +5 -0
  28. data/mod/core/set/all/chunk.rb +46 -24
  29. data/mod/core/set/all/event_conditions.rb +32 -11
  30. data/mod/core/set/all/export.rb +3 -5
  31. data/mod/core/set/all/item.rb +16 -0
  32. data/mod/core/set/all/name.rb +1 -0
  33. data/mod/core/set/all/references.rb +25 -16
  34. data/mod/core/set/all/rename.rb +20 -21
  35. data/mod/core/set/all/trash.rb +13 -4
  36. data/mod/core/spec/set/all/actify_spec.rb +4 -4
  37. data/mod/core/spec/set/all/event_conditions_spec.rb +68 -10
  38. data/mod/core/spec/set/all/export_spec.rb +7 -4
  39. data/mod/core/spec/set/all/references_spec.rb +38 -1
  40. data/mod/core/spec/set/all/rename_spec.rb +15 -9
  41. data/mod/follow/spec/set/right/followers_spec.rb +1 -1
  42. data/mod/follow/spec/set/right/following_spec.rb +4 -8
  43. data/mod/follow/spec/set/type/notification_template_spec.rb +1 -1
  44. data/mod/history/set/all/act_view.rb +2 -2
  45. data/mod/item/set/all/bar.haml +12 -0
  46. data/mod/item/set/all/bar.rb +77 -0
  47. data/mod/item/set/all/box.haml +10 -0
  48. data/mod/item/set/all/box.rb +8 -0
  49. data/mod/item/set/all/expanded_bar.haml +10 -0
  50. data/mod/machines/file/all_script_machine_output/file.js +30622 -66
  51. data/mod/machines/file/all_style_machine_output/file.css +3 -3
  52. data/mod/machines/file/script_html5shiv_printshiv_machine_output/file.js +1 -1
  53. data/mod/machines/lib/javascript/decko_slot.js.coffee +18 -13
  54. data/mod/machines/set/abstract/skin_box.rb +34 -0
  55. data/mod/machines/set/abstract/skin_box/box_bottom.haml +4 -0
  56. data/mod/machines/set/type/skin.rb +1 -1
  57. data/mod/pointer/set/abstract/01_paging.rb +49 -4
  58. data/mod/pointer/set/abstract/01_paging/paging_links.rb +6 -6
  59. data/mod/pointer/set/abstract/02_pointer/html_views.rb +10 -0
  60. data/mod/pointer/set/abstract/02_pointer/other_views.rb +9 -5
  61. data/mod/pointer/spec/set/abstract/pointer/html_views_spec.rb +4 -10
  62. data/mod/pointer/spec/set/abstract/pointer/other_views_spec.rb +20 -1
  63. data/mod/search/set/abstract/01_filter_form_helper.rb +4 -4
  64. data/mod/search/set/abstract/02_search_params.rb +1 -1
  65. data/mod/search/set/abstract/03_filter.rb +8 -0
  66. data/mod/search/set/abstract/search.rb +10 -0
  67. data/mod/settings/set/right/style.rb +13 -0
  68. data/mod/settings/set/right/style/editor.haml +4 -4
  69. data/mod/standard/set/all/path.rb +6 -0
  70. data/mod/standard/set/type/list.rb +3 -2
  71. data/mod/standard/set/type/listed_by.rb +5 -1
  72. data/mod/standard/spec/content/chunk/include_spec.rb +2 -2
  73. data/mod/standard/spec/set/all/rich_html/editing_spec.rb +4 -69
  74. data/mod/standard/spec/set/type/listed_by_spec.rb +2 -2
  75. data/mod/standard/spec/set/type/search_type_spec.rb +56 -0
  76. data/mod/standard/spec/set/type/set/html_views_spec.rb +5 -9
  77. data/mod/utility/set/abstract/bs_badge.rb +21 -0
  78. data/mod/utility/set/abstract/bs_badge/bs_badge.haml +5 -0
  79. data/mod/utility/set/abstract/bs_badge/labeled_badge.haml +5 -0
  80. data/mod/utility/set/abstract/bs_badge/tab_badge.haml +5 -0
  81. data/mod/utility/set/abstract/media.rb +27 -0
  82. data/mod/utility/set/abstract/media/media_snippet.haml +9 -0
  83. metadata +28 -10
  84. data/mod/machines/set/abstract/skin_thumbnail.rb +0 -28
  85. data/mod/machines/set/abstract/skin_thumbnail/thumbnail.haml +0 -10
@@ -1,8 +1,8 @@
1
- event :rename, after: :set_name, on: :update do
1
+ event :rename_in_trash, after: :set_name, on: :update do
2
2
  existing_card = Card.find_by_key_and_trash name.key, true
3
3
  return if !existing_card || existing_card == self
4
4
  existing_card.name = existing_card.name + "*trash"
5
- existing_card.rename_without_callbacks
5
+ existing_card.rename_in_trash_without_callbacks
6
6
  existing_card.save!
7
7
  end
8
8
 
@@ -14,28 +14,27 @@ def suspend_name name
14
14
  Card.where(id: id).update_all(name: tmp_name, key: tmp_name)
15
15
  end
16
16
 
17
+ event :validate_renaming, :validate, on: :update, changed: :name do
18
+ if db_content_is_changing?
19
+ errors.add :content, "cannot change content while changing name"
20
+ end
21
+ errors.add :type, "cannot change type while changing name" if type_id_is_changing?
22
+ end
23
+
17
24
  event :cascade_name_changes, :finalize, on: :update, changed: :name,
18
25
  before: :name_change_finalized do
19
- des = descendants
20
26
  @descendants = nil # reset
21
27
 
22
- des.each do |de|
23
- # here we specifically want NOT to invoke recursive cascades on these
24
- # cards, have to go this low level to avoid callbacks.
25
- Rails.logger.info "cascading name: #{de.name}"
26
- newname = de.name.swap name_before_last_save, name
27
- check_for_conflict de.name, newname
28
- Card.expire de.name # old name
29
- Card.where(id: de.id).update_all name: newname.to_s, key: newname.key
30
- de.update_referers = update_referers
31
- de.refresh_references_in
32
- Card.expire newname
33
- end
34
- end
28
+ children.each do |child|
29
+ Rails.logger.info "cascading name: #{child.name}"
30
+ newname = child.name.swap name_before_last_save, name
31
+ # not sure if this is still needed since we attach the children as subcards
32
+ # (it used to be resolved right here without adding subcards)
33
+ Card.expire child.name
35
34
 
36
- def check_for_conflict old_name, new_name
37
- return unless ActManager.include?(new_name) && (old_name.key != new_name.key)
38
- raise Card::Error, "conflict in act: "\
39
- "the name of '#{old_name}' is changing to '#{new_name}' "\
40
- "which is also a subcard of this act."
35
+ # superleft has to be the first argument. Otherwise the call of `name=` in
36
+ # `assign_attributes` can cause problems because `left` doesn't find the new left.
37
+ attach_subcard child.name, superleft: self, name: newname,
38
+ update_referers: update_referers
39
+ end
41
40
  end
@@ -50,12 +50,21 @@ module ClassMethods
50
50
  end
51
51
  end
52
52
 
53
- def delete
54
- update_attributes trash: true unless new_card?
53
+ def delete args={}
54
+ add_to_trash args do |delete_args|
55
+ update_attributes delete_args
56
+ end
57
+ end
58
+
59
+ def delete! args={}
60
+ add_to_trash args do |delete_args|
61
+ update_attributes! delete_args
62
+ end
55
63
  end
56
64
 
57
- def delete!
58
- update_attributes! trash: true unless new_card?
65
+ def add_to_trash args
66
+ return if new_card?
67
+ yield args.merge trash: true
59
68
  end
60
69
 
61
70
  event :manage_trash, :prepare_to_store, on: :create do
@@ -139,12 +139,12 @@ RSpec.describe "act API" do
139
139
  self.content = "new content"
140
140
  changed_attributes
141
141
  end
142
- test_event :integrate do
142
+ test_event :integrate, for: "new name" do
143
143
  expect(name_is_changing?).to be_truthy
144
144
  expect(name_before_act).to eq("A")
145
145
  expect(db_content_before_act).to eq("Alpha [[Z]]")
146
146
  end
147
- test_event :integrate_with_delay do
147
+ test_event :integrate_with_delay, for: "new name" do
148
148
  expect(name_is_changing?).to be_truthy
149
149
  expect(name_before_act).to eq("A")
150
150
  expect(db_content_before_act).to eq("Alpha [[Z]]")
@@ -161,13 +161,13 @@ RSpec.describe "act API" do
161
161
  end
162
162
 
163
163
  with_test_events do
164
- test_event :integrate, changed: :name do
164
+ test_event :integrate, changed: :name, for: "new name" do
165
165
  event_called :i_name
166
166
  end
167
167
  test_event :integrate, changed: :content do
168
168
  event_called :i_content
169
169
  end
170
- test_event :integrate_with_delay, changed: :name do
170
+ test_event :integrate_with_delay, changed: :name, for: "new name" do
171
171
  event_called :iwd_name
172
172
  end
173
173
  test_event :integrate_with_delay, changed: :content do
@@ -1,6 +1,6 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
- describe Card::Set::All::EventConditions, "event" do
3
+ RSpec.describe Card::Set::All::EventConditions do
4
4
  let(:create_card) {Card.create!(name: "main card")}
5
5
  let(:create_card_with_subcards) do
6
6
  Card.create name: "main card",
@@ -51,17 +51,75 @@ describe Card::Set::All::EventConditions, "event" do
51
51
  end
52
52
  end
53
53
 
54
- specify "skip event condition" do
55
- with_test_events do
56
- test_event :validate, on: :update, optional: true, for: "A" do
57
- add_to_log "not skipped"
54
+ describe "trigger option" do
55
+ specify "trigger for whole act" do
56
+ with_test_events do
57
+ test_event :validate, on: :update, trigger: :required, for: "A" do
58
+ add_to_log "triggered"
59
+ end
60
+ Card["A"].update_attributes! content: "changed content"
61
+
62
+ aggregate_failures do
63
+ expect(@log).to be_empty
64
+ Card["A"].update_attributes! content: "changed content",
65
+ trigger: :test_event_0
66
+ expect(@log).to contain_exactly "triggered"
67
+ end
58
68
  end
59
- Card["A"].update_attributes! content: "changed content", skip_event: :test_event_0
69
+ end
70
+ end
60
71
 
61
- aggregate_failures do
62
- expect(@log).to be_empty
63
- Card["A"].update_attributes! content: "changed content"
64
- expect(@log).to contain_exactly "not skipped"
72
+ describe "skip option" do
73
+ specify "skip_event condition" do
74
+ with_test_events do
75
+ test_event :validate, on: :update, skip: :allowed, for: "A" do
76
+ add_to_log "executed"
77
+ end
78
+ Card["A"].update_attributes! content: "changed content", skip: :test_event_0
79
+
80
+ aggregate_failures do
81
+ expect(@log).to be_empty
82
+ Card["A"].update_attributes! content: "changed content"
83
+ expect(@log).to contain_exactly "executed"
84
+ end
85
+ end
86
+ end
87
+
88
+ specify "skip_event condition in subcard" do
89
+ with_test_events do
90
+ test_event :validate, on: :update, skip: :allowed, for: "A+B" do
91
+ add_to_log "not skipped"
92
+ end
93
+ Card["A"].update_attributes! content: "changed content",
94
+ skip: :test_event_0,
95
+ subcards: { "+B" => "changed +B content" }
96
+
97
+ aggregate_failures do
98
+ expect(@log).to be_empty
99
+ Card["A"].update_attributes! content: "changed content",
100
+ subcards: { "+B" => "changed +B content" }
101
+ expect(@log).to contain_exactly "not skipped"
102
+ end
103
+ end
104
+ end
105
+
106
+ specify "skip_event_in_action condition" do
107
+ with_test_events do
108
+ test_event :validate, on: :update, skip: :allowed do
109
+ add_to_log "#{name} not skipped"
110
+ end
111
+ Card["A"].update_attributes! content: "changed content",
112
+ skip_in_action: :test_event_0,
113
+ subcards: { "+B" => "changed +B content" }
114
+
115
+ aggregate_failures do
116
+ expect(@log).to contain_exactly "A+B not skipped"
117
+ Card["A"].update_attributes! content: "changed content",
118
+ subcards: { "+B" => "changed +B content" }
119
+ expect(@log).to contain_exactly "A+B not skipped",
120
+ "A not skipped",
121
+ "A+B not skipped"
122
+ end
65
123
  end
66
124
  end
67
125
  end
@@ -34,11 +34,14 @@ RSpec.describe Card::Set::All::Export do
34
34
  a_hash_including(name: "A+B", type: "Basic", content: "AlphaBeta")
35
35
  )
36
36
  end
37
+
37
38
  it "stops while the depth count > 10" do
38
- expect(export_pointer("pointer export card")).to include(
39
- name: "pointer export card", type: "Pointer",
40
- content: "[[pointer export card]]"
41
- )
39
+ expect(export_pointer("pointer export card"))
40
+ .to include(
41
+ a_hash_including(name: "pointer export card",
42
+ type: "Pointer",
43
+ content: "[[pointer export card]]")
44
+ )
42
45
  end
43
46
  end
44
47
 
@@ -1,9 +1,46 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
- describe Card::Set::All::References do
3
+ RSpec.describe Card::Set::All::References do
4
4
  it "replaces references should work on nests inside links" do
5
5
  card = Card.create! name: "ref test", content: "[[test_card|test{{test}}]]"
6
6
  assert_equal "[[test_card|test{{best}}]]",
7
7
  card.replace_reference_syntax("test", "best")
8
8
  end
9
+
10
+ describe "#referers" do
11
+ it "returns all cards that refer to card" do
12
+ expect(Card["Blue"].referers.map(&:name))
13
+ .to contain_exactly(
14
+ "blue includer 1", "blue includer 2", "blue linker 1", "blue linker 2"
15
+ )
16
+ end
17
+ end
18
+
19
+ describe "#nesters" do
20
+ it "returns all cards that nest card" do
21
+ expect(Card["Blue"].nesters.map(&:name))
22
+ .to contain_exactly("blue includer 1", "blue includer 2")
23
+ end
24
+ end
25
+
26
+ describe "#referee" do
27
+ it "returns all cards that card nests" do
28
+ expect(Card["Y"].referees.map(&:name)).to contain_exactly("A", "A+B", "B", "T")
29
+ end
30
+
31
+ it "returns all cards that card links to and their ancestors" do
32
+ # NOTE: B is not directly referred to; the reference is implied by the link to A+B
33
+ expect(Card["X"].referees.map(&:name)).to contain_exactly("A", "A+B", "B", "T")
34
+ end
35
+ end
36
+
37
+ describe "#nestee" do
38
+ it "returns all cards that card nests" do
39
+ expect(Card["Y"].nestees.map(&:name)).to contain_exactly("A", "A+B", "B", "T")
40
+ end
41
+
42
+ it "returns all cards that card links to" do
43
+ expect(Card["X"].nestees.map(&:name)).to eq([])
44
+ end
45
+ end
9
46
  end
@@ -131,37 +131,43 @@ RSpec.describe Card::Set::All::Rename do
131
131
  example "renaming card with self link should nothang" do
132
132
  pre_content = Card["self_aware"].content
133
133
  update "self aware", name: "buttah", update_referers: true
134
- expect_content_of("Buttah").to eq pre_content
134
+ expect_card("Buttah").to have_content(pre_content)
135
135
  end
136
136
 
137
137
  it "renames card without updating references" do
138
138
  pre_content = Card["self_aware"].content
139
139
  update "self aware", name: "Newt", update_referers: false
140
- expect_content_of("Newt").to eq pre_content
140
+ expect_card("Newt").to have_content(pre_content)
141
141
  end
142
142
  end
143
143
 
144
144
  context "references" do
145
145
  it "updates nests" do
146
146
  update "Blue", name: "Red", update_referers: true
147
- expect_content_of("blue includer 1").to eq "{{Red}}"
148
- expect_content_of("blue includer 2").to eq "{{Red|closed;other:stuff}}"
147
+ expect_card("blue includer 1").to have_content("{{Red}}")
148
+ expect_card("blue includer 2").to have_content("{{Red|closed;other:stuff}}")
149
149
  end
150
150
 
151
- it "tupdates nests when renaming to plus" do
151
+ it "updates nests when renaming to plus" do
152
152
  update "Blue", name: "blue includer 1+color", update_referers: true
153
- expect_content_of("blue includer 1").to eq "{{blue includer 1+color}}"
153
+ expect_card("blue includer 1").to have_content("{{blue includer 1+color}}")
154
154
  end
155
155
 
156
156
  it "reference updates on case variants" do
157
157
  update "Blue", name: "Red", update_referers: true
158
- expect_content_of("blue linker 1").to eq "[[Red]]"
159
- expect_content_of("blue linker 2").to eq "[[Red]]"
158
+ expect_card("blue linker 1").to have_content("[[Red]]")
159
+ expect_card("blue linker 2").to have_content("[[Red]]")
160
+ end
161
+
162
+ it "handles link to and nest of same card" do
163
+ update "blue linker 1", content: "[[Blue]] is {{Blue|name}}"
164
+ update "Blue", name: "Red", update_referers: true
165
+ expect_card("blue linker 1").to have_content("[[Red]] is {{Red|name}}")
160
166
  end
161
167
 
162
168
  example "reference updates plus to simple" do
163
169
  assert_rename Card["A+B"], "schmuck"
164
- expect_content_of("X").to eq "[[A]] [[schmuck]] [[T]]"
170
+ expect_card("X").to have_content("[[A]] [[schmuck]] [[T]]")
165
171
  end
166
172
 
167
173
  it "substitutes name part" do
@@ -1,6 +1,6 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
- describe Card::Set::Right::Followers do
3
+ RSpec.describe Card::Set::Right::Followers do
4
4
  describe "#content" do
5
5
  it "returns a pointer list of followers" do
6
6
  card = Card.fetch "All Eyes on me"
@@ -1,15 +1,11 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  describe Card::Set::Right::Following do
4
- extend Card::SpecHelper::ViewHelper::ViewDescriber
5
-
6
4
  context "when admin is following" do
7
- let(:following) { Card.fetch "Joe Admin", :following }
8
-
9
- describe_views :core, :status, :rule_editor do
10
- it "doesn't have errors" do
11
- expect(following.format.render(view)).to lack_errors
12
- end
5
+ def card_subject
6
+ Card.fetch "Joe Admin", :following
13
7
  end
8
+
9
+ check_views_for_errors :core, :status, :rule_editor
14
10
  end
15
11
  end
@@ -1,4 +1,4 @@
1
- describe Card::Set::Type::NotificationTemplate do
1
+ RSpec.describe Card::Set::Type::NotificationTemplate do
2
2
  include ActionController::TestCase::Behavior
3
3
  before do
4
4
  @routes = Decko::Engine.routes
@@ -73,7 +73,7 @@ format :html do
73
73
  end
74
74
 
75
75
  def current_act_seq acts
76
- @act_seq = @act_seq ? (@act_seq += 1) : act_list_starting_seq(acts)
76
+ @act_seq = @act_seq ? (@act_seq -= 1) : act_list_starting_seq(acts)
77
77
  end
78
78
 
79
79
  def clean_acts acts
@@ -87,7 +87,7 @@ format :html do
87
87
  end
88
88
 
89
89
  def act_list_starting_seq acts
90
- acts.size - (acts_page_from_params - 1) * acts_per_page + 1
90
+ acts.size - (acts_page_from_params - 1) * acts_per_page
91
91
  end
92
92
 
93
93
  def acts_per_page
@@ -0,0 +1,12 @@
1
+ .bar.border.left-stripe.d-flex.justify-content-between.p-0.double-border-fix-bottom.with-fancy-border
2
+ - if voo.show? :bar_left
3
+ %div{class: classy("bar-left")}
4
+ = render :bar_left
5
+ - if voo.show? :bar_middle
6
+ %div{class: classy("bar-middle")}
7
+ = render :bar_middle
8
+ - if voo.show? :bar_right
9
+ %div{class: classy("bar-right")}
10
+ = render :bar_right
11
+ = render :bar_page_link
12
+ = render :bar_expand_link
@@ -0,0 +1,77 @@
1
+ include_set Abstract::BsBadge
2
+
3
+ format :html do
4
+ view :thin_bar do
5
+ render_bar hide: :bar_middle
6
+ end
7
+
8
+ view :bar do
9
+ wrap { haml :bar }
10
+ end
11
+
12
+ view :expanded_bar do
13
+ wrap { haml :expanded_bar }
14
+ end
15
+
16
+ view :expanded_edit_bar, perms: :none do
17
+ _render_expanded_bar!
18
+ end
19
+
20
+ before :bar do
21
+ shared = "align-items-center"
22
+ class_up "bar-left", "p-2 font-weight-bold d-flex grow-2 #{shared}"
23
+ class_up "bar-middle", "col-3 d-none d-md-flex p-3 border-left #{shared}"
24
+ class_up "bar-right",
25
+ "p-3 border-left d-flex justify-content-end text-align-right #{shared}"
26
+ end
27
+
28
+ view :bar_left do
29
+ class_up "card-title", "mb-0"
30
+ render :title
31
+ end
32
+
33
+ view :bar_right do
34
+ render :edit_button, optional: :hide
35
+ end
36
+
37
+ view :bar_middle do
38
+ labeled_badge stat_number, stat_label
39
+ end
40
+
41
+ view :bar_bottom do
42
+ if nest_mode == :edit
43
+ render :edit
44
+ else
45
+ render :core
46
+ end
47
+ end
48
+
49
+ def stat_number
50
+ card.content.lines.count
51
+ end
52
+
53
+ def stat_label
54
+ stat_number == 1 ? "line" : "lines"
55
+ end
56
+
57
+ view :bar_page_link do
58
+ link_to_card card, icon_tag(:open_in_new), class: "text-muted"
59
+ end
60
+
61
+ def toggle_class
62
+ "slotter btn btn-sm btn-outline-secondary p-0 border-0 rounded-0"
63
+ end
64
+
65
+ view :bar_expand_link do
66
+ link_to_view :expanded_bar, icon_tag(:play_arrow), class: toggle_class
67
+ end
68
+
69
+ view :bar_collapse_link do
70
+ link_to_view :bar, icon_tag(:arrow_drop_down, class: "md-24"), class: toggle_class
71
+ end
72
+
73
+ view :edit_button do
74
+ link_to_view :edit, "Edit",
75
+ class: "btn btn-sm btn-outline-primary slotter mr-2"
76
+ end
77
+ end