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