active_analytics 0.2.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5054460d87d609cd11f9418dd84963ff89407d20c22beb685b6d2a9eb8b2f922
4
- data.tar.gz: 3b2b67cb7d9ce4e652bd22ea519fa294f05fe3b105bd3bfbae932e477a3f9fe2
3
+ metadata.gz: 8be54d2704a6f0c7141fd6c0c4d276992d13f8cd1126868a5e71d038ba4333e5
4
+ data.tar.gz: 4a6eb25b26894054f9a1b669a198a6f97d2e13bdf892d8b09b5f8da5251036db
5
5
  SHA512:
6
- metadata.gz: 2280cf59a6a021c902451bf8a51b7ad678f57917aad786d16a4eeebfc2931766c49f99081eba4d13e0a74fbeba711b26c9c3c12f9405edc20dee4902908f6077
7
- data.tar.gz: 915bd565e0218737fc98ce6a97948896be7beb65669a3c8fb5d29d1936f11e8476ad1c24324967ce9fccb7c0db65ebab74c5c31d0d582d7aa0d03dbe7099fb52
6
+ metadata.gz: efae89347949dc906204ee4188b278a4482e46a2575a83868c4ffc73915869c46f88517fc235c4976ebec9f7474bc26b6823066156aaa2ccffa9ffaeb1128164
7
+ data.tar.gz: 4b94f1808a3ce5da5e8050fda39270fb7f65990aecb708f0dc6e54174139fa5d9a4f4c87431917be607fed4ad3580710af84b8449f290bbebb78308086ed6f25
data/README.md CHANGED
@@ -32,7 +32,8 @@ rails active_analytics:install:migrations
32
32
  rails db:migrate
33
33
  ```
34
34
 
35
- Your controllers have to call `ActiveAnalytics.record_request(request)` to record page views:
35
+ Your controllers have to call `ActiveAnalytics.record_request(request)` to record page views. The Rails way to achieve is to use a `before_action` :
36
+
36
37
  ```ruby
37
38
  class ApplicationController < ActionController::Base
38
39
  before_action :record_page_view
@@ -45,27 +46,43 @@ class ApplicationController < ActionController::Base
45
46
  end
46
47
  ```
47
48
 
48
- This is a basic `before_action`. In case you don't want to record all page views, simply define a `skip_before_action :record_page_view` in the relevant controller.
49
+ In case you don't want to record all page views, because each application has sensitive URLs such as password reset and so on, simply define a `skip_before_action :record_page_view` in the relevant controller.
49
50
 
50
51
  Finally, just add the route to ActiveAnalytics dashboard at the desired endpoint:
52
+
51
53
  ```ruby
52
54
  mount ActiveAnalytics::Engine, at: "analytics" # http://localhost:3000/analytics
53
55
  ```
54
56
 
55
57
  ## Authentication and permissions
56
- ActiveAnalytics cannot guess how you handle user authentication, because it is different for all Rails applications. So you have to inject your own mechanism into `ActiveAnalytics::ApplicationController`. Create a file in `config/initializers/active_analytics.rb`:
58
+
59
+ ActiveAnalytics cannot guess how you handle user authentication, because it is different for all Rails applications. So you have to monkey patch `ActiveAnalytics::ApplicationController` in order to inject your own mechanism. Create a file in `config/initializers/active_analytics.rb` to add a before action :
57
60
 
58
61
  ```ruby
62
+ # config/initializers/active_analytics.rb
59
63
  require_dependency "active_analytics/application_controller"
60
64
 
61
65
  module ActiveAnalytics
62
66
  class ApplicationController
63
- # include Currentuser # This is an example that you have to change by
64
- # before_action :require_admin # your own modules and methods
67
+ before_action :require_admin
68
+
69
+ def require_admin
70
+ # This example supposes there are current_user and User#admin? methods
71
+ raise ActionController::RoutingError.new("Not found") unless current_user.try(:admin?)
72
+ end
65
73
  end
66
74
  end
