logster 2.1.0 → 2.1.1

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.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +19 -19
  3. data/.rubocop.yml +1 -1
  4. data/.travis.yml +16 -16
  5. data/CHANGELOG.md +169 -166
  6. data/Gemfile +4 -4
  7. data/Guardfile +8 -8
  8. data/LICENSE.txt +22 -22
  9. data/README.md +99 -99
  10. data/Rakefile +21 -21
  11. data/assets/fonts/FontAwesome.otf +0 -0
  12. data/assets/fonts/fontawesome-webfont.eot +0 -0
  13. data/assets/fonts/fontawesome-webfont.svg +639 -639
  14. data/assets/fonts/fontawesome-webfont.ttf +0 -0
  15. data/assets/fonts/fontawesome-webfont.woff +0 -0
  16. data/assets/fonts/fontawesome-webfont.woff2 +0 -0
  17. data/assets/images/Icon-144_rounded.png +0 -0
  18. data/assets/images/Icon-144_square.png +0 -0
  19. data/assets/images/icon_144x144.png +0 -0
  20. data/assets/images/icon_64x64.png +0 -0
  21. data/assets/javascript/client-app.js +100 -100
  22. data/assets/stylesheets/client-app.css +0 -0
  23. data/build_client_app.sh +0 -0
  24. data/client-app/.editorconfig +20 -20
  25. data/client-app/.ember-cli +9 -9
  26. data/client-app/.eslintignore +19 -19
  27. data/client-app/.eslintrc.js +46 -46
  28. data/client-app/.gitignore +23 -23
  29. data/client-app/.travis.yml +27 -27
  30. data/client-app/.watchmanconfig +3 -3
  31. data/client-app/README.md +57 -57
  32. data/client-app/app/app.js +14 -14
  33. data/client-app/app/components/actions-menu.js +37 -37
  34. data/client-app/app/components/env-tab.js +44 -44
  35. data/client-app/app/components/message-info.js +65 -65
  36. data/client-app/app/components/message-row.js +45 -45
  37. data/client-app/app/components/panel-resizer.js +74 -74
  38. data/client-app/app/components/tab-contents.js +27 -27
  39. data/client-app/app/components/tab-link.js +5 -5
  40. data/client-app/app/components/tabbed-section.js +32 -32
  41. data/client-app/app/components/time-formatter.js +25 -25
  42. data/client-app/app/components/update-time.js +21 -21
  43. data/client-app/app/controllers/index.js +105 -105
  44. data/client-app/app/controllers/show.js +13 -13
  45. data/client-app/app/index.html +29 -29
  46. data/client-app/app/initializers/app-init.js +72 -72
  47. data/client-app/app/lib/preload.js +14 -14
  48. data/client-app/app/lib/utilities.js +140 -140
  49. data/client-app/app/models/message-collection.js +178 -178
  50. data/client-app/app/models/message.js +100 -100
  51. data/client-app/app/resolver.js +3 -3
  52. data/client-app/app/router.js +14 -14
  53. data/client-app/app/routes/index.js +57 -57
  54. data/client-app/app/routes/show.js +14 -14
  55. data/client-app/app/styles/app.css +521 -521
  56. data/client-app/app/templates/application.hbs +2 -2
  57. data/client-app/app/templates/components/actions-menu.hbs +12 -12
  58. data/client-app/app/templates/components/env-tab.hbs +10 -10
  59. data/client-app/app/templates/components/message-info.hbs +41 -41
  60. data/client-app/app/templates/components/message-row.hbs +15 -15
  61. data/client-app/app/templates/components/panel-resizer.hbs +3 -3
  62. data/client-app/app/templates/components/tabbed-section.hbs +10 -10
  63. data/client-app/app/templates/components/time-formatter.hbs +1 -1
  64. data/client-app/app/templates/index.hbs +58 -58
  65. data/client-app/app/templates/show.hbs +7 -7
  66. data/client-app/config/environment.js +51 -51
  67. data/client-app/config/optional-features.json +3 -3
  68. data/client-app/config/targets.js +18 -18
  69. data/client-app/ember-cli-build.js +29 -29
  70. data/client-app/package-lock.json +11365 -11365
  71. data/client-app/package.json +56 -56
  72. data/client-app/testem.js +25 -25
  73. data/client-app/tests/index.html +34 -34
  74. data/client-app/tests/integration/components/actions-menu-test.js +26 -26
  75. data/client-app/tests/integration/components/env-tab-test.js +73 -73
  76. data/client-app/tests/integration/components/message-info-test.js +26 -26
  77. data/client-app/tests/integration/components/message-row-test.js +26 -26
  78. data/client-app/tests/integration/components/panel-resizer-test.js +26 -26
  79. data/client-app/tests/integration/components/tab-contents-test.js +26 -26
  80. data/client-app/tests/integration/components/tab-link-test.js +26 -26
  81. data/client-app/tests/integration/components/tabbed-section-test.js +26 -26
  82. data/client-app/tests/integration/components/time-formatter-test.js +26 -26
  83. data/client-app/tests/integration/components/update-time-test.js +26 -26
  84. data/client-app/tests/test-helper.js +8 -8
  85. data/client-app/tests/unit/controllers/index-test.js +12 -12
  86. data/client-app/tests/unit/controllers/show-test.js +12 -12
  87. data/client-app/tests/unit/initializers/app-init-test.js +31 -31
  88. data/client-app/tests/unit/routes/index-test.js +11 -11
  89. data/client-app/tests/unit/routes/show-test.js +11 -11
  90. data/lib/examples/sidekiq_logster_reporter.rb +21 -21
  91. data/lib/logster.rb +54 -54
  92. data/lib/logster/base_store.rb +141 -141
  93. data/lib/logster/configuration.rb +25 -25
  94. data/lib/logster/defer_logger.rb +14 -14
  95. data/lib/logster/ignore_pattern.rb +65 -65
  96. data/lib/logster/logger.rb +113 -113
  97. data/lib/logster/message.rb +212 -212
  98. data/lib/logster/middleware/debug_exceptions.rb +26 -26
  99. data/lib/logster/middleware/reporter.rb +55 -55
  100. data/lib/logster/middleware/viewer.rb +221 -221
  101. data/lib/logster/rails/railtie.rb +63 -63
  102. data/lib/logster/redis_store.rb +566 -566
  103. data/lib/logster/scheduler.rb +54 -40
  104. data/lib/logster/version.rb +3 -3
  105. data/lib/logster/web.rb +14 -14
  106. data/logster.gemspec +35 -35
  107. data/test/examples/test_sidekiq_reporter_example.rb +46 -46
  108. data/test/fake_data/Gemfile +4 -4
  109. data/test/fake_data/generate.rb +10 -10
  110. data/test/logster/middleware/test_reporter.rb +19 -19
  111. data/test/logster/middleware/test_viewer.rb +96 -96
  112. data/test/logster/test_base_store.rb +147 -147
  113. data/test/logster/test_defer_logger.rb +34 -34
  114. data/test/logster/test_ignore_pattern.rb +41 -41
  115. data/test/logster/test_logger.rb +86 -86
  116. data/test/logster/test_message.rb +119 -119
  117. data/test/logster/test_redis_rate_limiter.rb +230 -230
  118. data/test/logster/test_redis_store.rb +720 -720
  119. data/test/test_helper.rb +38 -38
  120. data/vendor/assets/javascripts/logster.js.erb +39 -39
  121. metadata +2 -2
