gaq 0.2.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.
- data/.gitignore +18 -0
- data/Gemfile +15 -0
- data/Guardfile +23 -0
- data/LICENSE.txt +22 -0
- data/README.md +102 -0
- data/Rakefile +1 -0
- data/gaq.gemspec +22 -0
- data/lib/gaq.rb +3 -0
- data/lib/gaq/class_cache.rb +32 -0
- data/lib/gaq/command_language.rb +145 -0
- data/lib/gaq/configuration.rb +159 -0
- data/lib/gaq/controller_facade.rb +15 -0
- data/lib/gaq/controller_handle.rb +93 -0
- data/lib/gaq/dsl_target.rb +51 -0
- data/lib/gaq/dsl_target_factory.rb +64 -0
- data/lib/gaq/flash_commands_adapter.rb +48 -0
- data/lib/gaq/interprets_config.rb +13 -0
- data/lib/gaq/railtie.rb +40 -0
- data/lib/gaq/snippet_renderer.rb +44 -0
- data/lib/gaq/version.rb +3 -0
- data/spec-dummy/.rspec +1 -0
- data/spec-dummy/README.rdoc +261 -0
- data/spec-dummy/Rakefile +7 -0
- data/spec-dummy/app/assets/images/rails.png +0 -0
- data/spec-dummy/app/assets/javascripts/application.js +13 -0
- data/spec-dummy/app/assets/stylesheets/application.css +13 -0
- data/spec-dummy/app/controllers/application_controller.rb +3 -0
- data/spec-dummy/app/controllers/integration_spec_controller.rb +22 -0
- data/spec-dummy/app/helpers/application_helper.rb +2 -0
- data/spec-dummy/app/views/integration_spec/view.erb +0 -0
- data/spec-dummy/app/views/layouts/application.html.erb +16 -0
- data/spec-dummy/config.ru +4 -0
- data/spec-dummy/config/application.rb +64 -0
- data/spec-dummy/config/boot.rb +6 -0
- data/spec-dummy/config/environment.rb +5 -0
- data/spec-dummy/config/environments/development.rb +26 -0
- data/spec-dummy/config/environments/production.rb +51 -0
- data/spec-dummy/config/environments/test_dynamic.rb +39 -0
- data/spec-dummy/config/environments/test_static.rb +38 -0
- data/spec-dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec-dummy/config/initializers/inflections.rb +15 -0
- data/spec-dummy/config/initializers/mime_types.rb +5 -0
- data/spec-dummy/config/initializers/secret_token.rb +7 -0
- data/spec-dummy/config/initializers/session_store.rb +8 -0
- data/spec-dummy/config/initializers/wrap_parameters.rb +10 -0
- data/spec-dummy/config/locales/en.yml +5 -0
- data/spec-dummy/config/routes.rb +8 -0
- data/spec-dummy/db/seeds.rb +7 -0
- data/spec-dummy/public/404.html +26 -0
- data/spec-dummy/public/422.html +26 -0
- data/spec-dummy/public/500.html +25 -0
- data/spec-dummy/public/favicon.ico +0 -0
- data/spec-dummy/public/robots.txt +5 -0
- data/spec-dummy/script/rails +6 -0
- data/spec-dummy/spec/common_spec_methods.rb +88 -0
- data/spec-dummy/spec/features/dynamic_config_spec.rb +21 -0
- data/spec-dummy/spec/features/next_request_spec.rb +27 -0
- data/spec-dummy/spec/features/presence_spec.rb +64 -0
- data/spec-dummy/spec/spec_helper.rb +41 -0
- data/spec/lib/gaq/class_cache_spec.rb +62 -0
- data/spec/lib/gaq/command_language_spec.rb +267 -0
- data/spec/lib/gaq/configuration_spec.rb +233 -0
- data/spec/lib/gaq/controller_facade_spec.rb +29 -0
- data/spec/lib/gaq/controller_handle_spec.rb +510 -0
- data/spec/lib/gaq/dsl_target_factory_spec.rb +163 -0
- data/spec/lib/gaq/dsl_target_spec.rb +87 -0
- data/spec/lib/gaq/flash_commands_adapter_spec.rb +116 -0
- data/spec/lib/gaq/interprets_config_spec.rb +37 -0
- data/spec/lib/gaq/snippet_renderer_spec.rb +60 -0
- metadata +159 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in gaq.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'rails', '~> 3.1.0'
|
7
|
+
gem 'rspec-rails', '~> 2.13.2'
|
8
|
+
|
9
|
+
gem 'capybara', '~> 2.0.0'
|
10
|
+
gem 'terminal-notifier-guard'
|
11
|
+
gem 'rb-fsevent' # used by guard for watching
|
12
|
+
gem 'guard-rspec'
|
13
|
+
gem 'debugger'
|
14
|
+
|
15
|
+
gem 'nokogiri'
|
data/Guardfile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
binstubs = nil
|
2
|
+
|
3
|
+
bundle_config_path = Pathname.new(__FILE__) + '../.bundle/config'
|
4
|
+
if File.exist?(bundle_config_path)
|
5
|
+
yaml = File.read(bundle_config_path)
|
6
|
+
binstubs = YAML.load(yaml)["BUNDLE_BIN"]
|
7
|
+
end
|
8
|
+
|
9
|
+
guard 'rspec', binstubs: binstubs do
|
10
|
+
watch(%r{^spec/.+_spec\.rb$})
|
11
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
12
|
+
watch('spec/spec_helper.rb') { "spec" }
|
13
|
+
end
|
14
|
+
|
15
|
+
guard 'rspec', :spec_paths => "spec-dummy/spec", cli: "-I spec-dummy/spec --tag ~dynamic",
|
16
|
+
binstubs: binstubs,
|
17
|
+
env: { 'RAILS_ENV' => 'test_static' } do
|
18
|
+
end
|
19
|
+
|
20
|
+
guard 'rspec', :spec_paths => "spec-dummy/spec", cli: "-I spec-dummy/spec --tag ~static",
|
21
|
+
binstubs: binstubs,
|
22
|
+
env: { 'RAILS_ENV' => 'test_dynamic' } do
|
23
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Thomas Stratmann
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Gaq
|
2
|
+
|
3
|
+
Ever wanted to push a track event from a controller? Set a custom variable from model data? Now you can.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'gaq'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install gaq
|
18
|
+
|
19
|
+
## Setup
|
20
|
+
|
21
|
+
1. Require `gaq` from your `application.rb` and configure your web_property_id like this:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class MyApplication < Rails::Application
|
25
|
+
config.gaq.web_property_id = 'UA-XXYOURID-1'
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
2. Put this in your application layout:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
<%= render_gaq %>
|
33
|
+
```
|
34
|
+
|
35
|
+
This inserts javascript code for initializing _gaq, your tracking events and the ga.js snippet.
|
36
|
+
The ga.js snippet will only be rendered in the production environment, so no real tracking happens
|
37
|
+
during development.
|
38
|
+
|
39
|
+
3. DONE!
|
40
|
+
|
41
|
+
### More setup
|
42
|
+
|
43
|
+
If you want to use custom variables, configure them like this:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
config.gaq.declare_variable :user_gender, scope: :session, slot: 1
|
47
|
+
```
|
48
|
+
|
49
|
+
If you need the _anonymizeIp feature, enable it like this:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
config.gaq.anonymize_ip = true
|
53
|
+
```
|
54
|
+
|
55
|
+
## Usage in the controller
|
56
|
+
|
57
|
+
For inserting a track event to be rendered on the current request, do
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
gaq.track_event 'category', 'action', 'label'
|
61
|
+
```
|
62
|
+
|
63
|
+
If you have configured a custom variable like above, do this to set it:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
gaq.user_gender = 'female'
|
67
|
+
```
|
68
|
+
|
69
|
+
If you need to do any of these before a redirect, use these methods on `gaq.next_request`
|
70
|
+
instead of `gaq`:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
gaq.next_request.track_event 'category', 'action', 'label'
|
74
|
+
```
|
75
|
+
|
76
|
+
This feature uses the flash for storing _gaq items between requests.
|
77
|
+
|
78
|
+
## Supported tracker commands
|
79
|
+
|
80
|
+
Currently, only _trackEvent and _setCustomVar is supported. However commands are easily added, so open a pull request!
|
81
|
+
|
82
|
+
## Contributing
|
83
|
+
|
84
|
+
1. Fork it
|
85
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
86
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
87
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
88
|
+
5. Create new Pull Request
|
89
|
+
|
90
|
+
### Testing
|
91
|
+
|
92
|
+
Test dependencies are declared in the Gemfile.
|
93
|
+
All specs are run from guard.
|
94
|
+
|
95
|
+
The most interesting part is the controller_handle_spec, it asserts what commands get rendered under which circumstances.
|
96
|
+
|
97
|
+
There is a dummy rails application
|
98
|
+
in `spec-dummy` for integration tests, which we need because gaq keeps state in
|
99
|
+
the session. The integration specs are located inside of it.
|
100
|
+
|
101
|
+
It has two test environments, `test_static` and `test_dynamic`. Specs tagged with
|
102
|
+
`:static` will not be run under `test_dynamic` and vice versa. The dynamic tests are for dynamic configuration items, which is an upcoming feature that lets you configure things dynamically in the context of the running controller.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/gaq.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gaq/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "gaq"
|
8
|
+
gem.version = Gaq::VERSION
|
9
|
+
gem.authors = ["Thomas Stratmann"]
|
10
|
+
gem.email = ["thomas.stratmann@9elements.com"]
|
11
|
+
gem.description = %q{Gaq is a lightweight gem for support of pushing static and dynamic data to the _gaq from the backend.}
|
12
|
+
gem.summary = %q{Renders _gaq initialization and the ga.js snippet. Supports pushing from the back end}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'actionpack'
|
21
|
+
gem.add_dependency 'activesupport'
|
22
|
+
end
|
data/lib/gaq.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Gaq
|
2
|
+
class ClassCache
|
3
|
+
Miss = Class.new(RuntimeError)
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@cached = Hash.new { |hash, key| hash[key] = build(key) }
|
7
|
+
@build_instructions = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def building(key, base_class, &block)
|
11
|
+
@build_instructions[key] = [base_class, block]
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
@cached[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.singleton
|
20
|
+
@singleton ||= new
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def build(key)
|
26
|
+
raise Miss, "Nothing registered for key #{key.inspect}" unless @build_instructions.key?(key)
|
27
|
+
base_class, block = @build_instructions[key]
|
28
|
+
|
29
|
+
Class.new(base_class).tap(&block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Gaq
|
2
|
+
class CommandLanguage
|
3
|
+
attr_writer :value_coercer
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@descriptors = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
CommandDescriptor = Struct.new(:signature, :name, :identifier, :sort_slot)
|
10
|
+
|
11
|
+
def knows_command(identifier)
|
12
|
+
@descriptors[identifier] = CommandDescriptor.new.tap do |desc|
|
13
|
+
desc.identifier = identifier
|
14
|
+
yield desc
|
15
|
+
end
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def commands_to_flash_items(commands)
|
20
|
+
commands.map do |command|
|
21
|
+
command_to_segments(command)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# this happens to be the same, but may be different in the future
|
26
|
+
alias_method :commands_to_segments_for_to_json, :commands_to_flash_items
|
27
|
+
|
28
|
+
def commands_from_flash_items(flash_items)
|
29
|
+
flash_items.map do |flash_item|
|
30
|
+
descriptor, tracker_name = descriptor_and_tracker_name_from_first_segment(flash_item.first)
|
31
|
+
params = flash_item.drop(1).take(descriptor.signature.length)
|
32
|
+
Command.new(descriptor, descriptor.name, params, tracker_name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# modifies commands
|
37
|
+
def sort_commands(commands)
|
38
|
+
sorted_pairs = commands.each_with_index.sort_by do |command, index|
|
39
|
+
[
|
40
|
+
command.descriptor.sort_slot || sort_slot_fallback,
|
41
|
+
index
|
42
|
+
]
|
43
|
+
end
|
44
|
+
commands.replace sorted_pairs.map(&:first)
|
45
|
+
end
|
46
|
+
|
47
|
+
def new_command(identifier, *params)
|
48
|
+
descriptor = @descriptors.fetch(identifier) { raise "no command with identifier #{identifier.inspect}" }
|
49
|
+
params = coerce_params(params, descriptor.signature)
|
50
|
+
|
51
|
+
Command.new(descriptor, descriptor.name, params)
|
52
|
+
end
|
53
|
+
|
54
|
+
Command = Struct.new(:descriptor, :name, :params, :tracker_name)
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def sort_slot_fallback
|
59
|
+
@sort_slot_fallback ||= @descriptors.values.map(&:sort_slot).compact.max + 1
|
60
|
+
end
|
61
|
+
|
62
|
+
def command_to_segments(command)
|
63
|
+
descriptor = command.descriptor
|
64
|
+
|
65
|
+
first_segment = first_segment_from_descriptor_and_tracker_name(descriptor, command.tracker_name)
|
66
|
+
[first_segment, *command.params.take(descriptor.signature.length)]
|
67
|
+
end
|
68
|
+
|
69
|
+
def first_segment_from_descriptor_and_tracker_name(descriptor, tracker_name)
|
70
|
+
[tracker_name, descriptor.name].compact.join('.')
|
71
|
+
end
|
72
|
+
|
73
|
+
def descriptor_and_tracker_name_from_first_segment(first_segment)
|
74
|
+
split = first_segment.split('.')
|
75
|
+
command_name, tracker_name = split.reverse
|
76
|
+
descriptor = @descriptors.values.find { |desc| desc.name == command_name }
|
77
|
+
[descriptor, tracker_name]
|
78
|
+
end
|
79
|
+
|
80
|
+
def coerce_params(params, signature)
|
81
|
+
signature = signature.take(params.length)
|
82
|
+
signature.zip(params).map do |type, param|
|
83
|
+
@value_coercer.call(type, param)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.declare_language_on(instance)
|
88
|
+
instance.knows_command(:set_account) do |desc|
|
89
|
+
desc.name = "_setAccount"
|
90
|
+
desc.signature = [:String]
|
91
|
+
desc.sort_slot = 0
|
92
|
+
end
|
93
|
+
|
94
|
+
instance.knows_command(:track_pageview) do |desc|
|
95
|
+
desc.name = "_trackPageview"
|
96
|
+
desc.signature = [:String]
|
97
|
+
desc.sort_slot = 2
|
98
|
+
end
|
99
|
+
|
100
|
+
instance.knows_command(:track_event) do |desc|
|
101
|
+
desc.name = "_trackEvent"
|
102
|
+
desc.signature = [:String, :String, :String, :Int, :Boolean]
|
103
|
+
end
|
104
|
+
|
105
|
+
instance.knows_command(:set_custom_var) do |desc|
|
106
|
+
desc.name = "_setCustomVar"
|
107
|
+
desc.signature = [:Int, :String, :String, :Int]
|
108
|
+
desc.sort_slot = 3
|
109
|
+
end
|
110
|
+
|
111
|
+
instance.knows_command(:anonymize_ip) do |desc|
|
112
|
+
desc.name = "_gat._anonymizeIp"
|
113
|
+
desc.signature = []
|
114
|
+
desc.sort_slot = 1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.define_transformations_on(instance)
|
119
|
+
instance.value_coercer = method(:coerce_value)
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.coerce_value(type, value)
|
123
|
+
case type
|
124
|
+
when :Boolean
|
125
|
+
!!value
|
126
|
+
when :String
|
127
|
+
String(value)
|
128
|
+
when :Int
|
129
|
+
Integer(value)
|
130
|
+
when :Number
|
131
|
+
raise "'Number' coercion not implemented"
|
132
|
+
# GA docs do not tell us what that means
|
133
|
+
else
|
134
|
+
raise "Unable to coerce unknown type #{type.inspect}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.singleton
|
139
|
+
new.tap do |result|
|
140
|
+
declare_language_on(result)
|
141
|
+
define_transformations_on(result)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'active_support/ordered_options'
|
3
|
+
|
4
|
+
module Gaq
|
5
|
+
class Configuration
|
6
|
+
attr_reader :rails_config, :variables
|
7
|
+
|
8
|
+
RAILS_CONFIG_ACCESSORS = [:anonymize_ip, :render_ga_js]
|
9
|
+
attr_accessor(*RAILS_CONFIG_ACCESSORS)
|
10
|
+
|
11
|
+
VariableException = Class.new(RuntimeError)
|
12
|
+
|
13
|
+
Variable = Struct.new(:slot, :name, :scope) do
|
14
|
+
SCOPE_MAP = {
|
15
|
+
nil => 3, #we allow for a default @TODO check documentation
|
16
|
+
|
17
|
+
:visitor => 1,
|
18
|
+
:session => 2,
|
19
|
+
:page => 3,
|
20
|
+
|
21
|
+
1 => 1,
|
22
|
+
2 => 2,
|
23
|
+
3 => 3
|
24
|
+
}
|
25
|
+
|
26
|
+
def initialize(slot, name, scope)
|
27
|
+
super(slot, name)
|
28
|
+
set_scope scope
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def set_scope(scope)
|
34
|
+
self.scope = SCOPE_MAP.fetch(scope) do
|
35
|
+
raise VariableException, "unknown scope #{scope.inspect}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize
|
41
|
+
default_tracker_config = TrackerConfig.new(nil)
|
42
|
+
@default_tracker_rails_config = default_tracker_config.rails_config
|
43
|
+
|
44
|
+
@tracker_configs = { nil => default_tracker_config }
|
45
|
+
@rails_config = RailsConfig.new(self, default_tracker_config)
|
46
|
+
@variables = {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def declare_variable(name, options = {})
|
50
|
+
# @TODO deprecate use without slot given
|
51
|
+
# We just code like it's always given here
|
52
|
+
slot = options[:slot]
|
53
|
+
# @TODO raise when slot off limits
|
54
|
+
raise VariableException, "Already have a variable at that slot" if
|
55
|
+
@variables.find { |_, var| var.slot == slot }
|
56
|
+
raise VariableException, "Already have a variable of that name" if
|
57
|
+
@variables.find { |_, var| var.name == name }
|
58
|
+
|
59
|
+
variable = Variable.new(slot, name, options[:scope])
|
60
|
+
@variables[name] = variable
|
61
|
+
end
|
62
|
+
|
63
|
+
def register_tracker_name(name)
|
64
|
+
name = name.to_s
|
65
|
+
# @TODO check for collision, assert name format
|
66
|
+
|
67
|
+
raise "duplicate tracker name" if @tracker_configs.key?(name)
|
68
|
+
@tracker_configs[name] = TrackerConfig.new(name)
|
69
|
+
end
|
70
|
+
|
71
|
+
def tracker_rails_config(name) # name can be nil -> default tracker
|
72
|
+
name = name.to_s unless name.nil?
|
73
|
+
tracker_config = @tracker_configs.fetch(name) { raise "No tracker by that name (#{name.inspect})" }
|
74
|
+
tracker_config.rails_config
|
75
|
+
end
|
76
|
+
|
77
|
+
def tracker_config(name)
|
78
|
+
@tracker_configs[name]
|
79
|
+
end
|
80
|
+
|
81
|
+
def tracker_names
|
82
|
+
@tracker_configs.keys
|
83
|
+
end
|
84
|
+
|
85
|
+
def render_ga_js?(environment)
|
86
|
+
environment = environment.to_s
|
87
|
+
|
88
|
+
case render_ga_js
|
89
|
+
when TrueClass, FalseClass
|
90
|
+
render_ga_js
|
91
|
+
when Array, Symbol
|
92
|
+
Array(render_ga_js).map(&:to_s).include? environment
|
93
|
+
else
|
94
|
+
render_ga_js
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class TrackerConfig
|
99
|
+
attr_reader :rails_config, :tracker_name
|
100
|
+
|
101
|
+
RAILS_CONFIG_ACCESSORS = [:web_property_id, :track_pageview]
|
102
|
+
attr_accessor(*RAILS_CONFIG_ACCESSORS)
|
103
|
+
alias_method :track_pageview?, :track_pageview
|
104
|
+
|
105
|
+
def initialize(tracker_name)
|
106
|
+
@tracker_name = tracker_name
|
107
|
+
|
108
|
+
@track_pageview = true
|
109
|
+
@rails_config = RailsConfig.new(self)
|
110
|
+
|
111
|
+
@web_property_id = 'UA-XUNSET-S'
|
112
|
+
end
|
113
|
+
|
114
|
+
class RailsConfig
|
115
|
+
extend Forwardable
|
116
|
+
def_delegators :@config, *RAILS_CONFIG_ACCESSORS.map { |m| "#{m}=" }
|
117
|
+
|
118
|
+
def initialize(config)
|
119
|
+
@config = config
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class RailsConfig
|
125
|
+
extend Forwardable
|
126
|
+
def_delegators :@config, :declare_variable,
|
127
|
+
*Configuration::RAILS_CONFIG_ACCESSORS.map { |m| "#{m}=" }
|
128
|
+
def_delegators :@default_tracker_rails_config,
|
129
|
+
*TrackerConfig::RAILS_CONFIG_ACCESSORS.map { |m| "#{m}=" }
|
130
|
+
|
131
|
+
def initialize(config, default_tracker_rails_config)
|
132
|
+
@config = config
|
133
|
+
|
134
|
+
@default_tracker_rails_config = default_tracker_rails_config
|
135
|
+
|
136
|
+
@anonymize_ip = false
|
137
|
+
@render_ga_js = :production
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
def additional_trackers=(array)
|
142
|
+
raise "you can only do this once" if @trackers_set
|
143
|
+
@trackers_set = true
|
144
|
+
|
145
|
+
array.each { |name| @config.register_tracker_name(name) }
|
146
|
+
end
|
147
|
+
|
148
|
+
def tracker(name)
|
149
|
+
@config.tracker_rails_config(name)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class << self
|
154
|
+
def singleton
|
155
|
+
@singleton ||= new
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|