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
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'gaq/dsl_target_factory'
|
2
|
+
|
3
|
+
require 'gaq/class_cache'
|
4
|
+
|
5
|
+
module Gaq
|
6
|
+
describe DslTargetFactory do
|
7
|
+
let(:flash_commands_adapter) { Object.new }
|
8
|
+
let(:immediate_commands) { Object.new }
|
9
|
+
let(:new_command_proc) { Object.new }
|
10
|
+
let(:variables) { Object.new }
|
11
|
+
let(:tracker_names) { ["tracker name", "foo"] }
|
12
|
+
|
13
|
+
let(:class_cache) do
|
14
|
+
ClassCache.new
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:target_base_class) do
|
18
|
+
Class.new do
|
19
|
+
attr_reader :target_factory, :factory_token, :new_command_proc, :commands, :tracker_name
|
20
|
+
|
21
|
+
def initialize(target_factory, factory_token, new_command_proc, commands, tracker_name)
|
22
|
+
@target_factory, @factory_token, @new_command_proc, @commands, @tracker_name =
|
23
|
+
target_factory, factory_token, new_command_proc, commands, tracker_name
|
24
|
+
end
|
25
|
+
|
26
|
+
class << self
|
27
|
+
def builds_variable_commands_module(&block)
|
28
|
+
@block = block
|
29
|
+
end
|
30
|
+
|
31
|
+
def variable_commands_module(*args)
|
32
|
+
@block.call(*args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#default implementation for examples not concerned about variables
|
37
|
+
self.builds_variable_commands_module { Module.new }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
subject do
|
42
|
+
result = described_class.new(class_cache, flash_commands_adapter, immediate_commands, new_command_proc, variables, tracker_names)
|
43
|
+
result.target_base_class = target_base_class
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "targets" do
|
48
|
+
shared_context "common initalization of target" do
|
49
|
+
it "passes itself as target factory" do
|
50
|
+
target.target_factory.should be subject
|
51
|
+
end
|
52
|
+
|
53
|
+
it "passes the new_command_proc through" do
|
54
|
+
target.new_command_proc.should be new_command_proc
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
shared_context immediate_commands: true do
|
59
|
+
it "passes immediate_commands as commands" do
|
60
|
+
target.commands.should be immediate_commands
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
shared_context flash_commands: true do
|
65
|
+
it "passes flash_commands_adapter as commands" do
|
66
|
+
target.commands.should be flash_commands_adapter
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
shared_context default_tracker: true do
|
71
|
+
it "passes nil as the tracker name" do
|
72
|
+
target.tracker_name.should be_nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
shared_context custom_tracker: true do
|
77
|
+
it "passes nil as the tracker name" do
|
78
|
+
target.tracker_name.should be == "tracker name"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#root_target", immediate_commands: true, default_tracker: true do
|
83
|
+
let(:target) { subject.root_target }
|
84
|
+
|
85
|
+
include_context "common initalization of target"
|
86
|
+
end
|
87
|
+
|
88
|
+
let(:root_token) do
|
89
|
+
subject.root_target.factory_token
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#target_with_tracker_name (unregistered name)" do
|
93
|
+
it "complains about the unknown name" do
|
94
|
+
expect {
|
95
|
+
subject.target_with_tracker_name("bogus", root_token)
|
96
|
+
}.to raise_exception
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#target_with_tracker_name" do
|
101
|
+
it "tolerates both strings and symbols as tracker names" do
|
102
|
+
subject.target_with_tracker_name("tracker name", root_token).tracker_name.should be ==
|
103
|
+
subject.target_with_tracker_name(:"tracker name", root_token).tracker_name
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#target_with_tracker_name (token is root token)", immediate_commands: true, custom_tracker: true do
|
108
|
+
let(:target) { subject.target_with_tracker_name("tracker name", root_token) }
|
109
|
+
|
110
|
+
include_context "common initalization of target"
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#target_for_next_request (token is root token)", flash_commands: true, default_tracker: true do
|
114
|
+
let(:target) { subject.target_for_next_request(root_token) }
|
115
|
+
|
116
|
+
include_context "common initalization of target"
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#target_with_tracker_name (token is from #target_for_next_request)", flash_commands: true, custom_tracker: true do
|
120
|
+
let(:target) do
|
121
|
+
token = subject.target_for_next_request(root_token).factory_token
|
122
|
+
subject.target_with_tracker_name("tracker name", token)
|
123
|
+
end
|
124
|
+
|
125
|
+
include_context "common initalization of target"
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#target_with_tracker_name (token is from #target_with_tracker_name)", custom_tracker: true, immediate_commands: true do
|
129
|
+
let(:target) do
|
130
|
+
token = subject.target_with_tracker_name("foo", root_token).factory_token
|
131
|
+
subject.target_with_tracker_name("tracker name", token)
|
132
|
+
end
|
133
|
+
|
134
|
+
include_context "common initalization of target"
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "#target_for_next_request (token is from #target_with_tracker_name)", custom_tracker: true, flash_commands: true do
|
138
|
+
let(:target) do
|
139
|
+
token = subject.target_with_tracker_name("tracker name", root_token).factory_token
|
140
|
+
subject.target_for_next_request(token)
|
141
|
+
end
|
142
|
+
|
143
|
+
include_context "common initalization of target"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "variables" do
|
148
|
+
let(:variables_module) { Module.new }
|
149
|
+
|
150
|
+
before do
|
151
|
+
target_base_class.builds_variable_commands_module do |*args|
|
152
|
+
args.should have(1).argument
|
153
|
+
args.first.should be variables
|
154
|
+
variables_module
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
it "includes the variables module into the target's class" do
|
159
|
+
subject.root_target.should be_a_kind_of(variables_module)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'gaq/dsl_target'
|
2
|
+
|
3
|
+
require 'gaq/configuration'
|
4
|
+
require 'gaq/command_language'
|
5
|
+
|
6
|
+
module Gaq
|
7
|
+
describe DslTarget do
|
8
|
+
let(:target_factory) { double "target factory" }
|
9
|
+
let(:factory_token) { Object.new }
|
10
|
+
let(:new_command_proc) { double "new_command_proc" }
|
11
|
+
let(:commands) { double "commands" }
|
12
|
+
let(:tracker_name) { "custom tracker name" }
|
13
|
+
|
14
|
+
shared_context with_instance_subject: true do
|
15
|
+
subject do
|
16
|
+
described_class.new(target_factory, factory_token, new_command_proc, commands, tracker_name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def command_expecting_tracker_name
|
21
|
+
result = double
|
22
|
+
result.should_receive(:tracker_name=).with(tracker_name)
|
23
|
+
commands.should_receive(:<<).with(result).once
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#tracker", with_instance_subject: true do
|
28
|
+
it "delegates to target factory, passing token" do
|
29
|
+
tracker_name = "tracker name"
|
30
|
+
|
31
|
+
target_factory.should_receive(:target_with_tracker_name)
|
32
|
+
.with(tracker_name, factory_token)
|
33
|
+
.and_return(5)
|
34
|
+
|
35
|
+
subject.tracker(tracker_name).should be 5
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#next_request", with_instance_subject: true do
|
40
|
+
it "delegates to target factory, passing token" do
|
41
|
+
target_factory.should_receive(:target_for_next_request)
|
42
|
+
.with(factory_token)
|
43
|
+
.and_return(5)
|
44
|
+
|
45
|
+
subject.next_request.should be 5
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#track_event", with_instance_subject: true do
|
50
|
+
it "creates and pushes a new command" do
|
51
|
+
command = command_expecting_tracker_name
|
52
|
+
|
53
|
+
new_command_proc.should_receive(:call)
|
54
|
+
.with(:track_event, "3", "5")
|
55
|
+
.and_return(command)
|
56
|
+
|
57
|
+
subject.track_event("3", "5")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe ".variable_commands_module" do
|
62
|
+
let(:variable) do
|
63
|
+
result = Configuration::Variable.new(4, "myvar", 2)
|
64
|
+
result.slot.should be == 4
|
65
|
+
result.name.should be == "myvar"
|
66
|
+
result.scope.should be == 2
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
subject do
|
71
|
+
cls = Class.new(described_class)
|
72
|
+
cls.send :include, described_class.variable_commands_module([variable])
|
73
|
+
cls.new(target_factory, factory_token, new_command_proc, commands, tracker_name)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "creates a variable setter that pushes a set_custom_var command" do
|
77
|
+
command = command_expecting_tracker_name
|
78
|
+
|
79
|
+
new_command_proc.should_receive(:call)
|
80
|
+
.with(:set_custom_var, 4, "myvar", "value", 2)
|
81
|
+
.and_return(command)
|
82
|
+
|
83
|
+
subject.myvar = "value"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'gaq/flash_commands_adapter'
|
2
|
+
|
3
|
+
module Gaq
|
4
|
+
describe FlashCommandsAdapter do
|
5
|
+
let(:flash) { {} }
|
6
|
+
|
7
|
+
let(:language) do
|
8
|
+
result = double "language"
|
9
|
+
if example.metadata[:null_conversion]
|
10
|
+
result.stub(:commands_to_flash_items) { |commands| commands }
|
11
|
+
result.stub(:commands_from_flash_items) { |commands| commands }
|
12
|
+
end
|
13
|
+
result
|
14
|
+
end
|
15
|
+
let(:controller_facade) do
|
16
|
+
result = double "controller facade"
|
17
|
+
result.stub(:flash) { flash }
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
subject do
|
22
|
+
described_class.new(language, controller_facade)
|
23
|
+
end
|
24
|
+
|
25
|
+
def commands_as_seen_in_flash
|
26
|
+
flash[described_class::FLASH_KEY].drop(1)
|
27
|
+
end
|
28
|
+
|
29
|
+
def version_as_seen_in_flash
|
30
|
+
flash[described_class::FLASH_KEY].first
|
31
|
+
end
|
32
|
+
|
33
|
+
def version_as_coded
|
34
|
+
described_class::FLASH_FORMAT_VERSION
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#<<" do
|
38
|
+
it "saves to flash with pushed command", null_conversion: true do
|
39
|
+
subject << 5
|
40
|
+
commands_as_seen_in_flash.should be == [5]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "saves to flash with pushed commands", null_conversion: true do
|
44
|
+
subject << 5
|
45
|
+
subject << 17
|
46
|
+
commands_as_seen_in_flash.should be == [5, 17]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "converts items using language.commands_to_flash_items" do
|
50
|
+
language.should_receive(:commands_to_flash_items).with([5]).and_return([:bogus])
|
51
|
+
language.should_receive(:commands_to_flash_items).with([5, 17]).and_return([:result_array])
|
52
|
+
|
53
|
+
subject << 5
|
54
|
+
subject << 17
|
55
|
+
|
56
|
+
commands_as_seen_in_flash.should be == [:result_array]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "saves to flash including current format version", null_conversion: true do
|
60
|
+
subject << 5
|
61
|
+
|
62
|
+
version_as_seen_in_flash.should be == version_as_coded
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#commands_from_flash" do
|
67
|
+
context "with empty flash" do
|
68
|
+
it "returns something empty", null_conversion: true do
|
69
|
+
subject.commands_from_flash.should be_empty
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "with current data in flash" do
|
74
|
+
before do
|
75
|
+
flash[described_class::FLASH_KEY] = [
|
76
|
+
described_class::FLASH_FORMAT_VERSION,
|
77
|
+
2,
|
78
|
+
3
|
79
|
+
]
|
80
|
+
end
|
81
|
+
|
82
|
+
it "passes flash items through language.commands_from_flash_items and returns result" do
|
83
|
+
language.should_receive(:commands_from_flash_items)
|
84
|
+
.with([2, 3]).and_return([:conversion, :result])
|
85
|
+
subject.commands_from_flash.should be == [:conversion, :result]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "with stale data in flash" do
|
90
|
+
before do
|
91
|
+
flash[described_class::FLASH_KEY] = [
|
92
|
+
described_class::FLASH_FORMAT_VERSION - 1,
|
93
|
+
2,
|
94
|
+
3
|
95
|
+
]
|
96
|
+
end
|
97
|
+
|
98
|
+
it "returns something empty", null_conversion: true do
|
99
|
+
subject.commands_from_flash.should be_empty
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "with ancient data in flash" do
|
104
|
+
before do
|
105
|
+
flash[described_class::FLASH_KEY] = [
|
106
|
+
["_trackEvent", "foo", "bar", "baz"]
|
107
|
+
]
|
108
|
+
end
|
109
|
+
|
110
|
+
it "returns something empty", null_conversion: true do
|
111
|
+
subject.commands_from_flash.should be_empty
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'gaq/interprets_config'
|
2
|
+
require 'gaq/controller_facade'
|
3
|
+
|
4
|
+
require 'active_support/string_inquirer'
|
5
|
+
|
6
|
+
module Gaq
|
7
|
+
describe InterpretsConfig do
|
8
|
+
subject do
|
9
|
+
Class.new do
|
10
|
+
include InterpretsConfig
|
11
|
+
|
12
|
+
public :interpret_config #make it accessible to tests
|
13
|
+
end.new
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:controller) { Object.new }
|
17
|
+
|
18
|
+
let(:controller_facade) do
|
19
|
+
ControllerFacade.new(controller)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "passes strings and numbers right through" do
|
23
|
+
subject.interpret_config(3, controller_facade).should be 3
|
24
|
+
subject.interpret_config("foo", controller_facade).should be == "foo"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "uses controller_facade.evaluate_config_lambda for result when value is callable" do
|
28
|
+
value = double
|
29
|
+
value.stub(:call) do |arg|
|
30
|
+
arg.should be controller
|
31
|
+
:foo
|
32
|
+
end
|
33
|
+
|
34
|
+
subject.interpret_config(value, controller_facade).should be :foo
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'gaq/snippet_renderer'
|
2
|
+
|
3
|
+
module Gaq
|
4
|
+
describe SnippetRenderer do
|
5
|
+
let(:rendering_context) do
|
6
|
+
double("rendering context").tap do |context|
|
7
|
+
# no assertion?? XXX
|
8
|
+
context.stub(:javascript_tag) { |content| content }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:config) do
|
13
|
+
double "config"
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:rails_env) do
|
17
|
+
double "rails env"
|
18
|
+
end
|
19
|
+
|
20
|
+
subject do
|
21
|
+
described_class.new(rendering_context, config, rails_env)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#render" do
|
25
|
+
def pushed_arrays(rendered)
|
26
|
+
rendered = rendered.lines.drop_while { |line| !/^\s*_gaq.push/.match(line) }
|
27
|
+
rendered = rendered.take_while { |line| !line.empty? }
|
28
|
+
|
29
|
+
rendered = rendered.join('')[/\A\_gaq.push\((.*)\);\Z/m, 1]
|
30
|
+
rendered = rendered.split(/,\n\s*/)
|
31
|
+
rendered.map { |string| JSON.parse(string) }
|
32
|
+
end
|
33
|
+
|
34
|
+
it "renders commands segments" do
|
35
|
+
config.stub(:render_ga_js?) { false }
|
36
|
+
|
37
|
+
commands_as_segments = [
|
38
|
+
["foo", "bar"],
|
39
|
+
["baz", true, 3]
|
40
|
+
]
|
41
|
+
rendered = subject.render(commands_as_segments)
|
42
|
+
|
43
|
+
pushed_arrays(rendered).should be == commands_as_segments
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "snippet rendering" do
|
47
|
+
it "renders the snippet when config.render_ga_js? returns true" do
|
48
|
+
config.stub(:render_ga_js?) { true }
|
49
|
+
subject.render([]).should include('google-analytics.com')
|
50
|
+
end
|
51
|
+
|
52
|
+
it "does not render the snippet when config.render_ga_js? returns false" do
|
53
|
+
config.stub(:render_ga_js?) { false }
|
54
|
+
subject.render([]).should_not include('google-analytics.com')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|