67
75
  ```
68
76
 
77
+ If you have Devise, you can check the permission directly from routes.rb :
78
+
79
+ ```ruby
80
+ # config/routes.rb
81
+ authenticate :user, -> (u) { u.admin? } do # Supposing there is a User#admin? method
82
+ mount ActiveAnalytics::Engine, at: "analytics" # http://localhost:3000/analytics
83
+ end
84
+ ```
85
+
69
86
  ## License
70
87
  The gem is available as open-source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
71
88
 
@@ -86,137 +86,6 @@ Ariato.assignRoles = function(container, controller) {
86
86
  controller[name] = controller.roles[name][0]
87
87
  }
88
88
 
89
- Ariato.Accordion = function(node) {
90
- this.node = node
91
- node.ariaAccordion = this
92
- this.regions = []
93
- }
94
-
95
- Ariato.Accordion.addRegion = function(region) {
96
- var button = region.labelledBy()
97
-
98
- if (!button)
99
- return
100
-
101
- var accordion = region.node.parentElement.ariaAccordion || new Ariato.Accordion(region.node.parentElement)
102
- accordion.addRegion(region)
103
- return accordion
104
- }
105
-
106
- Ariato.Accordion.prototype.addRegion = function(region) {
107
- this.regions.push(region)
108
- }
109
-
110
- Ariato.Accordion.prototype.hideRegions = function() {
111
- for (var i = 0; i < this.regions.length; i++)
112
- this.regions[i].hide()
113
- }
114
-
115
- Ariato.Accordion.prototype.showRegion = function(region) {
116
- if (this.mutilpleAllowed())
117
- region.expanded() ? region.hide() : region.show()
118
- else {
119
- this.hideRegions()
120
- region.show()
121
- }
122
- }
123
-
124
- Ariato.Accordion.prototype.mutilpleAllowed = function() {
125
- return this.node.hasAttribute("data-allow-multiple")
126
- }
127
-
128
- Ariato.Carousel = function() {
129
- this.currentSlide() || this.showSlide(this.slides()[0])
130
- this.node.addEventListener("keydown", this.keydown.bind(this))
131
-
132
- var nextButton = this.node.querySelector("[data-carousel=next]")
133
- nextButton && nextButton.addEventListener("click", this.clicked.bind(this))
134
-
135
- var previousButton = this.node.querySelector("[data-carousel=previous]")
136
- previousButton.addEventListener("click", this.clicked.bind(this))
137
- }
138
-
139
- Ariato.Carousel.prototype.slides = function() {
140
- return this.node.querySelectorAll("[aria-roledescription=slide]")
141
- }
142
-
143
- Ariato.Carousel.prototype.currentSlide = function() {
144
- return this.node.querySelector("[aria-current=slide]")
145
- }
146
-
147
- Ariato.Carousel.prototype.showSlide = function(slide) {
148
- var slides = this.slides()
149
-
150
- for (var i = 0; i < slides.length; i++)
151
- if (slides[i] == slide)
152
- slides[i].setAttribute("aria-current", "slide")
153
- else
154
- slides[i].removeAttribute("aria-current")
155
- }
156
-
157
- Ariato.Carousel.prototype.nextSlide = function(slide) {
158
- var slides = this.slides()
159
- this.currentSlide()
160
- for (var i = 0; i < slides.length; i++) {
161
- if (slides[i] == slide)
162
- slides[i].setAttribute("aria-current", "slide")
163
- else
164
- slides[i].removeAttribute("aria-current")
165
- }
166
- }
167
-
168
- Ariato.Carousel.prototype.nextSlide = function(slide) {
169
- var current = this.currentSlide()
170
- return current && current.nextElementSibling
171
- }
172
-
173
- Ariato.Carousel.prototype.previousSlide = function(slide) {
174
- var current = this.currentSlide()
175
- return current && current.previousElementSibling
176
- }
177
-
178
- Ariato.Carousel.prototype.keydown = function(event) {
179
- switch(event.key) {
180
- case "ArrowLeft":
181
- this.showSlide(this.previousOrLastSlide())
182
- break
183
- case "ArrowRight":
184
- this.showSlide(this.nextOrFirstSlide())
185
- break
186
- }
187
- }
188
-
189
- Ariato.Carousel.prototype.clicked = function(event) {
190
- switch(event.currentTarget.getAttribute("data-carousel")) {
191
- case "next":
192
- this.showSlide(this.previousOrLastSlide())
193
- break
194
- case "previous":
195
- this.showSlide(this.nextOrFirstSlide())
196
- break
197
- }
198
- }
199
-
200
- Ariato.Carousel.prototype.previousOrLastSlide = function(event) {
201
- var slide = this.previousSlide()
202
- if (slide)
203
- return slide
204
- else {
205
- var slides = this.slides()
206
- return slides[slides.length-1]
207
- }
208
- }
209
-
210
- Ariato.Carousel.prototype.nextOrFirstSlide = function(event) {
211
- var slide = this.nextSlide()
212
- if (slide)
213
- return slide
214
- else {
215
- var slides = this.slides()
216
- return slides[0]
217
- }
218
- }
219
-
220
89
  Ariato.Dialog = function(node) {
221
90
  node.setAttribute("hidden", true)
222
91
  node.addEventListener("open", this.open.bind(this))
@@ -346,139 +215,6 @@ Ariato.Dialog.prototype.removeBackdrop = function() {
346
215
 
347
216
  Ariato.Alertdialog = Ariato.Dialog
348
217
 
349
- Ariato.MenuBar = function() {
350
- this.node.addEventListener("keydown", this.keyDown.bind(this))
351
- }
352
-
353
- // Ariato defines role="menubar" but MenuBar in camel case is nicer
354
- Ariato.Menubar = Ariato.MenuBar
355
-
356
- Ariato.Menubar.prototype.keyDown = function(event) {
357
- switch (event.key) {
358
- case "ArrowDown":
359
- event.preventDefault()
360
- if (event.target.hasAttribute("aria-haspopup"))
361
- this.openItem(event.target)
362
- else
363
- this.focusNextItem(event.target)
364
- break
365
- case "ArrowUp":
366
- event.preventDefault()
367
- if (event.target.hasAttribute("aria-haspopup"))
368
- this.openItem(event.target)
369
- else
370
- this.focusPreviousItem(event.target)
371
- break
372
- case "ArrowRight":
373
- // Open parent next menu
374
- // Open child menu
375
- // Focus next item
376
- this.openNextMenu(event.target)
377
- break
378
- if (event.target.hasAttribute("aria-haspopup"))
379
- this.openItem(event.target)
380
- else
381
- this.openNextMenu(this.findParentMenu(event.target))
382
- case "ArrowLeft":
383
- this.openPreviousMenu(event.target)
384
- break
385
- case "Escape":
386
- this.closeAllExcept()
387
- break
388
- }
389
- }
390
-
391
- Ariato.Menubar.prototype.closeAllExcept = function(item) {
392
- var menus = this.node.querySelectorAll("[role=menu]")
393
- for (var i = 0; i < menus.length; i++)
394
- menus[i].style.display = menus[i].contains(item) ? "block" : null
395
- }
396
-
397
- Ariato.Menubar.prototype.openItem = function(item) {
398
- var menu = item.parentElement.querySelector("[role=menu]")
399
- item.setAttribute("aria-expanded", true)
400
- var subItem = menu.querySelector("[role=menuitem]")
401
- if (subItem) {
402
- this.closeAllExcept(subItem)
403
- subItem.focus()
404
- } else {
405
- this.closeAllExcept(item)
406
- item.focus()
407
- }
408
- }
409
-
410
- Ariato.Menubar.prototype.openNextMenu = function(item) {
411
- var menu = this.findNextMenu(item)
412
- menu && this.openItem(menu.parentElement.querySelector("[role=menuitem]"))
413
- }
414
-
415
- Ariato.Menubar.prototype.openPreviousMenu = function(item) {
416
- var menu = this.findPreviousMenu(item)
417
- menu && this.openItem(menu.parentElement.querySelector("[role=menuitem]"))
418
- }
419
-
420
- Ariato.Menubar.prototype.focusNextItem = function(item) {
421
- var nextItem = this.findNextItem(item)
422
- nextItem && nextItem.focus()
423
- }
424
-
425
- Ariato.Menubar.prototype.focusPreviousItem = function(item) {
426
- var previousItem = this.findPreviousItem(item)
427
- previousItem && previousItem.focus()
428
- }
429
-
430
- Ariato.Menubar.prototype.findParentMenu = function(item) {
431
- var parent = item.parentElement
432
- var menuRoles = ["menu", "menubar"]
433
- while (parent && !menuRoles.includes(parent.getAttribute("role")))
434
- parent = parent.parentElement
435
- return parent
436
- }
437
-
438
- Ariato.Menubar.prototype.findNextItem = function(item) {
439
- var menu = this.findParentMenu(item)
440
- var items = menu.querySelectorAll("[role=menuitem]")
441
- for (var i = 0; i < items.length; i++)
442
- if (items[i] == item)
443
- return items[i+1]
444
- }
445
-
446
- Ariato.Menubar.prototype.findPreviousItem = function(item) {
447
- var menu = this.findParentMenu(item)
448
- var items = menu.querySelectorAll("[role=menuitem]")
449
- for (var i = 0; i < items.length; i++)
450
- if (items[i] == item)
451
- return items[i-1]
452
- }
453
-
454
- Ariato.Menubar.prototype.findNextMenu = function(item) {
455
- var menus = this.rootMenus()
456
- for (var i = 0; i < menus.length; i++)
457
- if (menus[i].contains(item))
458
- return menus[i+1]
459
-
460
- var parent = item.parentElement
461
- for (var i = 0; i < menus.length; i++)
462
- if (parent.contains(menus[i]))
463
- return menus[i+1]
464
- }
465
-
466
- Ariato.Menubar.prototype.findPreviousMenu = function(item) {
467
- var menus = this.rootMenus()
468
- for (var i = 0; i < menus.length; i++)
469
- if (menus[i].contains(item))
470
- return menus[i-1]
471
-
472
- var parent = item.parentElement
473
- for (var i = 0; i < menus.length; i++)
474
- if (parent.contains(menus[i]))
475
- return menus[i-1]
476
- }
477
-
478
- Ariato.Menubar.prototype.rootMenus = function() {
479
- return this.node.querySelectorAll("li > [role=menu]")
480
- }
481
-
482
218
  Ariato.MenuButton = function(node) {
483
219
  this.node = this.button = node
484
220
  this.menu = document.getElementById(this.button.getAttribute("aria-controls"))
@@ -583,164 +319,4 @@ Ariato.Menu = function(node) {
583
319
 
584
320
  Ariato.Menu.prototype.labelledBy = function() {
585
321
  return document.getElementById(this.node.getAttribute("aria-labelledby"))
586
- }
587
-
588
- /*
589
- * A region is a role="region" element which represents a panel of an accordion.
590
- * It is controlled by a button.
591
- */
592
- Ariato.Region = function(node) {
593
- this.node = node
594
- var labelledBy = this.labelledBy()
595
-
596
- if (!labelledBy)
597
- return
598
-
599
- this.accordion = Ariato.Accordion.addRegion(this)
600
- labelledBy.addEventListener("click", this.buttonClicked.bind(this))
601
- }
602
-
603
- Ariato.Region.prototype.labelledBy = function() {
604
- return document.getElementById(this.node.getAttribute("aria-labelledby"))
605
- }
606
-
607
- Ariato.Region.prototype.buttonClicked = function(event) {
608
- this.accordion.showRegion(this)
609
- }
610
-
611
- Ariato.Region.prototype.show = function(event) {
612
- this.labelledBy().setAttribute("aria-expanded", true)
613
- this.node.removeAttribute("hidden")
614
- }
615
-
616
- Ariato.Region.prototype.hide = function(event) {
617
- this.labelledBy().setAttribute("aria-expanded", false)
618
- this.node.setAttribute("hidden", "")
619
- }
620
-
621
- Ariato.Region.prototype.expanded = function() {
622
- return !this.node.hasAttribute("hidden")
623
- }
624
-
625
- Ariato.Tablist = function(node) {
626
- this.node = node
627
- var tabs = this.tabs()
628
- for (var i = 0; i < tabs.length; i++) {
629
- tabs[i].addEventListener("click", this.click.bind(this))
630
- tabs[i].addEventListener("keydown", this.keydown.bind(this))
631
- tabs[i].addEventListener("keyup", this.keyup.bind(this))
632
- }
633
- tabs[0] && this.showTab(tabs[0])
634
- }
635
-
636
- Ariato.Tablist.prototype.click = function(event) {
637
- this.showTab(event.currentTarget)
638
- }
639
-
640
- Ariato.Tablist.prototype.tabs = function() {
641
- return this.node.querySelectorAll("[role=tab]")
642
- }
643
-
644
- Ariato.Tablist.prototype.activeTab = function() {
645
- return this.node.querySelector("[aria-selected=true]")
646
- }
647
-
648
- Ariato.Tablist.prototype.panels = function() {
649
- var tabs = this.tabs(), result = []
650
- for (var i = 0; i < tabs.length; i++)
651
- result.push(document.getElementById(tabs[i].getAttribute("aria-controls")))
652
- return result
653
- }
654
-
655
- Ariato.Tablist.prototype.showTab = function(tab) {
656
- this.hidePanels()
657
- tab.removeAttribute("tabindex")
658
- tab.setAttribute("aria-selected", "true")
659
- document.getElementById(tab.getAttribute("aria-controls")).style.display = null
660
- tab.focus()
661
- }
662
-
663
- Ariato.Tablist.prototype.hidePanels = function() {
664
- var tabs = this.tabs()
665
- for (var i = 0; i < tabs.length; i++) {
666
- tabs[i].setAttribute("tabindex", "-1");
667
- tabs[i].setAttribute("aria-selected", "false");
668
- }
669
-
670
- var panels = this.panels()
671
- for (var i = 0; i < panels.length; i++)
672
- panels[i].style.display = "none"
673
- }
674
-
675
- Ariato.Tablist.prototype.keydown = function(event) {
676
- switch (event.key) {
677
- case "End":
678
- var tabs = this.tabs()
679
- event.preventDefault()
680
- this.showTab(this.tabs()[tabs.length - 1])
681
- break
682
- case "Home":
683
- event.preventDefault()
684
- this.showTab(this.tabs()[0])
685
- break
686
- case "ArrowUp":
687
- event.preventDefault()
688
- this.showPrevious()
689
- break
690
- case "ArrowDown":
691
- event.preventDefault()
692
- this.showNext()
693
- break
694
- }
695
- }
696
-
697
- Ariato.Tablist.prototype.keyup = function(event) {
698
- if (event.key == "ArrowLeft")
699
- this.showPrevious()
700
- else if (event.key == "ArrowRight")
701
- this.showNext(event)
702
- // TODO delete
703
- }
704
-
705
- Ariato.Tablist.prototype.showNext = function() {
706
- var tabs = this.tabs()
707
- var index = Array.prototype.indexOf.call(tabs, this.activeTab())
708
- tabs[index + 1] && this.showTab(tabs[index + 1])
709
- }
710
-
711
- Ariato.Tablist.prototype.showPrevious = function() {
712
- var tabs = this.tabs()
713
- var index = Array.prototype.indexOf.call(tabs, this.activeTab())
714
- tabs[index - 1] && this.showTab(tabs[index - 1])
715
- }
716
-
717
- Ariato.ThemeSwitcher = function() {
718
- Ariato.ThemeSwitcher.initialize()
719
- this.node.addEventListener("click", this.change.bind(this))
720
- }
721
-
722
- Ariato.ThemeSwitcher.initialize = function() {
723
- if (!this.initialized) {
724
- console.log("initialize")
725
- this.initialized = true
726
- this.update()
727
- }
728
- }
729
-
730
- Ariato.ThemeSwitcher.update = function() {
731
- var name = localStorage.getItem("ariato-theme")
732
- document.documentElement.classList.forEach(function(theme) {
733
- theme.startsWith("theme-") && document.documentElement.classList.remove(theme)
734
- })
735
- document.documentElement.classList.add("theme-" + name)
736
-
737
- var buttons = document.querySelectorAll("[data-ariato='ThemeSwitcher']")
738
- for (var i = 0; i < buttons.length; i++)
739
- buttons[i].setAttribute("aria-pressed", buttons[i].getAttribute("data-theme") == name)
740
- }
741
-
742
- Ariato.ThemeSwitcher.prototype.change = function(event) {
743
- var name = event.currentTarget.getAttribute("data-theme")
744
- localStorage.setItem("ariato-theme", name)
745
- name && Ariato.ThemeSwitcher.update(name)
746
322
  }