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.
@@ -1,72 +1,50 @@
1
1
  module Evva
2
2
  class SwiftGenerator
3
- SWIFT_EVENT_HEADER =
4
- "import CoreLocation\n"\
5
- "import Foundation\n"\
6
- "import SharedCode\n\n"\
7
- "@objc class MixpanelHelper: NSObject {\n\n"\
8
- "\tprivate struct EventData {\n"\
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
- event_file = SWIFT_EVENT_HEADER
35
- bundle.each do |event|
36
- event_file += swift_case(event)
37
- end
38
- event_file += SWIFT_EVENT_DATA_HEADER
39
- bundle.each do |event|
40
- event_file += swift_event_data(event)
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 swift_case(event_data)
49
- function_name = 'track' + titleize(event_data.event_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}\n"
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})\n"
32
+ "\t\tcase #{function_name}(#{trimmed_properties})"
55
33
  end
56
34
  end
57
35
 
58
- def swift_event_data(event_data)
59
- function_name = 'track' + titleize(event_data.event_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}\")\n\n"
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)\n\n"
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
- properties = SWIFT_PEOPLE_HEADER
80
- properties += people_bundle.map { |prop| swift_people_const(prop) }.join('')
81
- properties + "\n" + SWIFT_INCREMENT_FUNCTION + "\n}\n"
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
- def special_property_enum(enum)
85
- enum_body = "import Foundation\n\n"
86
- enum_body += "enum #{enum.enum_name}: String {\n"
87
- enum_body += enum.values.map { |vals| "\tcase #{vals.tr(' ', '_')} = \"#{vals}\"\n" }.join('')
88
- enum_body + "} \n"
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 swift_people_const(prop)
123
- "\tcase #{titleize(prop)} = \"#{prop}\"\n"
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
@@ -1,4 +1,4 @@
1
1
  module Evva
2
- VERSION = '0.1.4.1'.freeze
3
- VERSION_UPDATED_AT = '2018-02-08'.freeze
2
+ VERSION = '0.2.0'.freeze
3
+ VERSION_UPDATED_AT = '2021-08-30'.freeze
4
4
  end
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
- bundle[:enums].each do |enum|
51
- path = "#{configuration.out_path}/#{enum.enum_name}.#{extension}"
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[:sheet_id])
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
@@ -0,0 +1,3 @@
1
+ Enum Name,Possible Values
2
+ PageViewSourceScreen,"course_discovery,synced_courses,nearby,deal"
3
+ PremiumClickBuy,"notes,hi_res_maps,whatever"
@@ -0,0 +1,4 @@
1
+ Event Name,Event Properties
2
+ cp_page_view,"course_id:Long,course_name:String"
3
+ nav_feed_tap,
4
+ cp_view_scorecard,"course_id:Long,course_name:String"
@@ -0,0 +1,3 @@
1
+ Property Name,Comments
2
+ rounds_with_wear,asldkaslkdlaksd
3
+ total_friends,
@@ -2,9 +2,12 @@ type: Android
2
2
 
3
3
  data_source:
4
4
  type: google_sheet
5
- sheet_id: 1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4
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
- def trim_spaces(str)
5
- str.gsub(/^[ \t]+/, '')
6
- .gsub(/[ \t]+$/, '')
7
- end
4
+ describe '#events' do
5
+ subject { generator.events(events, "MixpanelAnalytics") }
8
6
 
9
- describe '#kotlin_function' do
10
- subject { trim_spaces(generator.kotlin_function(event)) }
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
- context 'when the event has no properties' do
13
- let(:event) { Evva::MixpanelEvent.new('nav_feed_tap') }
14
- let(:expected) { <<-Kotlin
15
- open fun trackNavFeedTap() {
16
- mixpanelMask.trackEvent(MixpanelEvent.NAV_FEED_TAP)
17
- }
18
- Kotlin
19
- }
20
- it { should eq trim_spaces(expected) }
21
- end
22
-
23
- context 'event has properties' do
24
- let(:event) { Evva::MixpanelEvent.new('cp_page_view', { course_id: 'Long', course_name: 'String' }) }
25
- let(:expected) { <<-Kotlin
26
- open fun trackCpPageView(course_id: Long, course_name: String) {
27
- val properties = JSONObject().apply {
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
- Kotlin
34
- }
35
- it { should eq trim_spaces(expected) }
36
- end
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
- Kotlin
50
- }
51
- it { should eq trim_spaces(expected) }
52
- end
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
- 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 {
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
- Kotlin
81
- }
82
- it { should eq trim_spaces(expected) }
83
- end
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 '#special_property_enum' do
87
- subject { trim_spaces(generator.special_property_enum(enum)) }
88
- let(:enum) { Evva::MixpanelEnum.new('CourseProfileSource', ['course_discovery','synced_courses']) }
89
- let(:expected) { <<-Kotlin
90
- package com.hole19golf.hole19.analytics
91
-
92
- enum class CourseProfileSource(val key: String) {
93
- COURSE_DISCOVERY("course_discovery"),
94
- SYNCED_COURSES("synced_courses")
95
- }
96
- Kotlin
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 trim_spaces(expected) }
106
+ it { should eq expected }
99
107
  end
100
108
 
101
109
  describe '#event_enum' do
102
- subject { trim_spaces(generator.event_enum(event_bundle, 'MixpanelEvent')) }
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) { <<-Kotlin
108
- package com.hole19golf.hole19.analytics
109
- import com.hole19golf.hole19.analytics.Event
110
-
111
- enum class MixpanelEvent(override val key: String) : Event {
112
- NAV_FEED_TAP("nav_feed_tap"),
113
- NAV_PERFORMANCE_TAP("nav_performance_tap")
114
- }
115
- Kotlin
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 trim_spaces(expected) }
131
+ it { should eq expected }
118
132
  end
119
133
 
120
134
  describe '#people_properties' do
121
- subject { trim_spaces(generator.people_properties(people_bundle, 'MixpanelProperties')) }
135
+ subject { generator.people_properties(people_bundle, 'MixpanelProperties') }
122
136
  let(:people_bundle) { ['rounds_with_wear', 'friends_from_facebook'] }
123
- let(:expected) { <<-Kotlin
124
- package com.hole19golf.hole19.analytics
125
- import com.hole19golf.hole19.analytics.Event
126
-
127
- enum class MixpanelProperties(val key: String) {
128
- ROUNDS_WITH_WEAR("rounds_with_wear"),
129
- FRIENDS_FROM_FACEBOOK("friends_from_facebook");
130
- }
131
- Kotlin
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 trim_spaces(expected) }
151
+ it { should eq expected }
134
152
  end
135
153
  end