evva 0.1.4.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -2
- data/.rspec +0 -1
- data/.rubocop_todo.yml +10 -10
- data/.travis.yml +1 -0
- data/Gemfile +9 -8
- data/Gemfile.lock +7 -11
- data/README.md +10 -0
- data/Rakefile +9 -0
- data/changelog.md +19 -2
- data/evva.gemspec +0 -3
- data/evva_config.yml +7 -3
- data/lib/evva/{mixpanel_enum.rb → analytics_enum.rb} +1 -1
- data/lib/evva/{mixpanel_event.rb → analytics_event.rb} +1 -1
- data/lib/evva/android_generator.rb +88 -69
- data/lib/evva/config.rb +13 -2
- data/lib/evva/google_sheet.rb +49 -46
- data/lib/evva/swift_generator.rb +44 -33
- data/lib/evva/version.rb +2 -2
- data/lib/evva.rb +7 -9
- data/spec/evva_spec.rb +1 -1
- data/spec/fixtures/sample_public_enums.csv +3 -0
- data/spec/fixtures/sample_public_events.csv +4 -0
- data/spec/fixtures/sample_public_people_properties.csv +3 -0
- data/spec/fixtures/test.yml +7 -4
- data/spec/lib/evva/android_generator_spec.rb +125 -116
- data/spec/lib/evva/config_spec.rb +8 -4
- data/spec/lib/evva/google_sheet_spec.rb +44 -74
- data/spec/lib/evva/swift_generator_spec.rb +100 -69
- metadata +10 -40
- data/lib/evva/data_source.rb +0 -23
- data/spec/fixtures/sample_public_enums.html +0 -1
- data/spec/fixtures/sample_public_info.html +0 -1
- data/spec/fixtures/sample_public_people_properties.html +0 -1
- data/spec/fixtures/sample_public_sheet.html +0 -1
- data/spec/lib/evva/data_source_spec.rb +0 -28
data/lib/evva/swift_generator.rb
CHANGED
@@ -10,29 +10,26 @@ module Evva
|
|
10
10
|
NATIVE_TYPES = %w[Int String Double Float Bool].freeze
|
11
11
|
|
12
12
|
def events(bundle, file_name)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
header_footer_wrapper do
|
14
|
+
"""\tenum Event {
|
15
|
+
#{bundle.map { |e| event_case(e) }.join("\n")}
|
16
|
+
|
17
|
+
\t\tvar data: EventData {
|
18
|
+
\t\t\tswitch self {
|
19
|
+
#{bundle.map { |e| event_data(e) }.join("\n\n")}
|
20
|
+
\t\t\t}
|
21
|
+
\t\t}
|
22
|
+
\t}"""
|
17
23
|
end
|
18
|
-
event_file += "\n\t\tvar data: EventData {\n"
|
19
|
-
event_file += "\t\t\tswitch self {\n"
|
20
|
-
bundle.each do |event|
|
21
|
-
event_file += event_data(event)
|
22
|
-
end
|
23
|
-
event_file += "\t\t\t}\n"
|
24
|
-
event_file += "\t\t}\n\n"
|
25
|
-
event_file += "\t}"
|
26
|
-
event_file += EXTENSION_FOOTER
|
27
24
|
end
|
28
25
|
|
29
26
|
def event_case(event_data)
|
30
27
|
function_name = camelize(event_data.event_name)
|
31
28
|
if event_data.properties.empty?
|
32
|
-
"\t\tcase #{function_name}
|
29
|
+
"\t\tcase #{function_name}"
|
33
30
|
else
|
34
31
|
trimmed_properties = event_data.properties.map { |k, v| k.to_s + ': ' + native_type(v) }.join(", ")
|
35
|
-
"\t\tcase #{function_name}(#{trimmed_properties})
|
32
|
+
"\t\tcase #{function_name}(#{trimmed_properties})"
|
36
33
|
end
|
37
34
|
end
|
38
35
|
|
@@ -40,14 +37,14 @@ module Evva
|
|
40
37
|
function_name = camelize(event_data.event_name)
|
41
38
|
if event_data.properties.empty?
|
42
39
|
function_body = "\t\t\tcase .#{function_name}:\n" \
|
43
|
-
"\t\t\t\treturn EventData(name: \"#{event_data.event_name}\")
|
40
|
+
"\t\t\t\treturn EventData(name: \"#{event_data.event_name}\")"
|
44
41
|
else
|
45
42
|
function_header = prepend_let(event_data.properties)
|
46
43
|
function_arguments = dictionary_pairs(event_data.properties)
|
47
44
|
function_body = "\t\t\tcase .#{function_name}(#{function_header}):\n"\
|
48
45
|
"\t\t\t\treturn EventData(name: \"#{event_data.event_name}\", properties: [\n"\
|
49
46
|
"\t\t\t\t\t#{function_arguments.join(",\n\t\t\t\t\t")} ]\n"\
|
50
|
-
"\t\t\t\t)
|
47
|
+
"\t\t\t\t)"
|
51
48
|
end
|
52
49
|
function_body
|
53
50
|
end
|
@@ -57,23 +54,39 @@ module Evva
|
|
57
54
|
end
|
58
55
|
|
59
56
|
def people_properties(people_bundle, file_name)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
57
|
+
header_footer_wrapper do
|
58
|
+
props = "\tenum Property: String {\n"
|
59
|
+
people_bundle.each do |prop|
|
60
|
+
props << "\t\tcase #{camelize(prop)} = \"#{prop}\"\n"
|
61
|
+
end
|
62
|
+
props << "\t}"
|
64
63
|
end
|
65
|
-
properties += "\t}"
|
66
|
-
properties += EXTENSION_FOOTER
|
67
64
|
end
|
68
65
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
66
|
+
def special_property_enums(enums)
|
67
|
+
header_footer_wrapper do
|
68
|
+
enums.map do |enum|
|
69
|
+
body = "\tenum #{enum.enum_name}: String {\n"
|
70
|
+
enum.values.each do |value|
|
71
|
+
body << "\t\tcase #{camelize(value)} = \"#{value}\"\n"
|
72
|
+
end
|
73
|
+
body << "\t}"
|
74
|
+
end.join("\n\n")
|
74
75
|
end
|
75
|
-
|
76
|
-
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def header_footer_wrapper
|
81
|
+
"""// This file was automatically generated by evva: https://github.com/hole19/evva
|
82
|
+
|
83
|
+
import Foundation
|
84
|
+
|
85
|
+
extension Analytics {
|
86
|
+
|
87
|
+
#{yield.gsub("\t", " ")}
|
88
|
+
}
|
89
|
+
"""
|
77
90
|
end
|
78
91
|
|
79
92
|
def dictionary_pairs(props)
|
@@ -89,8 +102,6 @@ module Evva
|
|
89
102
|
end
|
90
103
|
end
|
91
104
|
|
92
|
-
private
|
93
|
-
|
94
105
|
def is_raw_representable_property?(type)
|
95
106
|
!NATIVE_TYPES.include?(native_type(type).chomp('?'))
|
96
107
|
end
|
@@ -108,7 +119,7 @@ module Evva
|
|
108
119
|
end
|
109
120
|
|
110
121
|
def camelize(term)
|
111
|
-
string = term.to_s
|
122
|
+
string = term.to_s.tr(' ', '_').downcase
|
112
123
|
string = string.sub(/^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
|
113
124
|
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
114
125
|
string.gsub!("/".freeze, "::".freeze)
|
data/lib/evva/version.rb
CHANGED
data/lib/evva.rb
CHANGED
@@ -5,9 +5,8 @@ require 'evva/logger'
|
|
5
5
|
require 'evva/google_sheet'
|
6
6
|
require 'evva/config'
|
7
7
|
require 'evva/file_reader'
|
8
|
-
require 'evva/
|
9
|
-
require 'evva/
|
10
|
-
require 'evva/mixpanel_enum'
|
8
|
+
require 'evva/analytics_event'
|
9
|
+
require 'evva/analytics_enum'
|
11
10
|
require 'evva/object_extension'
|
12
11
|
require 'evva/version'
|
13
12
|
require 'evva/android_generator'
|
@@ -27,7 +26,7 @@ module Evva
|
|
27
26
|
bundle = analytics_data(config: config.data_source)
|
28
27
|
case config.type.downcase
|
29
28
|
when 'android'
|
30
|
-
generator = Evva::AndroidGenerator.new
|
29
|
+
generator = Evva::AndroidGenerator.new(config.package_name)
|
31
30
|
evva_write(bundle, generator, config, 'kt')
|
32
31
|
when 'ios'
|
33
32
|
generator = Evva::SwiftGenerator.new
|
@@ -44,20 +43,19 @@ module Evva
|
|
44
43
|
path = "#{configuration.out_path}/#{configuration.event_enum_file_name}.#{extension}"
|
45
44
|
write_to_file(path, generator.event_enum(bundle[:events], configuration.event_enum_file_name))
|
46
45
|
end
|
46
|
+
|
47
47
|
path = "#{configuration.out_path}/#{configuration.people_file_name}.#{extension}"
|
48
48
|
write_to_file(path, generator.people_properties(bundle[:people], configuration.people_file_name))
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
write_to_file(path, generator.special_property_enum(enum))
|
53
|
-
end
|
50
|
+
path = "#{configuration.out_path}/#{configuration.special_enum_file_name}.#{extension}"
|
51
|
+
write_to_file(path, generator.special_property_enums(bundle[:enums]))
|
54
52
|
end
|
55
53
|
|
56
54
|
def analytics_data(config:)
|
57
55
|
source =
|
58
56
|
case config[:type]
|
59
57
|
when 'google_sheet'
|
60
|
-
Evva::GoogleSheet.new(config[:
|
58
|
+
Evva::GoogleSheet.new(config[:events_url], config[:people_properties_url], config[:enum_classes_url])
|
61
59
|
end
|
62
60
|
events_bundle = {}
|
63
61
|
events_bundle[:events] = source.events
|
data/spec/evva_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe Evva do
|
|
7
7
|
before do
|
8
8
|
allow_any_instance_of(Evva::FileReader).to receive(:open_file).and_return(file)
|
9
9
|
allow_any_instance_of(Evva::GoogleSheet).to receive(:events).and_return(
|
10
|
-
[Evva::
|
10
|
+
[Evva::AnalyticsEvent.new('trackEvent',[])])
|
11
11
|
|
12
12
|
allow_any_instance_of(Evva::GoogleSheet).to receive(:people_properties).and_return([])
|
13
13
|
allow_any_instance_of(Evva::GoogleSheet).to receive(:enum_classes).and_return([])
|
data/spec/fixtures/test.yml
CHANGED
@@ -2,9 +2,12 @@ type: Android
|
|
2
2
|
|
3
3
|
data_source:
|
4
4
|
type: google_sheet
|
5
|
-
|
5
|
+
events_url: https://path-to-csv
|
6
|
+
people_properties_url: https://path-to-csv
|
7
|
+
enum_classes_url: https://path-to-csv
|
6
8
|
|
7
9
|
out_path: analytics
|
8
|
-
event_file_name:
|
9
|
-
event_enum_file_name:
|
10
|
-
people_file_name:
|
10
|
+
event_file_name: AnalyticsEvent
|
11
|
+
event_enum_file_name: AnalyticsEvents
|
12
|
+
people_file_name: AnalyticsProperties
|
13
|
+
package_name: com.package.name.analytics
|
@@ -1,135 +1,144 @@
|
|
1
1
|
describe Evva::AndroidGenerator do
|
2
|
-
let(:generator) { described_class.new }
|
2
|
+
let(:generator) { described_class.new("com.hole19golf.hole19.analytics") }
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
describe '#events' do
|
5
|
+
subject { generator.events(events, "AnalyticsEvent") }
|
6
|
+
|
7
|
+
let(:events) { [
|
8
|
+
Evva::AnalyticsEvent.new('cp_page_view'),
|
9
|
+
Evva::AnalyticsEvent.new('cp_page_view_a', { course_id: 'Long', course_name: 'String' }),
|
10
|
+
Evva::AnalyticsEvent.new('cp_page_view_b', { course_id: 'Long', course_name: 'String', from_screen: 'CourseProfileSource' }),
|
11
|
+
Evva::AnalyticsEvent.new('cp_page_view_c', { course_id: 'Long', course_name: 'String', from_screen: 'CourseProfileSource?' }),
|
12
|
+
Evva::AnalyticsEvent.new('cp_page_view_d', { course_id: 'Long?', course_name: 'String' })
|
13
|
+
] }
|
14
|
+
|
15
|
+
let(:expected) {
|
16
|
+
<<-Kotlin
|
17
|
+
package com.hole19golf.hole19.analytics
|
18
|
+
|
19
|
+
/**
|
20
|
+
* This file was automatically generated by evva: https://github.com/hole19/evva
|
21
|
+
*/
|
22
|
+
|
23
|
+
sealed class AnalyticsEvent(event: AnalyticsEvents) {
|
24
|
+
val name = event.key
|
25
|
+
|
26
|
+
open val properties: Map<String, Any?>? = null
|
8
27
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
context 'event has optional properties' do
|
55
|
-
let(:event) { Evva::MixpanelEvent.new('cp_page_view', { course_id: 'Long', course_name: 'String', from_screen: 'CourseProfileSource?' }) }
|
56
|
-
let(:expected) { <<-Kotlin
|
57
|
-
open fun trackCpPageView(course_id: Long, course_name: String, from_screen: CourseProfileSource?) {
|
58
|
-
val properties = JSONObject().apply {
|
59
|
-
put("course_id", course_id)
|
60
|
-
put("course_name", course_name)
|
61
|
-
from_screen?.let { put("from_screen", it.key) }
|
62
|
-
}
|
63
|
-
mixpanelMask.trackEvent(MixpanelEvent.CP_PAGE_VIEW, properties)
|
64
|
-
}
|
65
|
-
Kotlin
|
66
|
-
}
|
67
|
-
it { should eq trim_spaces(expected) }
|
68
|
-
end
|
69
|
-
|
70
|
-
context 'event has optional but not special properties' do
|
71
|
-
let(:event) { Evva::MixpanelEvent.new('cp_page_view', { course_id: 'Long?', course_name: 'String' }) }
|
72
|
-
let(:expected) { <<-Kotlin
|
73
|
-
open fun trackCpPageView(course_id: Long?, course_name: String) {
|
74
|
-
val properties = JSONObject().apply {
|
75
|
-
course_id?.let { put("course_id", it) }
|
76
|
-
put("course_name", course_name)
|
77
|
-
}
|
78
|
-
mixpanelMask.trackEvent(MixpanelEvent.CP_PAGE_VIEW, properties)
|
79
|
-
}
|
80
|
-
Kotlin
|
81
|
-
}
|
82
|
-
it { should eq trim_spaces(expected) }
|
83
|
-
end
|
28
|
+
object CpPageView : AnalyticsEvent(AnalyticsEvents.CP_PAGE_VIEW)
|
29
|
+
|
30
|
+
data class CpPageViewA(
|
31
|
+
val courseId: Long, val courseName: String
|
32
|
+
) : AnalyticsEvent(AnalyticsEvents.CP_PAGE_VIEW_A) {
|
33
|
+
override val properties = mapOf(
|
34
|
+
"course_id" to courseId,
|
35
|
+
"course_name" to courseName
|
36
|
+
)
|
37
|
+
}
|
38
|
+
|
39
|
+
data class CpPageViewB(
|
40
|
+
val courseId: Long, val courseName: String, val fromScreen: CourseProfileSource
|
41
|
+
) : AnalyticsEvent(AnalyticsEvents.CP_PAGE_VIEW_B) {
|
42
|
+
override val properties = mapOf(
|
43
|
+
"course_id" to courseId,
|
44
|
+
"course_name" to courseName,
|
45
|
+
"from_screen" to fromScreen.key
|
46
|
+
)
|
47
|
+
}
|
48
|
+
|
49
|
+
data class CpPageViewC(
|
50
|
+
val courseId: Long, val courseName: String, val fromScreen: CourseProfileSource?
|
51
|
+
) : AnalyticsEvent(AnalyticsEvents.CP_PAGE_VIEW_C) {
|
52
|
+
override val properties = mapOf(
|
53
|
+
"course_id" to courseId,
|
54
|
+
"course_name" to courseName,
|
55
|
+
"from_screen" to fromScreen?.key
|
56
|
+
)
|
57
|
+
}
|
58
|
+
|
59
|
+
data class CpPageViewD(
|
60
|
+
val courseId: Long?, val courseName: String
|
61
|
+
) : AnalyticsEvent(AnalyticsEvents.CP_PAGE_VIEW_D) {
|
62
|
+
override val properties = mapOf(
|
63
|
+
"course_id" to courseId,
|
64
|
+
"course_name" to courseName
|
65
|
+
)
|
66
|
+
}
|
67
|
+
}
|
68
|
+
Kotlin
|
69
|
+
}
|
70
|
+
|
71
|
+
it { should eq expected }
|
84
72
|
end
|
85
73
|
|
86
|
-
describe '#
|
87
|
-
subject {
|
88
|
-
let(:
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
74
|
+
describe '#special_property_enums' do
|
75
|
+
subject { generator.special_property_enums(enums) }
|
76
|
+
let(:enums) { [
|
77
|
+
Evva::AnalyticsEnum.new('CourseProfileSource', ['course_discovery', 'synced_courses']),
|
78
|
+
Evva::AnalyticsEnum.new('PremiumFrom', ['Course Profile', 'Round Setup'])
|
79
|
+
] }
|
80
|
+
let(:expected) {
|
81
|
+
<<-Kotlin
|
82
|
+
package com.hole19golf.hole19.analytics
|
83
|
+
|
84
|
+
/**
|
85
|
+
* This file was automatically generated by evva: https://github.com/hole19/evva
|
86
|
+
*/
|
87
|
+
|
88
|
+
enum class CourseProfileSource(val key: String) {
|
89
|
+
COURSE_DISCOVERY("course_discovery"),
|
90
|
+
SYNCED_COURSES("synced_courses");
|
91
|
+
}
|
92
|
+
|
93
|
+
enum class PremiumFrom(val key: String) {
|
94
|
+
COURSE_PROFILE("Course Profile"),
|
95
|
+
ROUND_SETUP("Round Setup");
|
96
|
+
}
|
97
|
+
Kotlin
|
97
98
|
}
|
98
|
-
it { should eq
|
99
|
+
it { should eq expected }
|
99
100
|
end
|
100
101
|
|
101
102
|
describe '#event_enum' do
|
102
|
-
subject {
|
103
|
+
subject { generator.event_enum(event_bundle, 'AnalyticsEvents') }
|
103
104
|
let(:event_bundle) { [
|
104
|
-
Evva::
|
105
|
-
Evva::
|
105
|
+
Evva::AnalyticsEvent.new('nav_feed_tap', {}),
|
106
|
+
Evva::AnalyticsEvent.new('nav_performance_tap', {})
|
106
107
|
] }
|
107
|
-
let(:expected) {
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
108
|
+
let(:expected) {
|
109
|
+
<<-Kotlin
|
110
|
+
package com.hole19golf.hole19.analytics
|
111
|
+
|
112
|
+
/**
|
113
|
+
* This file was automatically generated by evva: https://github.com/hole19/evva
|
114
|
+
*/
|
115
|
+
|
116
|
+
enum class AnalyticsEvents(val key: String) {
|
117
|
+
NAV_FEED_TAP("nav_feed_tap"),
|
118
|
+
NAV_PERFORMANCE_TAP("nav_performance_tap");
|
119
|
+
}
|
120
|
+
Kotlin
|
116
121
|
}
|
117
|
-
it { should eq
|
122
|
+
it { should eq expected }
|
118
123
|
end
|
119
124
|
|
120
125
|
describe '#people_properties' do
|
121
|
-
subject {
|
126
|
+
subject { generator.people_properties(people_bundle, 'AnalyticsProperties') }
|
122
127
|
let(:people_bundle) { ['rounds_with_wear', 'friends_from_facebook'] }
|
123
|
-
let(:expected) {
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
128
|
+
let(:expected) {
|
129
|
+
<<-Kotlin
|
130
|
+
package com.hole19golf.hole19.analytics
|
131
|
+
|
132
|
+
/**
|
133
|
+
* This file was automatically generated by evva: https://github.com/hole19/evva
|
134
|
+
*/
|
135
|
+
|
136
|
+
enum class AnalyticsProperties(val key: String) {
|
137
|
+
ROUNDS_WITH_WEAR("rounds_with_wear"),
|
138
|
+
FRIENDS_FROM_FACEBOOK("friends_from_facebook");
|
139
|
+
}
|
140
|
+
Kotlin
|
132
141
|
}
|
133
|
-
it { should eq
|
142
|
+
it { should eq expected }
|
134
143
|
end
|
135
144
|
end
|
@@ -5,13 +5,16 @@ describe Evva::Config do
|
|
5
5
|
{
|
6
6
|
type: 'EvvaOS',
|
7
7
|
data_source: {
|
8
|
-
type:
|
9
|
-
|
8
|
+
type: 'google_sheet',
|
9
|
+
events_url: 'https://events.csv',
|
10
|
+
people_properties_url: 'https://people_properties.csv',
|
11
|
+
enum_classes_url: 'https://enum_classes.csv',
|
10
12
|
},
|
11
13
|
out_path: 'clear/path/to/event',
|
12
14
|
event_file_name: 'event/file/name',
|
13
15
|
people_file_name: 'people/file/name',
|
14
|
-
event_enum_file_name: 'event/enum/file'
|
16
|
+
event_enum_file_name: 'event/enum/file',
|
17
|
+
package_name: 'com.package.name.analytics'
|
15
18
|
}
|
16
19
|
end
|
17
20
|
|
@@ -26,11 +29,12 @@ describe Evva::Config do
|
|
26
29
|
its(:event_file_name) { should eq('event/file/name') }
|
27
30
|
its(:people_file_name) { should eq('people/file/name') }
|
28
31
|
its(:event_enum_file_name) { should eq 'event/enum/file' }
|
32
|
+
its(:package_name) { should eq 'com.package.name.analytics' }
|
29
33
|
|
30
34
|
describe '#data_source' do
|
31
35
|
subject(:data_source) { config.data_source }
|
32
36
|
|
33
|
-
it { should eq(type: 'google_sheet',
|
37
|
+
it { should eq(type: 'google_sheet', events_url: 'https://events.csv', people_properties_url: 'https://people_properties.csv', enum_classes_url: 'https://enum_classes.csv') }
|
34
38
|
|
35
39
|
context 'when given an unknown type data source' do
|
36
40
|
before { hash[:data_source] = { type: 'i_dunno' } }
|