evva 0.1.4.1 → 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.
- checksums.yaml +5 -5
- data/.gitignore +1 -2
- data/.rspec +0 -1
- data/.ruby-version +1 -0
- data/.travis.yml +1 -0
- data/Gemfile +10 -8
- data/Gemfile.lock +5 -6
- data/README.md +10 -0
- data/Rakefile +9 -0
- data/changelog.md +18 -1
- data/evva.gemspec +0 -3
- data/evva_config.yml +5 -1
- data/lib/evva/android_generator.rb +72 -53
- data/lib/evva/config.rb +13 -2
- data/lib/evva/google_sheet.rb +49 -46
- data/lib/evva/swift_generator.rb +61 -61
- data/lib/evva/version.rb +2 -2
- data/lib/evva.rb +5 -7
- 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 +5 -2
- data/spec/lib/evva/android_generator_spec.rb +117 -99
- 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 +9 -38
- 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
@@ -1,72 +1,50 @@
|
|
1
1
|
module Evva
|
2
2
|
class SwiftGenerator
|
3
|
-
|
4
|
-
"
|
5
|
-
"
|
6
|
-
|
7
|
-
|
8
|
-
"\
|
9
|
-
"\t\tlet name: String\n"\
|
10
|
-
"\t\tlet properties: [String: Any]?\n"\
|
11
|
-
"\t\tlet timeEvent: Bool\n\n"\
|
12
|
-
"\t\tinit(name: String, properties: [String: Any]? = nil, timeEvent: Bool = false) {\n"\
|
13
|
-
"\t\t\tself.name = name\n"\
|
14
|
-
"\t\t\tself.properties = properties\n"\
|
15
|
-
"\t\t\tself.timeEvent = timeEvent\n"\
|
16
|
-
"\t\t}\n"\
|
17
|
-
"\t}\n\n"\
|
18
|
-
"\tenum Event {\n".freeze
|
19
|
-
|
20
|
-
SWIFT_EVENT_DATA_HEADER =
|
21
|
-
"\t\tprivate var data: EventData {\n"\
|
22
|
-
"\t\t\tswitch self {\n".freeze
|
23
|
-
|
24
|
-
SWIFT_PEOPLE_HEADER = "fileprivate enum Counter: String {\n".freeze
|
25
|
-
|
26
|
-
SWIFT_INCREMENT_FUNCTION =
|
27
|
-
"\tfunc increment(times: Int = 1) {\n"\
|
28
|
-
"\t\tMixpanelAPI.instance.incrementCounter(rawValue, times: times)\n"\
|
29
|
-
"\t}".freeze
|
3
|
+
EXTENSION_HEADER =
|
4
|
+
"\nimport Foundation\n\n"\
|
5
|
+
"extension Analytics {\n\n".freeze
|
6
|
+
|
7
|
+
EXTENSION_FOOTER =
|
8
|
+
"\n\n}\n"
|
30
9
|
|
31
10
|
NATIVE_TYPES = %w[Int String Double Float Bool].freeze
|
32
11
|
|
33
12
|
def events(bundle, file_name)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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}"""
|
41
23
|
end
|
42
|
-
event_file += "\t\t\t}\n"
|
43
|
-
event_file += "\t\t}\n"
|
44
|
-
event_file += "\t}\n"
|
45
|
-
event_file += "}\n"
|
46
24
|
end
|
47
25
|
|
48
|
-
def
|
49
|
-
function_name =
|
26
|
+
def event_case(event_data)
|
27
|
+
function_name = camelize(event_data.event_name)
|
50
28
|
if event_data.properties.empty?
|
51
|
-
"\t\tcase #{function_name}
|
29
|
+
"\t\tcase #{function_name}"
|
52
30
|
else
|
53
31
|
trimmed_properties = event_data.properties.map { |k, v| k.to_s + ': ' + native_type(v) }.join(", ")
|
54
|
-
"\t\tcase #{function_name}(#{trimmed_properties})
|
32
|
+
"\t\tcase #{function_name}(#{trimmed_properties})"
|
55
33
|
end
|
56
34
|
end
|
57
35
|
|
58
|
-
def
|
59
|
-
function_name =
|
36
|
+
def event_data(event_data)
|
37
|
+
function_name = camelize(event_data.event_name)
|
60
38
|
if event_data.properties.empty?
|
61
39
|
function_body = "\t\t\tcase .#{function_name}:\n" \
|
62
|
-
"\t\t\t\treturn EventData(name: \"#{event_data.event_name}\")
|
40
|
+
"\t\t\t\treturn EventData(name: \"#{event_data.event_name}\")"
|
63
41
|
else
|
64
42
|
function_header = prepend_let(event_data.properties)
|
65
43
|
function_arguments = dictionary_pairs(event_data.properties)
|
66
44
|
function_body = "\t\t\tcase .#{function_name}(#{function_header}):\n"\
|
67
45
|
"\t\t\t\treturn EventData(name: \"#{event_data.event_name}\", properties: [\n"\
|
68
46
|
"\t\t\t\t\t#{function_arguments.join(",\n\t\t\t\t\t")} ]\n"\
|
69
|
-
"\t\t\t\t)
|
47
|
+
"\t\t\t\t)"
|
70
48
|
end
|
71
49
|
function_body
|
72
50
|
end
|
@@ -76,16 +54,39 @@ module Evva
|
|
76
54
|
end
|
77
55
|
|
78
56
|
def people_properties(people_bundle, file_name)
|
79
|
-
|
80
|
-
|
81
|
-
|
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}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
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")
|
75
|
+
end
|
82
76
|
end
|
83
77
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
+
"""
|
89
90
|
end
|
90
91
|
|
91
92
|
def dictionary_pairs(props)
|
@@ -101,8 +102,6 @@ module Evva
|
|
101
102
|
end
|
102
103
|
end
|
103
104
|
|
104
|
-
private
|
105
|
-
|
106
105
|
def is_raw_representable_property?(type)
|
107
106
|
!NATIVE_TYPES.include?(native_type(type).chomp('?'))
|
108
107
|
end
|
@@ -119,12 +118,13 @@ module Evva
|
|
119
118
|
props.map { |k, v| "let #{k}" }.join(', ')
|
120
119
|
end
|
121
120
|
|
122
|
-
def
|
123
|
-
|
121
|
+
def camelize(term)
|
122
|
+
string = term.to_s.tr(' ', '_').downcase
|
123
|
+
string = string.sub(/^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
|
124
|
+
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
125
|
+
string.gsub!("/".freeze, "::".freeze)
|
126
|
+
string
|
124
127
|
end
|
125
128
|
|
126
|
-
def titleize(str)
|
127
|
-
str.split('_').collect(&:capitalize).join
|
128
|
-
end
|
129
129
|
end
|
130
130
|
end
|
data/lib/evva/version.rb
CHANGED
data/lib/evva.rb
CHANGED
@@ -5,7 +5,6 @@ require 'evva/logger'
|
|
5
5
|
require 'evva/google_sheet'
|
6
6
|
require 'evva/config'
|
7
7
|
require 'evva/file_reader'
|
8
|
-
require 'evva/data_source'
|
9
8
|
require 'evva/mixpanel_event'
|
10
9
|
require 'evva/mixpanel_enum'
|
11
10
|
require 'evva/object_extension'
|
@@ -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/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
10
|
event_file_name: MixpanelAnalytics
|
9
11
|
event_enum_file_name: MixpanelEvent
|
10
|
-
people_file_name: MixpanelProperties
|
12
|
+
people_file_name: MixpanelProperties
|
13
|
+
package_name: com.package.name.analytics
|
@@ -1,135 +1,153 @@
|
|
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
|
-
.gsub(/[ \t]+$/, '')
|
7
|
-
end
|
4
|
+
describe '#events' do
|
5
|
+
subject { generator.events(events, "MixpanelAnalytics") }
|
8
6
|
|
9
|
-
|
10
|
-
|
7
|
+
let(:events) { [
|
8
|
+
Evva::MixpanelEvent.new('cp_page_view'),
|
9
|
+
Evva::MixpanelEvent.new('cp_page_view_a', { course_id: 'Long', course_name: 'String' }),
|
10
|
+
Evva::MixpanelEvent.new('cp_page_view_b', { course_id: 'Long', course_name: 'String', from_screen: 'CourseProfileSource' }),
|
11
|
+
Evva::MixpanelEvent.new('cp_page_view_c', { course_id: 'Long', course_name: 'String', from_screen: 'CourseProfileSource?' }),
|
12
|
+
Evva::MixpanelEvent.new('cp_page_view_d', { course_id: 'Long?', course_name: 'String' })
|
13
|
+
] }
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
15
|
+
let(:expected) {
|
16
|
+
<<-Kotlin
|
17
|
+
package com.hole19golf.hole19.analytics
|
18
|
+
|
19
|
+
import com.hole19golf.hole19.analytics.Event
|
20
|
+
import com.hole19golf.hole19.analytics.MixpanelAnalyticsMask
|
21
|
+
import org.json.JSONObject
|
22
|
+
|
23
|
+
/**
|
24
|
+
* This file was automatically generated by evva: https://github.com/hole19/evva
|
25
|
+
*/
|
26
|
+
|
27
|
+
open class MixpanelAnalytics(private val mask: MixpanelAnalyticsMask) {
|
28
|
+
|
29
|
+
open fun trackCpPageView() {
|
30
|
+
mask.trackEvent(MixpanelEvent.CP_PAGE_VIEW)
|
31
|
+
}
|
32
|
+
|
33
|
+
open fun trackCpPageViewA(course_id: Long, course_name: String) {
|
34
|
+
val properties = JSONObject().apply {
|
28
35
|
put("course_id", course_id)
|
29
36
|
put("course_name", course_name)
|
30
|
-
}
|
31
|
-
mixpanelMask.trackEvent(MixpanelEvent.CP_PAGE_VIEW, properties)
|
32
37
|
}
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
context 'event has special properties' do
|
39
|
-
let(:event) { Evva::MixpanelEvent.new('cp_page_view', { course_id: 'Long', course_name: 'String', from_screen: 'CourseProfileSource' }) }
|
40
|
-
let(:expected) { <<-Kotlin
|
41
|
-
open fun trackCpPageView(course_id: Long, course_name: String, from_screen: CourseProfileSource) {
|
42
|
-
val properties = JSONObject().apply {
|
38
|
+
mask.trackEvent(MixpanelEvent.CP_PAGE_VIEW_A, properties)
|
39
|
+
}
|
40
|
+
|
41
|
+
open fun trackCpPageViewB(course_id: Long, course_name: String, from_screen: CourseProfileSource) {
|
42
|
+
val properties = JSONObject().apply {
|
43
43
|
put("course_id", course_id)
|
44
44
|
put("course_name", course_name)
|
45
45
|
put("from_screen", from_screen.key)
|
46
|
-
}
|
47
|
-
mixpanelMask.trackEvent(MixpanelEvent.CP_PAGE_VIEW, properties)
|
48
46
|
}
|
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 {
|
47
|
+
mask.trackEvent(MixpanelEvent.CP_PAGE_VIEW_B, properties)
|
48
|
+
}
|
49
|
+
|
50
|
+
open fun trackCpPageViewC(course_id: Long, course_name: String, from_screen: CourseProfileSource?) {
|
51
|
+
val properties = JSONObject().apply {
|
59
52
|
put("course_id", course_id)
|
60
53
|
put("course_name", course_name)
|
61
54
|
from_screen?.let { put("from_screen", it.key) }
|
62
|
-
}
|
63
|
-
mixpanelMask.trackEvent(MixpanelEvent.CP_PAGE_VIEW, properties)
|
64
55
|
}
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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 {
|
56
|
+
mask.trackEvent(MixpanelEvent.CP_PAGE_VIEW_C, properties)
|
57
|
+
}
|
58
|
+
|
59
|
+
open fun trackCpPageViewD(course_id: Long?, course_name: String) {
|
60
|
+
val properties = JSONObject().apply {
|
75
61
|
course_id?.let { put("course_id", it) }
|
76
62
|
put("course_name", course_name)
|
77
|
-
}
|
78
|
-
mixpanelMask.trackEvent(MixpanelEvent.CP_PAGE_VIEW, properties)
|
79
63
|
}
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
64
|
+
mask.trackEvent(MixpanelEvent.CP_PAGE_VIEW_D, properties)
|
65
|
+
}
|
66
|
+
|
67
|
+
open fun updateProperties(property: MixpanelProperties, value: Any) {
|
68
|
+
mask.updateProperties(property.key, value)
|
69
|
+
}
|
70
|
+
|
71
|
+
open fun incrementCounter(property: MixpanelProperties) {
|
72
|
+
mask.incrementCounter(property.key)
|
73
|
+
}
|
74
|
+
}
|
75
|
+
Kotlin
|
76
|
+
}
|
77
|
+
|
78
|
+
it { should eq expected }
|
84
79
|
end
|
85
80
|
|
86
|
-
describe '#
|
87
|
-
subject {
|
88
|
-
let(:
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
81
|
+
describe '#special_property_enums' do
|
82
|
+
subject { generator.special_property_enums(enums) }
|
83
|
+
let(:enums) { [
|
84
|
+
Evva::MixpanelEnum.new('CourseProfileSource', ['course_discovery', 'synced_courses']),
|
85
|
+
Evva::MixpanelEnum.new('PremiumFrom', ['Course Profile', 'Round Setup'])
|
86
|
+
] }
|
87
|
+
let(:expected) {
|
88
|
+
<<-Kotlin
|
89
|
+
package com.hole19golf.hole19.analytics
|
90
|
+
|
91
|
+
/**
|
92
|
+
* This file was automatically generated by evva: https://github.com/hole19/evva
|
93
|
+
*/
|
94
|
+
|
95
|
+
enum class CourseProfileSource(val key: String) {
|
96
|
+
COURSE_DISCOVERY("course_discovery"),
|
97
|
+
SYNCED_COURSES("synced_courses");
|
98
|
+
}
|
99
|
+
|
100
|
+
enum class PremiumFrom(val key: String) {
|
101
|
+
COURSE_PROFILE("Course Profile"),
|
102
|
+
ROUND_SETUP("Round Setup");
|
103
|
+
}
|
104
|
+
Kotlin
|
97
105
|
}
|
98
|
-
it { should eq
|
106
|
+
it { should eq expected }
|
99
107
|
end
|
100
108
|
|
101
109
|
describe '#event_enum' do
|
102
|
-
subject {
|
110
|
+
subject { generator.event_enum(event_bundle, 'MixpanelEvent') }
|
103
111
|
let(:event_bundle) { [
|
104
112
|
Evva::MixpanelEvent.new('nav_feed_tap', {}),
|
105
113
|
Evva::MixpanelEvent.new('nav_performance_tap', {})
|
106
114
|
] }
|
107
|
-
let(:expected) {
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
115
|
+
let(:expected) {
|
116
|
+
<<-Kotlin
|
117
|
+
package com.hole19golf.hole19.analytics
|
118
|
+
|
119
|
+
import com.hole19golf.hole19.analytics.Event
|
120
|
+
|
121
|
+
/**
|
122
|
+
* This file was automatically generated by evva: https://github.com/hole19/evva
|
123
|
+
*/
|
124
|
+
|
125
|
+
enum class MixpanelEvent(override val key: String) : Event {
|
126
|
+
NAV_FEED_TAP("nav_feed_tap"),
|
127
|
+
NAV_PERFORMANCE_TAP("nav_performance_tap");
|
128
|
+
}
|
129
|
+
Kotlin
|
116
130
|
}
|
117
|
-
it { should eq
|
131
|
+
it { should eq expected }
|
118
132
|
end
|
119
133
|
|
120
134
|
describe '#people_properties' do
|
121
|
-
subject {
|
135
|
+
subject { generator.people_properties(people_bundle, 'MixpanelProperties') }
|
122
136
|
let(:people_bundle) { ['rounds_with_wear', 'friends_from_facebook'] }
|
123
|
-
let(:expected) {
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
137
|
+
let(:expected) {
|
138
|
+
<<-Kotlin
|
139
|
+
package com.hole19golf.hole19.analytics
|
140
|
+
|
141
|
+
/**
|
142
|
+
* This file was automatically generated by evva: https://github.com/hole19/evva
|
143
|
+
*/
|
144
|
+
|
145
|
+
enum class MixpanelProperties(val key: String) {
|
146
|
+
ROUNDS_WITH_WEAR("rounds_with_wear"),
|
147
|
+
FRIENDS_FROM_FACEBOOK("friends_from_facebook");
|
148
|
+
}
|
149
|
+
Kotlin
|
132
150
|
}
|
133
|
-
it { should eq
|
151
|
+
it { should eq expected }
|
134
152
|
end
|
135
153
|
end
|