analytic 0.2.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/README.md +98 -9
- data/Rakefile +5 -0
- data/app/assets/images/analytic/icon.svg +1 -0
- data/app/controllers/analytic/dashboard_controller.rb +2 -1
- data/app/controllers/concerns/analytic/trackable.rb +50 -9
- data/app/helpers/analytic/application_helper.rb +42 -0
- data/app/jobs/analytic/track_job.rb +4 -2
- data/app/models/analytic/application_record.rb +2 -0
- data/app/models/analytic/dashboard.rb +61 -29
- data/app/models/analytic/{view.rb → event.rb} +4 -3
- data/app/models/analytic/period.rb +41 -0
- data/app/models/analytic/stat.rb +30 -0
- data/app/packs/analytic/application/components/chart.tsx +148 -0
- data/app/packs/analytic/application/components/index.tsx +21 -0
- data/app/packs/analytic/application/index.ts +1 -0
- data/app/packs/analytic/application/initializers/fontawesome.ts +7 -1
- data/app/packs/analytic/entrypoints/application.tailwind.css +39 -3
- data/app/views/analytic/dashboard/show.html.erb +61 -14
- data/app/views/layouts/analytic/application.html.erb +1 -7
- data/bin/rails +9 -7
- data/db/migrate/{20240805210911_create_analytic_views.rb → 20240805210911_create_analytic_events.rb} +6 -2
- data/lib/analytic/config.rb +63 -3
- data/lib/analytic/engine.rb +4 -0
- data/lib/analytic/version.rb +1 -1
- metadata +13 -11
- data/app/assets/builds/analytic/application.css +0 -810
- data/app/assets/builds/analytic/application.js +0 -54498
- data/app/assets/builds/analytic/application.js.map +0 -7
@@ -0,0 +1,148 @@
|
|
1
|
+
import { useMemo, type FC } from "react";
|
2
|
+
|
3
|
+
import { Group } from "@visx/group";
|
4
|
+
import { Bar } from "@visx/shape";
|
5
|
+
import { scaleBand, scaleLinear } from "@visx/scale";
|
6
|
+
import { AxisBottom, AxisLeft } from "@visx/axis";
|
7
|
+
import { GridColumns, GridRows } from "@visx/grid";
|
8
|
+
import { ParentSize } from "@visx/responsive";
|
9
|
+
|
10
|
+
const BAR_BG = "#6366f1"; // indigo-500
|
11
|
+
const CHART_BG = "#f8fafc"; // slate-50
|
12
|
+
const GRID_COLOR = "#e2e8f0"; // slate-200
|
13
|
+
const AXIS_TICK_COLOR = GRID_COLOR;
|
14
|
+
const AXIS_LINE_COLOR = GRID_COLOR;
|
15
|
+
|
16
|
+
const CHART_ML = 60; // px
|
17
|
+
const CHART_MB = 60; // px
|
18
|
+
const CHART_MT = 40; // px
|
19
|
+
const CHART_MR = 40; // px
|
20
|
+
|
21
|
+
const TICK_LABEL_PROPS = {
|
22
|
+
fill: "#718096", // slate-600
|
23
|
+
fontSize: 10,
|
24
|
+
fontFamily: `"ui-sans-serif", "system-ui", "sans-serif"`,
|
25
|
+
} as const;
|
26
|
+
|
27
|
+
type Entry = {
|
28
|
+
label: string;
|
29
|
+
value: number;
|
30
|
+
};
|
31
|
+
|
32
|
+
const ENTRIES: Entry[] = [
|
33
|
+
{ label: "Jan, 2024", value: 404 },
|
34
|
+
{ label: "Feb, 2024", value: 303 },
|
35
|
+
{ label: "Mar, 2024", value: 250 },
|
36
|
+
{ label: "Apr, 2024", value: 304 },
|
37
|
+
{ label: "May, 2024", value: 404 },
|
38
|
+
{ label: "Jun, 2024", value: 604 },
|
39
|
+
];
|
40
|
+
|
41
|
+
const Bars: FC<{
|
42
|
+
width: number;
|
43
|
+
height: number;
|
44
|
+
entries: Entry[];
|
45
|
+
}> = ({ width, height, entries }) => {
|
46
|
+
const xScale = useMemo(
|
47
|
+
() =>
|
48
|
+
scaleBand<string>({
|
49
|
+
range: [0, width],
|
50
|
+
round: true,
|
51
|
+
domain: entries.map(({ label }) => label),
|
52
|
+
padding: 0.4,
|
53
|
+
}),
|
54
|
+
[width]
|
55
|
+
);
|
56
|
+
const yScale = useMemo(
|
57
|
+
() =>
|
58
|
+
scaleLinear<number>({
|
59
|
+
range: [height, 0],
|
60
|
+
round: true,
|
61
|
+
domain: [0, Math.max(...entries.map(({ value }) => value))],
|
62
|
+
}),
|
63
|
+
[height]
|
64
|
+
);
|
65
|
+
|
66
|
+
return (
|
67
|
+
<>
|
68
|
+
<GridRows
|
69
|
+
scale={yScale}
|
70
|
+
width={width}
|
71
|
+
height={height}
|
72
|
+
stroke={GRID_COLOR}
|
73
|
+
/>
|
74
|
+
|
75
|
+
<GridColumns
|
76
|
+
scale={xScale}
|
77
|
+
width={width}
|
78
|
+
height={height}
|
79
|
+
stroke={GRID_COLOR}
|
80
|
+
/>
|
81
|
+
|
82
|
+
{entries.map((entry, index) => {
|
83
|
+
const barWidth = xScale.bandwidth();
|
84
|
+
const barHeight = height - yScale(entry.value);
|
85
|
+
const barX = xScale(entry.label);
|
86
|
+
const barY = height - barHeight;
|
87
|
+
|
88
|
+
return (
|
89
|
+
<Bar
|
90
|
+
key={index}
|
91
|
+
x={barX}
|
92
|
+
y={barY}
|
93
|
+
rx={8}
|
94
|
+
ry={8}
|
95
|
+
width={barWidth}
|
96
|
+
height={barHeight}
|
97
|
+
fill={BAR_BG}
|
98
|
+
/>
|
99
|
+
);
|
100
|
+
})}
|
101
|
+
|
102
|
+
<AxisBottom
|
103
|
+
top={height}
|
104
|
+
scale={xScale}
|
105
|
+
stroke={AXIS_LINE_COLOR}
|
106
|
+
tickStroke={AXIS_TICK_COLOR}
|
107
|
+
tickLabelProps={TICK_LABEL_PROPS}
|
108
|
+
/>
|
109
|
+
|
110
|
+
<AxisLeft
|
111
|
+
left={0}
|
112
|
+
scale={yScale}
|
113
|
+
stroke={AXIS_LINE_COLOR}
|
114
|
+
tickStroke={AXIS_TICK_COLOR}
|
115
|
+
tickLabelProps={TICK_LABEL_PROPS}
|
116
|
+
/>
|
117
|
+
</>
|
118
|
+
);
|
119
|
+
};
|
120
|
+
|
121
|
+
export const Chart: FC<{
|
122
|
+
height?: number;
|
123
|
+
entries?: Entry[];
|
124
|
+
}> = ({ height = 300, entries = ENTRIES }) => (
|
125
|
+
<ParentSize>
|
126
|
+
{({ width }) => (
|
127
|
+
<svg width={width} height={height}>
|
128
|
+
<rect
|
129
|
+
x={0}
|
130
|
+
y={0}
|
131
|
+
rx={4}
|
132
|
+
ry={4}
|
133
|
+
width={width}
|
134
|
+
height={height}
|
135
|
+
fill={CHART_BG}
|
136
|
+
/>
|
137
|
+
|
138
|
+
<Group top={CHART_MT} left={CHART_ML}>
|
139
|
+
<Bars
|
140
|
+
width={width - CHART_MR - CHART_ML}
|
141
|
+
height={height - CHART_MT - CHART_MB}
|
142
|
+
entries={entries}
|
143
|
+
/>
|
144
|
+
</Group>
|
145
|
+
</svg>
|
146
|
+
)}
|
147
|
+
</ParentSize>
|
148
|
+
);
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { type FC } from "react";
|
2
|
+
|
3
|
+
import { Chart } from "./chart";
|
4
|
+
import { createRoot } from "react-dom/client";
|
5
|
+
|
6
|
+
const COMPONENTS: Record<string, FC> = {
|
7
|
+
Chart,
|
8
|
+
};
|
9
|
+
|
10
|
+
document.addEventListener("DOMContentLoaded", () => {
|
11
|
+
const elements = document.querySelectorAll("[data-react]");
|
12
|
+
for (const element of elements) {
|
13
|
+
const root = createRoot(element);
|
14
|
+
|
15
|
+
const name = element.getAttribute("data-react");
|
16
|
+
|
17
|
+
const Component = COMPONENTS[name!];
|
18
|
+
|
19
|
+
root.render(<Component />);
|
20
|
+
}
|
21
|
+
});
|
@@ -10,14 +10,20 @@ import { faGlobe } from "@fortawesome/free-solid-svg-icons/faGlobe";
|
|
10
10
|
import { faHouse } from "@fortawesome/free-solid-svg-icons/faHouse";
|
11
11
|
import { faSliders } from "@fortawesome/free-solid-svg-icons/faSliders";
|
12
12
|
import { faUsers } from "@fortawesome/free-solid-svg-icons/faUsers";
|
13
|
+
import { faArrowUp } from "@fortawesome/free-solid-svg-icons/faArrowUp";
|
14
|
+
import { faArrowDown } from "@fortawesome/free-solid-svg-icons/faArrowDown";
|
15
|
+
import { faXmark } from "@fortawesome/free-solid-svg-icons/faXmark";
|
13
16
|
|
14
17
|
library.add(
|
18
|
+
faArrowDown,
|
19
|
+
faArrowUp,
|
15
20
|
faClock,
|
16
21
|
faEye,
|
22
|
+
faFileCode,
|
17
23
|
faGauge,
|
18
24
|
faGlobe,
|
19
25
|
faHouse,
|
20
26
|
faSliders,
|
21
27
|
faUsers,
|
22
|
-
|
28
|
+
faXmark
|
23
29
|
);
|
@@ -3,8 +3,40 @@
|
|
3
3
|
@tailwind utilities;
|
4
4
|
|
5
5
|
@layer components {
|
6
|
+
.stat {
|
7
|
+
@apply flex gap-2 justify-between items-center;
|
8
|
+
}
|
9
|
+
|
10
|
+
.delta {
|
11
|
+
@apply font-normal flex gap-2 items-center;
|
12
|
+
}
|
13
|
+
|
14
|
+
.delta--neutral {
|
15
|
+
@apply text-slate-400;
|
16
|
+
}
|
17
|
+
|
18
|
+
.delta--positive {
|
19
|
+
@apply text-emerald-600;
|
20
|
+
}
|
21
|
+
|
22
|
+
.delta--negative {
|
23
|
+
@apply text-rose-600;
|
24
|
+
}
|
25
|
+
|
26
|
+
.pills {
|
27
|
+
@apply inline-flex gap-2 bg-slate-100 border-slate-200 rounded-full text-slate-600;
|
28
|
+
}
|
29
|
+
|
6
30
|
.pill {
|
7
|
-
@apply
|
31
|
+
@apply flex items-center justify-center gap-2 rounded-full px-6 py-2;
|
32
|
+
}
|
33
|
+
|
34
|
+
.pill--default {
|
35
|
+
@apply bg-slate-100 font-medium hover:bg-slate-200 hover:text-slate-800;
|
36
|
+
}
|
37
|
+
|
38
|
+
.pill--active {
|
39
|
+
@apply bg-white shadow font-semibold text-slate-800;
|
8
40
|
}
|
9
41
|
|
10
42
|
.card {
|
@@ -20,7 +52,7 @@
|
|
20
52
|
}
|
21
53
|
|
22
54
|
.card__content {
|
23
|
-
@apply px-5 py-4
|
55
|
+
@apply px-5 py-4;
|
24
56
|
}
|
25
57
|
|
26
58
|
.card__title {
|
@@ -28,7 +60,11 @@
|
|
28
60
|
}
|
29
61
|
|
30
62
|
.card__value {
|
31
|
-
@apply
|
63
|
+
@apply text-slate-800 font-extrabold text-lg;
|
64
|
+
}
|
65
|
+
|
66
|
+
.cards {
|
67
|
+
@apply grid gap-4 grid-cols-1 md:grid-cols-3;
|
32
68
|
}
|
33
69
|
|
34
70
|
.table {
|
@@ -1,25 +1,28 @@
|
|
1
1
|
<%- provide(:title, @dashboard.name) %>
|
2
2
|
|
3
3
|
<div class="space-y-4">
|
4
|
-
<div class="
|
5
|
-
<%= link_to '
|
6
|
-
<%= link_to '
|
7
|
-
<%= link_to '
|
8
|
-
<%= link_to '
|
9
|
-
<%= link_to '
|
4
|
+
<div class="pills">
|
5
|
+
<%= link_to 'All', analytic.dashboard_path, class: "pill #{@dashboard.period.nil? ? 'pill--active' : 'pill--default'}" %>
|
6
|
+
<%= link_to '24 hours', analytic.dashboard_path(period: '24h'), class: "pill #{@dashboard.period.eql?('24h') ? 'pill--active' : 'pill--default'}" %>
|
7
|
+
<%= link_to '7 days', analytic.dashboard_path(period: '7d'), class: "pill #{@dashboard.period.eql?('7d') ? 'pill--active' : 'pill--default'}" %>
|
8
|
+
<%= link_to '4 weeks', analytic.dashboard_path(period: '4w'), class: "pill #{@dashboard.period.eql?('4w') ? 'pill--active' : 'pill--default'}" %>
|
9
|
+
<%= link_to '12 months', analytic.dashboard_path(period: '12m'), class: "pill #{@dashboard.period.eql?('12m') ? 'pill--active' : 'pill--default'}" %>
|
10
10
|
</div>
|
11
11
|
|
12
|
-
<div class="
|
12
|
+
<div class="cards">
|
13
13
|
<div class="card">
|
14
14
|
<div class="card__header">
|
15
15
|
<div class="card__title">
|
16
|
-
<%=
|
16
|
+
<%= fa_icon_tag("fa-solid fa-users") %>
|
17
17
|
Visitors
|
18
18
|
</div>
|
19
19
|
</div>
|
20
20
|
<div class="card__content">
|
21
21
|
<div class="card__value">
|
22
|
-
|
22
|
+
<div class="stat">
|
23
|
+
<%= number_with_delimiter @dashboard.visitors.count %>
|
24
|
+
<%= delta_tag(@dashboard.visitors.delta) %>
|
25
|
+
</div>
|
23
26
|
</div>
|
24
27
|
</div>
|
25
28
|
</div>
|
@@ -27,13 +30,16 @@
|
|
27
30
|
<div class="card">
|
28
31
|
<div class="card__header">
|
29
32
|
<div class="card__title">
|
30
|
-
<%=
|
33
|
+
<%= fa_icon_tag("fa-solid fa-globe") %>
|
31
34
|
Sessions
|
32
35
|
</div>
|
33
36
|
</div>
|
34
37
|
<div class="card__content">
|
35
38
|
<div class="card__value">
|
36
|
-
|
39
|
+
<div class="stat">
|
40
|
+
<%= number_with_delimiter @dashboard.sessions.count %>
|
41
|
+
<%= delta_tag(@dashboard.sessions.delta) %>
|
42
|
+
</div>
|
37
43
|
</div>
|
38
44
|
</div>
|
39
45
|
</div>
|
@@ -41,13 +47,16 @@
|
|
41
47
|
<div class="card">
|
42
48
|
<div class="card__header">
|
43
49
|
<div class="card__title">
|
44
|
-
<%=
|
50
|
+
<%= fa_icon_tag("fa-solid fa-eye") %>
|
45
51
|
Views
|
46
52
|
</div>
|
47
53
|
</div>
|
48
54
|
<div class="card__content">
|
49
55
|
<div class="card__value">
|
50
|
-
|
56
|
+
<div class="stat">
|
57
|
+
<%= number_with_delimiter @dashboard.views.count %>
|
58
|
+
<%= delta_tag(@dashboard.views.delta) %>
|
59
|
+
</div>
|
51
60
|
</div>
|
52
61
|
</div>
|
53
62
|
</div>
|
@@ -70,7 +79,7 @@
|
|
70
79
|
</tr>
|
71
80
|
</thead>
|
72
81
|
<tbody>
|
73
|
-
<%- @dashboard.pages.each do |(host, path, views)| -%>
|
82
|
+
<%- @dashboard.current.pages.each do |(host, path, views)| -%>
|
74
83
|
<tr>
|
75
84
|
<td><%= host %></td>
|
76
85
|
<td><%= path %></td>
|
@@ -81,4 +90,42 @@
|
|
81
90
|
</table>
|
82
91
|
</div>
|
83
92
|
</div>
|
93
|
+
|
94
|
+
<div class="cards">
|
95
|
+
<div class="card">
|
96
|
+
<div class="card__header">
|
97
|
+
<div class="card__title">
|
98
|
+
<%= tag.i class: "fa-solid fa-file-code" %>
|
99
|
+
Visitors
|
100
|
+
</div>
|
101
|
+
</div>
|
102
|
+
<div class="card__content">
|
103
|
+
<%= react(component: "Chart") %>
|
104
|
+
</div>
|
105
|
+
</div>
|
106
|
+
|
107
|
+
<div class="card">
|
108
|
+
<div class="card__header">
|
109
|
+
<div class="card__title">
|
110
|
+
<%= tag.i class: "fa-solid fa-globe" %>
|
111
|
+
Sessions
|
112
|
+
</div>
|
113
|
+
</div>
|
114
|
+
<div class="card__content">
|
115
|
+
<%= react(component: "Chart") %>
|
116
|
+
</div>
|
117
|
+
</div>
|
118
|
+
|
119
|
+
<div class="card">
|
120
|
+
<div class="card__header">
|
121
|
+
<div class="card__title">
|
122
|
+
<%= tag.i class: "fa-solid fa-eye" %>
|
123
|
+
Views
|
124
|
+
</div>
|
125
|
+
</div>
|
126
|
+
<div class="card__content">
|
127
|
+
<%= react(component: "Chart") %>
|
128
|
+
</div>
|
129
|
+
</div>
|
130
|
+
</div>
|
84
131
|
</div>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<%= csp_meta_tag %>
|
7
7
|
<%= javascript_include_tag 'analytic/application' %>
|
8
8
|
<%= stylesheet_link_tag 'analytic/application' %>
|
9
|
-
<%= %>
|
9
|
+
<%= favicon_link_tag 'analytic/icon.svg', type: 'image/svg+xml' %>
|
10
10
|
</head>
|
11
11
|
<body class="bg-slate-50">
|
12
12
|
|
@@ -32,11 +32,5 @@
|
|
32
32
|
<main class="container mx-auto px-4 py-8">
|
33
33
|
<%= yield %>
|
34
34
|
</main>
|
35
|
-
|
36
|
-
<footer class="container mx-auto px-4 text-center">
|
37
|
-
<div class="flex gap-2 justify-center">
|
38
|
-
<%= time_tag(Time.current) %>
|
39
|
-
</div>
|
40
|
-
</footer>
|
41
35
|
</body>
|
42
36
|
</html>
|
data/bin/rails
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
ENGINE_PATH = File.expand_path("../lib/analytic/engine", __dir__)
|
5
|
-
APP_PATH = File.expand_path("../spec/dummy/config/application", __dir__)
|
3
|
+
# frozen_string_literal: true
|
6
4
|
|
7
|
-
|
8
|
-
|
5
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
6
|
+
ENGINE_PATH = File.expand_path('../lib/analytic/engine', __dir__)
|
7
|
+
APP_PATH = File.expand_path('../spec/dummy/config/application', __dir__)
|
9
8
|
|
10
|
-
|
11
|
-
require
|
9
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
10
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
11
|
+
|
12
|
+
require 'rails/all'
|
13
|
+
require 'rails/engine/commands'
|
data/db/migrate/{20240805210911_create_analytic_views.rb → 20240805210911_create_analytic_events.rb}
RENAMED
@@ -1,20 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
3
|
+
class CreateAnalyticEvents < ActiveRecord::Migration[7.1]
|
4
4
|
def change
|
5
|
-
create_table :
|
5
|
+
create_table :analytic_events do |t|
|
6
6
|
t.uuid :visitor_id, null: false
|
7
7
|
t.uuid :session_id, null: false
|
8
|
+
t.inet :ip, null: false
|
8
9
|
t.string :path, null: false
|
9
10
|
t.string :host, null: false
|
10
11
|
t.jsonb :params, null: false, default: {}
|
11
12
|
t.datetime :timestamp, null: false
|
13
|
+
t.text :referrer, null: true
|
14
|
+
t.text :user_agent, null: true
|
12
15
|
|
13
16
|
t.timestamps
|
14
17
|
|
15
18
|
t.index %i[timestamp path]
|
16
19
|
t.index %i[timestamp visitor_id]
|
17
20
|
t.index %i[timestamp session_id]
|
21
|
+
t.index %i[timestamp referrer]
|
18
22
|
end
|
19
23
|
end
|
20
24
|
end
|
data/lib/analytic/config.rb
CHANGED
@@ -2,11 +2,71 @@
|
|
2
2
|
|
3
3
|
module Analytic
|
4
4
|
class Config
|
5
|
-
# @
|
6
|
-
|
5
|
+
# @example
|
6
|
+
# config.time_zone = ActiveSupport::TimeZone['Tokyo']
|
7
|
+
#
|
8
|
+
# @!attribute [rw] time_zone
|
9
|
+
# @return [ActiveSupport::TimeZone]
|
10
|
+
attr_accessor :time_zone
|
11
|
+
|
12
|
+
# @example
|
13
|
+
# config.ip_v4_mask = 24
|
14
|
+
#
|
15
|
+
# @!attribute [rw] ip_v4_mask
|
16
|
+
# @return [Integer]
|
17
|
+
attr_accessor :ip_v4_mask
|
18
|
+
|
19
|
+
# @example
|
20
|
+
# config.ip_v6_mask = 48
|
21
|
+
#
|
22
|
+
# @!attribute [rw] ip_v6_mask
|
23
|
+
# @return [Integer]
|
24
|
+
attr_accessor :ip_v6_mask
|
25
|
+
|
26
|
+
# @example
|
27
|
+
# config.connects_to = database: { writing: :primary, reading: :replica }
|
28
|
+
#
|
29
|
+
# @!attribute [rw] connects_to
|
30
|
+
# @return [Hash, nil]
|
31
|
+
attr_accessor :connects_to
|
32
|
+
|
33
|
+
# @!attribute [rw] middleware
|
34
|
+
# @return [Array<Rack::Middleware>]
|
35
|
+
attr_accessor :middleware
|
36
|
+
|
37
|
+
# @example
|
38
|
+
# config.params = %i[utm_source utm_medium utm_campaign utm_content utm_term ref source]
|
39
|
+
#
|
40
|
+
# @!attribute [rw] params
|
41
|
+
# @return [Array<Symbol>]
|
42
|
+
attr_accessor :params
|
7
43
|
|
8
44
|
def initialize
|
9
|
-
@
|
45
|
+
@time_zone = Time.zone
|
46
|
+
@ip_v4_mask = 24 # e.g. 255.255.255.255 => '255.255.255.0/255.255.255.0'
|
47
|
+
@ip_v6_mask = 48 # e.g. 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' => 'ffff:ffff:ffff:0000:0000:0000:0000:0000'
|
48
|
+
@middleware = []
|
49
|
+
@params = %i[utm_source utm_medium utm_campaign utm_content utm_term ref source]
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Boolean]
|
53
|
+
def ip_v4_mask?
|
54
|
+
@ip_v4_mask.present?
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Boolean]
|
58
|
+
def ip_v6_mask?
|
59
|
+
@ip_v6_mask.present?
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Boolean]
|
63
|
+
def connects_to?
|
64
|
+
@connects_to.present?
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param middleware [Rack::Middleware]
|
68
|
+
def use(*args, &block)
|
69
|
+
middleware << [args, block]
|
10
70
|
end
|
11
71
|
end
|
12
72
|
end
|
data/lib/analytic/engine.rb
CHANGED
data/lib/analytic/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: analytic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Sylvestre
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -34,10 +34,8 @@ files:
|
|
34
34
|
- LICENSE
|
35
35
|
- README.md
|
36
36
|
- Rakefile
|
37
|
-
- app/assets/builds/analytic/application.css
|
38
|
-
- app/assets/builds/analytic/application.js
|
39
|
-
- app/assets/builds/analytic/application.js.map
|
40
37
|
- app/assets/config/analytic_manifest.js
|
38
|
+
- app/assets/images/analytic/icon.svg
|
41
39
|
- app/controllers/analytic/application_controller.rb
|
42
40
|
- app/controllers/analytic/dashboard_controller.rb
|
43
41
|
- app/controllers/concerns/analytic/trackable.rb
|
@@ -47,7 +45,11 @@ files:
|
|
47
45
|
- app/mailers/analytic/application_mailer.rb
|
48
46
|
- app/models/analytic/application_record.rb
|
49
47
|
- app/models/analytic/dashboard.rb
|
50
|
-
- app/models/analytic/
|
48
|
+
- app/models/analytic/event.rb
|
49
|
+
- app/models/analytic/period.rb
|
50
|
+
- app/models/analytic/stat.rb
|
51
|
+
- app/packs/analytic/application/components/chart.tsx
|
52
|
+
- app/packs/analytic/application/components/index.tsx
|
51
53
|
- app/packs/analytic/application/index.ts
|
52
54
|
- app/packs/analytic/application/initializers/fontawesome.ts
|
53
55
|
- app/packs/analytic/application/initializers/index.ts
|
@@ -58,7 +60,7 @@ files:
|
|
58
60
|
- bin/dev
|
59
61
|
- bin/rails
|
60
62
|
- config/routes.rb
|
61
|
-
- db/migrate/
|
63
|
+
- db/migrate/20240805210911_create_analytic_events.rb
|
62
64
|
- lib/analytic.rb
|
63
65
|
- lib/analytic/config.rb
|
64
66
|
- lib/analytic/engine.rb
|
@@ -68,7 +70,7 @@ licenses:
|
|
68
70
|
- MIT
|
69
71
|
metadata:
|
70
72
|
rubygems_mfa_required: 'true'
|
71
|
-
post_install_message:
|
73
|
+
post_install_message:
|
72
74
|
rdoc_options: []
|
73
75
|
require_paths:
|
74
76
|
- lib
|
@@ -83,8 +85,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
85
|
- !ruby/object:Gem::Version
|
84
86
|
version: '0'
|
85
87
|
requirements: []
|
86
|
-
rubygems_version: 3.5.
|
87
|
-
signing_key:
|
88
|
+
rubygems_version: 3.5.18
|
89
|
+
signing_key:
|
88
90
|
specification_version: 4
|
89
91
|
summary: Analytic
|
90
92
|
test_files: []
|