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