active_analytics 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
  }