@@ -1,140 +1,140 @@
1
- import Preload from "client-app/lib/preload";
2
-
3
- const entityMap = {
4
- "&": "&",
5
- "<": "&lt;",
6
- ">": "&gt;",
7
- '"': "&quot;",
8
- "'": "&#39;",
9
- "/": "&#x2F;"
10
- };
11
-
12
- export function escapeHtml(string) {
13
- return String(string).replace(/[&<>"'/]/g, s => entityMap[s]);
14
- }
15
-
16
- export function ajax(url, settings) {
17
- settings = settings || {};
18
- settings.headers = settings.headers || {};
19
- settings.headers["X-SILENCE-LOGGER"] = true;
20
- return Em.$.ajax(Preload.get("rootPath") + url, settings);
21
- }
22
-
23
- export function preloadOrAjax(url, settings) {
24
- const preloaded = Preload.get(`preload.${url.replace(".json", "")}`);
25
- if (preloaded) {
26
- return Em.RSVP.resolve(preloaded);
27
- } else {
28
- return ajax(url, settings);
29
- }
30
- }
31
-
32
- let HIDDEN_PROPERTY;
33
- let TITLE;
34
- let TITLE_COUNT;
35
-
36
- export function updateHiddenProperty(property) {
37
- HIDDEN_PROPERTY = property;
38
- }
39
-
40
- export function isHidden() {
41
- if (HIDDEN_PROPERTY !== undefined) {
42
- return document[HIDDEN_PROPERTY];
43
- } else {
44
- return !document.hasFocus;
45
- }
46
- }
47
-
48
- export function increaseTitleCount(increment) {
49
- if (!isHidden()) {
50
- return;
51
- }
52
- TITLE = TITLE || document.title;
53
- TITLE_COUNT = TITLE_COUNT || 0;
54
- TITLE_COUNT += increment;
55
- document.title = `${TITLE} (${TITLE_COUNT})`;
56
- }
57
-
58
- export function resetTitleCount() {
59
- TITLE_COUNT = 0;
60
- document.title = TITLE || document.title;
61
- }
62
-
63
- export function formatTime(timestamp) {
64
- let formatted;
65
- const time = moment(timestamp);
66
- const now = moment();
67
-
68
- if (time.diff(now.startOf("day")) > 0) {
69
- formatted = time.format("h:mm a");
70
- } else {
71
- if (time.diff(now.startOf("week")) > 0) {
72
- formatted = time.format("dd h:mm a");
73
- } else {
74
- if (time.diff(now.startOf("year")) > 0) {
75
- formatted = time.format("D MMM h:mm a");
76
- } else {
77
- formatted = time.format("D MMM YY");
78
- }
79
- }
80
- }
81
-
82
- return formatted;
83
- }
84
-
85
- export function buildArrayString(array) {
86
- const buffer = [];
87
- array.forEach(v => {
88
- if (v === null) {
89
- buffer.push("null");
90
- } else if (Object.prototype.toString.call(v) === "[object Array]") {
91
- buffer.push(buildArrayString(v));
92
- } else {
93
- buffer.push(escapeHtml(v.toString()));
94
- }
95
- });
96
- return "[" + buffer.join(", ") + "]";
97
- }
98
-
99
- export function buildHashString(hash, recurse) {
100
- if (!hash) return "";
101
-
102
- const buffer = [];
103
- const hashes = [];
104
- _.each(hash, (v, k) => {
105
- if (v === null) {
106
- buffer.push("null");
107
- } else if (Object.prototype.toString.call(v) === "[object Array]") {
108
- buffer.push(
109
- "<tr><td>" +
110
- escapeHtml(k) +
111
- "</td><td>" +
112
- buildArrayString(v) +
113
- "</td></tr>"
114
- );
115
- } else if (typeof v === "object") {
116
- hashes.push(k);
117
- } else {
118
- buffer.push(
119
- "<tr><td>" + escapeHtml(k) + "</td><td>" + escapeHtml(v) + "</td></tr>"
120
- );
121
- }
122
- });
123
-
124
- if (_.size(hashes) > 0) {
125
- _.each(hashes, function(k1) {
126
- const v = hash[k1];
127
- buffer.push("<tr><td></td><td><table>");
128
- buffer.push(
129
- "<td>" +
130
- escapeHtml(k1) +
131
- "</td><td>" +
132
- buildHashString(v, true) +
133
- "</td>"
134
- );
135
- buffer.push("</table></td></tr>");
136
- });
137
- }
138
- const className = recurse ? "" : "env-table";
139
- return "<table class='" + className + "'>" + buffer.join("\n") + "</table>";
140
- }
1
+ import Preload from "client-app/lib/preload";
2
+
3
+ const entityMap = {
4
+ "&": "&amp;",
5
+ "<": "&lt;",
6
+ ">": "&gt;",
7
+ '"': "&quot;",
8
+ "'": "&#39;",
9
+ "/": "&#x2F;"
10
+ };
11
+
12
+ export function escapeHtml(string) {
13
+ return String(string).replace(/[&<>"'/]/g, s => entityMap[s]);
14
+ }
15
+
16
+ export function ajax(url, settings) {
17
+ settings = settings || {};
18
+ settings.headers = settings.headers || {};
19
+ settings.headers["X-SILENCE-LOGGER"] = true;
20
+ return Em.$.ajax(Preload.get("rootPath") + url, settings);
21
+ }
22
+
23
+ export function preloadOrAjax(url, settings) {
24
+ const preloaded = Preload.get(`preload.${url.replace(".json", "")}`);
25
+ if (preloaded) {
26
+ return Em.RSVP.resolve(preloaded);
27
+ } else {
28
+ return ajax(url, settings);
29
+ }
30
+ }
31
+
32
+ let HIDDEN_PROPERTY;
33
+ let TITLE;
34
+ let TITLE_COUNT;
35
+
36
+ export function updateHiddenProperty(property) {
37
+ HIDDEN_PROPERTY = property;
38
+ }
39
+
40
+ export function isHidden() {
41
+ if (HIDDEN_PROPERTY !== undefined) {
42
+ return document[HIDDEN_PROPERTY];
43
+ } else {
44
+ return !document.hasFocus;
45
+ }
46
+ }
47
+
48
+ export function increaseTitleCount(increment) {
49
+ if (!isHidden()) {
50
+ return;
51
+ }
52
+ TITLE = TITLE || document.title;
53
+ TITLE_COUNT = TITLE_COUNT || 0;
54
+ TITLE_COUNT += increment;
55
+ document.title = `${TITLE} (${TITLE_COUNT})`;
56
+ }
57
+
58
+ export function resetTitleCount() {
59
+ TITLE_COUNT = 0;
60
+ document.title = TITLE || document.title;
61
+ }
62
+
63
+ export function formatTime(timestamp) {
64
+ let formatted;
65
+ const time = moment(timestamp);
66
+ const now = moment();
67
+
68
+ if (time.diff(now.startOf("day")) > 0) {
69
+ formatted = time.format("h:mm a");
70
+ } else {
71
+ if (time.diff(now.startOf("week")) > 0) {
72
+ formatted = time.format("dd h:mm a");
73
+ } else {
74
+ if (time.diff(now.startOf("year")) > 0) {
75
+ formatted = time.format("D MMM h:mm a");
76
+ } else {
77
+ formatted = time.format("D MMM YY");
78
+ }
79
+ }
80
+ }
81
+
82
+ return formatted;
83
+ }
84
+
85
+ export function buildArrayString(array) {
86
+ const buffer = [];
87
+ array.forEach(v => {
88
+ if (v === null) {
89
+ buffer.push("null");
90
+ } else if (Object.prototype.toString.call(v) === "[object Array]") {
91
+ buffer.push(buildArrayString(v));
92
+ } else {
93
+ buffer.push(escapeHtml(v.toString()));
94
+ }
95
+ });
96
+ return "[" + buffer.join(", ") + "]";
97
+ }
98
+
99
+ export function buildHashString(hash, recurse) {
100
+ if (!hash) return "";
101
+
102
+ const buffer = [];
103
+ const hashes = [];
104
+ _.each(hash, (v, k) => {
105
+ if (v === null) {
106
+ buffer.push("null");
107
+ } else if (Object.prototype.toString.call(v) === "[object Array]") {
108
+ buffer.push(
109
+ "<tr><td>" +
110
+ escapeHtml(k) +
111
+ "</td><td>" +
112
+ buildArrayString(v) +
113
+ "</td></tr>"
114
+ );
115
+ } else if (typeof v === "object") {
116
+ hashes.push(k);
117
+ } else {
118
+ buffer.push(
119
+ "<tr><td>" + escapeHtml(k) + "</td><td>" + escapeHtml(v) + "</td></tr>"
120
+ );
121
+ }
122
+ });
123
+
124
+ if (_.size(hashes) > 0) {
125
+ _.each(hashes, function(k1) {
126
+ const v = hash[k1];
127
+ buffer.push("<tr><td></td><td><table>");
128
+ buffer.push(
129
+ "<td>" +
130
+ escapeHtml(k1) +
131
+ "</td><td>" +
132
+ buildHashString(v, true) +
133
+ "</td>"
134
+ );
135
+ buffer.push("</table></td></tr>");
136
+ });
137
+ }
138
+ const className = recurse ? "" : "env-table";
139
+ return "<table class='" + className + "'>" + buffer.join("\n") + "</table>";
140
+ }
@@ -1,178 +1,178 @@
1
- import { ajax, increaseTitleCount } from "client-app/lib/utilities";
2
- import Message from "client-app/models/message";
3
- import { compare } from "@ember/utils";
4
- import { computed } from "@ember/object";
5
-
6
- const BATCH_SIZE = 50;
7
-
8
- export default Em.Object.extend({
9
- messages: Em.A(),
10
- currentMessage: null,
11
- total: 0,
12
-
13
- solve(message) {
14
- message.solve().then(() => {
15
- this.reload();
16
- });
17
- },
18
-
19
- destroy(message) {
20
- const messages = this.get("messages");
21
- const idx = messages.indexOf(message);
22
- message.destroy();
23
- message.set("selected", false);
24
- this.set("total", this.get("total") - 1);
25
- this.get("messages").removeObject(message);
26
-
27
- if (idx > 0) {
28
- message = messages[idx - 1];
29
- message.set("selected", true);
30
- this.set("currentMessage", message);
31
- } else {
32
- if (this.get("total") > 0) {
33
- message = messages[0];
34
- message.set("selected", true);
35
- this.set("currentMessage", message);
36
- } else {
37
- this.reload();
38
- }
39
- }
40
- },
41
-
42
- load(opts) {
43
- opts = opts || {};
44
-
45
- const data = {
46
- filter: this.get("filter").join("_")
47
- };
48
-
49
- const search = this.get("search");
50
- if (!_.isEmpty(search)) {
51
- data.search = search;
52
- const regexSearch = this.get("regexSearch");
53
- if (regexSearch) {
54
- data.regex_search = "true";
55
- }
56
- }
57
-
58
- if (opts.before) {
59
- data.before = opts.before;
60
- }
61
-
62
- if (opts.after) {
63
- data.after = opts.after;
64
- }
65
-
66
- return ajax("/messages.json", {
67
- data: data
68
- }).then(data => {
69
- // guard against race: ensure the results we're trying to apply
70
- // match the current search terms
71
- if (compare(data.filter, this.get("filter")) != 0) {
72
- return;
73
- }
74
- if (compare(data.search, this.get("search")) != 0) {
75
- return;
76
- }
77
-
78
- if (data.messages.length > 0) {
79
- const newRows = this.toMessages(data.messages);
80
- const messages = this.get("messages");
81
- if (opts.before) {
82
- messages.unshiftObjects(newRows);
83
- } else {
84
- newRows.forEach(nmsg => {
85
- messages.forEach(emsg => {
86
- if (emsg.key == nmsg.key) {
87
- messages.removeObject(emsg);
88
- if (this.get("currentMessage") === emsg) {
89
- // TODO would updateFromJson() work here?
90
- this.set("currentMessage", nmsg);
91
- nmsg.set("selected", emsg.get("selected"));
92
- }
93
- }
94
- });
95
- });
96
- messages.addObjects(newRows);
97
- if (newRows.length > 0) {
98
- increaseTitleCount(newRows.length);
99
- }
100
- }
101
- }
102
- this.set("total", data.total);
103
- return data;
104
- });
105
- },
106
-
107
- reload() {
108
- this.set("total", 0);
109
- this.get("messages").clear();
110
-
111
- return this.load().then(data => this.updateCanLoadMore(data));
112
- },
113
-
114
- updateCanLoadMore(data) {
115
- if (!data) {
116
- return;
117
- }
118
- if (data.messages.length < BATCH_SIZE) {
119
- this.set("canLoadMore", false);
120
- } else {
121
- this.set("canLoadMore", true);
122
- }
123
- },
124
-
125
- loadMore() {
126
- const messages = this.get("messages");
127
- if (messages.length === 0) {
128
- this.load({});
129
- return;
130
- }
131
-
132
- const lastKey = messages[messages.length - 1].get("key");
133
- this.load({
134
- after: lastKey
135
- });
136
- },
137
-
138
- hideCountInLoadMore: computed("search", "filter", function() {
139
- const search = this.get("search");
140
- const filter = this.get("filter");
141
- return (search && search.length > 0) || (filter && filter.length < 6);
142
- }),
143
-
144
- moreBefore: computed("messages.length", "canLoadMore", function() {
145
- return this.get("messages.length") >= BATCH_SIZE && this.get("canLoadMore");
146
- }),
147
-
148
- totalBefore: computed("total", "messages.length", function() {
149
- return this.get("total") - this.get("messages").length;
150
- }),
151
-
152
- showMoreBefore: function() {
153
- const messages = this.get("messages");
154
- const firstKey = messages[0].get("key");
155
-
156
- this.load({
157
- before: firstKey
158
- }).then(data => this.updateCanLoadMore(data));
159
- },
160
-
161
- regexSearch: computed("search", function() {
162
- const search = this.get("search");
163
- if (search && search.length > 2 && search[0] === "/") {
164
- const match = search.match(/\/(.*)\/(.*)/);
165
- if (match && match.length === 3) {
166
- try {
167
- return new RegExp(match[1], match[2]);
168
- } catch (err) {
169
- // don't care
170
- }
171
- }
172
- }
173
- }),
174
-
175
- toMessages(messages) {
176
- return messages.map(m => Message.create(m));
177
- }
178
- });
1
+ import { ajax, increaseTitleCount } from "client-app/lib/utilities";
2
+ import Message from "client-app/models/message";
3
+ import { compare } from "@ember/utils";
4
+ import { computed } from "@ember/object";
5
+
6
+ const BATCH_SIZE = 50;
7
+
8
+ export default Em.Object.extend({
9
+ messages: Em.A(),
10
+ currentMessage: null,
11
+ total: 0,
12
+
13
+ solve(message) {
14
+ message.solve().then(() => {
15
+ this.reload();
16
+ });
17
+ },
18
+
19
+ destroy(message) {
20
+ const messages = this.get("messages");
21
+ const idx = messages.indexOf(message);
22
+ message.destroy();
23
+ message.set("selected", false);
24
+ this.set("total", this.get("total") - 1);
25
+ this.get("messages").removeObject(message);
26
+
27
+ if (idx > 0) {
28
+ message = messages[idx - 1];
29
+ message.set("selected", true);
30
+ this.set("currentMessage", message);
31
+ } else {
32
+ if (this.get("total") > 0) {
33
+ message = messages[0];
34
+ message.set("selected", true);
35
+ this.set("currentMessage", message);
36
+ } else {
37
+ this.reload();
38
+ }
39
+ }
40
+ },
41
+
42
+ load(opts) {
43
+ opts = opts || {};
44
+
45
+ const data = {
46
+ filter: this.get("filter").join("_")
47
+ };
48
+
49
+ const search = this.get("search");
50
+ if (!_.isEmpty(search)) {
51
+ data.search = search;
52
+ const regexSearch = this.get("regexSearch");
53
+ if (regexSearch) {
54
+ data.regex_search = "true";
55
+ }
56
+ }
57
+
58
+ if (opts.before) {
59
+ data.before = opts.before;
60
+ }
61
+
62
+ if (opts.after) {
63
+ data.after = opts.after;
64
+ }
65
+
66
+ return ajax("/messages.json", {
67
+ data: data
68
+ }).then(data => {
69
+ // guard against race: ensure the results we're trying to apply
70
+ // match the current search terms
71
+ if (compare(data.filter, this.get("filter")) != 0) {
72
+ return;
73
+ }
74
+ if (compare(data.search, this.get("search")) != 0) {
75
+ return;
76
+ }
77
+
78
+ if (data.messages.length > 0) {
79
+ const newRows = this.toMessages(data.messages);
80
+ const messages = this.get("messages");
81
+ if (opts.before) {
82
+ messages.unshiftObjects(newRows);
83
+ } else {
84
+ newRows.forEach(nmsg => {
85
+ messages.forEach(emsg => {
86
+ if (emsg.key == nmsg.key) {
87
+ messages.removeObject(emsg);
88
+ if (this.get("currentMessage") === emsg) {
89
+ // TODO would updateFromJson() work here?
90
+ this.set("currentMessage", nmsg);
91
+ nmsg.set("selected", emsg.get("selected"));
92
+ }
93
+ }
94
+ });
95
+ });
96
+ messages.addObjects(newRows);
97
+ if (newRows.length > 0) {
98
+ increaseTitleCount(newRows.length);
99
+ }
100
+ }
101
+ }
102
+ this.set("total", data.total);
103
+ return data;
104
+ });
105
+ },
106
+
107
+ reload() {
108
+ this.set("total", 0);
109
+ this.get("messages").clear();
110
+
111
+ return this.load().then(data => this.updateCanLoadMore(data));
112
+ },
113
+
114
+ updateCanLoadMore(data) {
115
+ if (!data) {
116
+ return;
117
+ }
118
+ if (data.messages.length < BATCH_SIZE) {
119
+ this.set("canLoadMore", false);
120
+ } else {
121
+ this.set("canLoadMore", true);
122
+ }
123
+ },
124
+
125
+ loadMore() {
126
+ const messages = this.get("messages");
127
+ if (messages.length === 0) {
128
+ this.load({});
129
+ return;
130
+ }
131
+
132
+ const lastKey = messages[messages.length - 1].get("key");
133
+ this.load({
134
+ after: lastKey
135
+ });
136
+ },
137
+
138
+ hideCountInLoadMore: computed("search", "filter", function() {
139
+ const search = this.get("search");
140
+ const filter = this.get("filter");
141
+ return (search && search.length > 0) || (filter && filter.length < 6);
142
+ }),
143
+
144
+ moreBefore: computed("messages.length", "canLoadMore", function() {
145
+ return this.get("messages.length") >= BATCH_SIZE && this.get("canLoadMore");
146
+ }),
147
+
148
+ totalBefore: computed("total", "messages.length", function() {
149
+ return this.get("total") - this.get("messages").length;
150
+ }),
151
+
152
+ showMoreBefore: function() {
153
+ const messages = this.get("messages");
154
+ const firstKey = messages[0].get("key");
155
+
156
+ this.load({
157
+ before: firstKey
158
+ }).then(data => this.updateCanLoadMore(data));
159
+ },
160
+
161
+ regexSearch: computed("search", function() {
162
+ const search = this.get("search");
163
+ if (search && search.length > 2 && search[0] === "/") {
164
+ const match = search.match(/\/(.*)\/(.*)/);
165
+ if (match && match.length === 3) {
166
+ try {
167
+ return new RegExp(match[1], match[2]);
168
+ } catch (err) {
169
+ // don't care
170
+ }
171
+ }
172
+ }
173
+ }),
174
+
175
+ toMessages(messages) {
176
+ return messages.map(m => Message.create(m));
177
+ }
178
+ });