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
@@ -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
|