evva 0.1.4.1 → 0.2.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/.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
|