evva 0.1.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 +7 -0
- data/.gitignore +5 -0
- data/.rspec +5 -0
- data/.rubocop_todo.yml +477 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +79 -0
- data/README.md +27 -0
- data/bin/evva +4 -0
- data/evva.gemspec +24 -0
- data/evva_config.yml +10 -0
- data/lib/evva.rb +95 -0
- data/lib/evva/android_generator.rb +115 -0
- data/lib/evva/config.rb +68 -0
- data/lib/evva/data_source.rb +23 -0
- data/lib/evva/file_reader.rb +24 -0
- data/lib/evva/google_sheet.rb +91 -0
- data/lib/evva/logger.rb +61 -0
- data/lib/evva/mixpanel_enum.rb +14 -0
- data/lib/evva/mixpanel_event.rb +13 -0
- data/lib/evva/object_extension.rb +41 -0
- data/lib/evva/swift_generator.rb +128 -0
- data/lib/evva/version.rb +4 -0
- data/rubocop.yml +54 -0
- data/spec/evva_spec.rb +40 -0
- data/spec/fixtures/sample_public_enums.html +1 -0
- data/spec/fixtures/sample_public_info.html +1 -0
- data/spec/fixtures/sample_public_people_properties.html +1 -0
- data/spec/fixtures/sample_public_sheet.html +1 -0
- data/spec/fixtures/test.yml +10 -0
- data/spec/lib/evva/android_generator_spec.rb +135 -0
- data/spec/lib/evva/config_spec.rb +40 -0
- data/spec/lib/evva/data_source_spec.rb +28 -0
- data/spec/lib/evva/google_sheet_spec.rb +107 -0
- data/spec/lib/evva/logger_spec.rb +36 -0
- data/spec/lib/evva/object_extension_spec.rb +99 -0
- data/spec/lib/evva/swift_generator_spec.rb +81 -0
- data/spec/spec_helper.rb +113 -0
- metadata +136 -0
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gsx='http://schemas.google.com/spreadsheets/2006/extended'><id>https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/osju1vh/public/full</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>Enums</title><link rel='alternate' type='application/atom+xml' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/pubhtml'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/osju1vh/public/full'/><link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/osju1vh/public/full'/><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/osju1vh/public/full'/><author><name>ricardo.trindade</name><email>ricardo.trindade@hole19golf.com</email></author><openSearch:totalResults>2</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><entry><id>https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/osju1vh/public/full/cokwr</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>PageViewSourceScreen</title><content type='text'>values: course_discovery,synced_courses,nearby,deal</content><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/osju1vh/public/full/cokwr'/><gsx:enum>PageViewSourceScreen</gsx:enum><gsx:values>course_discovery,synced_courses,nearby,deal</gsx:values></entry><entry><id>https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/osju1vh/public/full/cpzh4</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>PremiumClickBuy</title><content type='text'>values: notes,hi_res_maps,whatever</content><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/osju1vh/public/full/cpzh4'/><gsx:enum>PremiumClickBuy</gsx:enum><gsx:values>notes,hi_res_maps,whatever</gsx:values></entry></feed>
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gs='http://schemas.google.com/spreadsheets/2006'><id>https://spreadsheets.google.com/feeds/worksheets/abcdefgh/public/full</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#worksheet'/><title type='text'>TestingEvva</title><link rel='alternate' type='application/atom+xml' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/abc1234567890/pubhtml'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/worksheets/abc1234567890/public/full'/><link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/worksheets/abc1234567890/public/full'/><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/worksheets/abc1234567890/public/full'/><author><name>ricardo.trindade</name><email>ricardo.trindade@hole19golf.com</email></author><openSearch:totalResults>3</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><entry><id>https://spreadsheets.google.com/feeds/worksheets/abc1234567890/public/full/od6</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#worksheet'/><title type='text'>Events</title><content type='text'>Events</content><link rel='http://schemas.google.com/spreadsheets/2006#listfeed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full'/><link rel='http://schemas.google.com/spreadsheets/2006#cellsfeed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/cells/abc1234567890/od6/public/full'/><link rel='http://schemas.google.com/visualization/2008#visualizationApi' type='application/atom+xml' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/abc1234567890/gviz/tq?gid=0&pub=1'/><link rel='http://schemas.google.com/spreadsheets/2006#exportcsv' type='text/csv' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/abc1234567890/export?gid=0&format=csv'/><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/worksheets/abc1234567890/public/full/od6'/><gs:colCount>26</gs:colCount><gs:rowCount>1000</gs:rowCount></entry><entry><id>https://spreadsheets.google.com/feeds/worksheets/abc1234567890/public/full/ojyi830</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#worksheet'/><title type='text'>PeopleProperties</title><content type='text'>PeopleProperties</content><link rel='http://schemas.google.com/spreadsheets/2006#listfeed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/abc1234567890/ojyi830/public/full'/><link rel='http://schemas.google.com/spreadsheets/2006#cellsfeed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/cells/abc1234567890/ojyi830/public/full'/><link rel='http://schemas.google.com/visualization/2008#visualizationApi' type='application/atom+xml' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/abc1234567890/gviz/tq?gid=1206814390&pub=1'/><link rel='http://schemas.google.com/spreadsheets/2006#exportcsv' type='text/csv' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/abc1234567890/export?gid=1206814390&format=csv'/><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/worksheets/abc1234567890/public/full/ojyi830'/><gs:colCount>26</gs:colCount><gs:rowCount>1000</gs:rowCount></entry><entry><id>https://spreadsheets.google.com/feeds/worksheets/abc1234567890/public/full/osju1vh</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#worksheet'/><title type='text'>Enums</title><content type='text'>Enums</content><link rel='http://schemas.google.com/spreadsheets/2006#listfeed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/abc1234567890/osju1vh/public/full'/><link rel='http://schemas.google.com/spreadsheets/2006#cellsfeed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/cells/abc1234567890/osju1vh/public/full'/><link rel='http://schemas.google.com/visualization/2008#visualizationApi' type='application/atom+xml' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/abc1234567890/gviz/tq?gid=1726367271&pub=1'/><link rel='http://schemas.google.com/spreadsheets/2006#exportcsv' type='text/csv' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/abc1234567890/export?gid=1726367271&format=csv'/><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/worksheets/abc1234567890/public/full/osju1vh'/><gs:colCount>26</gs:colCount><gs:rowCount>1000</gs:rowCount></entry></feed>
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gsx='http://schemas.google.com/spreadsheets/2006/extended'><id>https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/ojyi830/public/full</id><updated>2017-09-06T13:27:41.023Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>PeopleProperties</title><link rel='alternate' type='application/atom+xml' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/pubhtml'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/ojyi830/public/full'/><link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/ojyi830/public/full'/><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/ojyi830/public/full'/><author><name>ricardo.trindade</name><email>ricardo.trindade@hole19golf.com</email></author><openSearch:totalResults>2</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><entry><id>https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/ojyi830/public/full/cokwr</id><updated>2017-09-06T13:27:41.023Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>roundsWithWear</title><content type='text'>value: rounds_with_wear</content><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/ojyi830/public/full/cokwr'/><gsx:properties>roundsWithWear</gsx:properties><gsx:value>rounds_with_wear</gsx:value></entry><entry><id>https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/ojyi830/public/full/cpzh4</id><updated>2017-09-06T13:27:41.023Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>totalFriends</title><content type='text'>value: total_friends</content><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4/ojyi830/public/full/cpzh4'/><gsx:properties>totalFriends</gsx:properties><gsx:value>total_friends</gsx:value></entry></feed>
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gsx='http://schemas.google.com/spreadsheets/2006/extended'><id>https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>Events</title><link rel='alternate' type='application/atom+xml' href='https://docs.google.com/a/hole19golf.com/spreadsheets/d/abc1234567890/pubhtml'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full'/><link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full'/><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full'/><author><name>ricardo.trindade</name><email>ricardo.trindade@hole19golf.com</email></author><openSearch:totalResults>3</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><entry><id>https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full/cokwr</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>trackCpPageView</title><content type='text'>eventname: cp_page_view, props: course_id:Long,course_name:String</content><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full/cokwr'/><gsx:functionname>trackCpPageView</gsx:functionname><gsx:eventname>cp_page_view</gsx:eventname><gsx:props>course_id:Long,course_name:String</gsx:props></entry><entry><id>https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full/cpzh4</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>trackNavFeedTap</title><content type='text'>eventname: nav_feed_tap</content><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full/cpzh4'/><gsx:functionname>trackNavFeedTap</gsx:functionname><gsx:eventname>nav_feed_tap</gsx:eventname><gsx:props></gsx:props></entry><entry><id>https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full/cre1l</id><updated>2017-08-22T10:31:52.161Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/><title type='text'>trackCpViewScorecard</title><content type='text'>eventname: cp_view_scorecard, props: course_id:Long,course_name:String</content><link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/abc1234567890/od6/public/full/cre1l'/><gsx:functionname>trackCpViewScorecard</gsx:functionname><gsx:eventname>cp_view_scorecard</gsx:eventname><gsx:props>course_id:Long,course_name:String</gsx:props></entry></feed>
|
@@ -0,0 +1,135 @@
|
|
1
|
+
describe Evva::AndroidGenerator do
|
2
|
+
let(:generator) { described_class.new }
|
3
|
+
|
4
|
+
def trim_spaces(str)
|
5
|
+
str.gsub(/^[ \t]+/, '')
|
6
|
+
.gsub(/[ \t]+$/, '')
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#kotlin_function' do
|
10
|
+
subject { trim_spaces(generator.kotlin_function(event)) }
|
11
|
+
|
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 {
|
28
|
+
put("course_id", course_id)
|
29
|
+
put("course_name", course_name)
|
30
|
+
}
|
31
|
+
mixpanelMask.trackEvent(MixpanelEvent.CP_PAGE_VIEW, properties)
|
32
|
+
}
|
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 {
|
43
|
+
put("course_id", course_id)
|
44
|
+
put("course_name", course_name)
|
45
|
+
put("from_screen", from_screen.key)
|
46
|
+
}
|
47
|
+
mixpanelMask.trackEvent(MixpanelEvent.CP_PAGE_VIEW, properties)
|
48
|
+
}
|
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 {
|
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
|
84
|
+
end
|
85
|
+
|
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
|
97
|
+
}
|
98
|
+
it { should eq trim_spaces(expected) }
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#event_enum' do
|
102
|
+
subject { trim_spaces(generator.event_enum(event_bundle)) }
|
103
|
+
let(:event_bundle) { [
|
104
|
+
Evva::MixpanelEvent.new('nav_feed_tap', {}),
|
105
|
+
Evva::MixpanelEvent.new('nav_performance_tap', {})
|
106
|
+
] }
|
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
|
116
|
+
}
|
117
|
+
it { should eq trim_spaces(expected) }
|
118
|
+
end
|
119
|
+
|
120
|
+
describe '#people_properties' do
|
121
|
+
subject { trim_spaces(generator.people_properties(people_bundle)) }
|
122
|
+
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
|
132
|
+
}
|
133
|
+
it { should eq trim_spaces(expected) }
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
describe Evva::Config do
|
2
|
+
subject(:config) { Evva::Config.new(hash: hash) }
|
3
|
+
|
4
|
+
let(:hash) do
|
5
|
+
{
|
6
|
+
type: 'EvvaOS',
|
7
|
+
data_source: {
|
8
|
+
type: 'google_sheet',
|
9
|
+
sheet_id: 'abc1234567890'
|
10
|
+
},
|
11
|
+
out_path: 'clear/path/to/event',
|
12
|
+
event_file_name: 'event/file/name',
|
13
|
+
people_file_name: 'people/file/name',
|
14
|
+
event_enum_file_name: 'event/enum/file'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when hash is missing params' do
|
19
|
+
before { hash.delete(:type) }
|
20
|
+
it { expect { config }.to raise_error /missing keys/i }
|
21
|
+
end
|
22
|
+
|
23
|
+
its(:to_h) { should eq(hash) }
|
24
|
+
its(:type) { should eq('EvvaOS') }
|
25
|
+
its(:out_path) { should eq('clear/path/to/event') }
|
26
|
+
its(:event_file_name) { should eq('event/file/name') }
|
27
|
+
its(:people_file_name) { should eq('people/file/name') }
|
28
|
+
its(:event_enum_file_name) { should eq 'event/enum/file' }
|
29
|
+
|
30
|
+
describe '#data_source' do
|
31
|
+
subject(:data_source) { config.data_source }
|
32
|
+
|
33
|
+
it { should eq(type: 'google_sheet', sheet_id: 'abc1234567890') }
|
34
|
+
|
35
|
+
context 'when given an unknown type data source' do
|
36
|
+
before { hash[:data_source] = { type: 'i_dunno' } }
|
37
|
+
it { expect { config }.to raise_error /unknown data source type 'i_dunno'/i }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
describe Evva::DataSource do
|
2
|
+
subject(:dict) { Evva::DataSource.new(keys) }
|
3
|
+
|
4
|
+
describe '.initialize' do
|
5
|
+
let(:keys) { { 'event' => { 'kNo1' => 'text' } } }
|
6
|
+
it { expect { subject }.not_to raise_error }
|
7
|
+
|
8
|
+
context 'when created with invalid type parameters' do
|
9
|
+
let(:keys) { 'cenas' }
|
10
|
+
it { expect { subject }.to raise_error /expected Hash/ }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when created with an invalid json schema' do
|
14
|
+
let(:keys) { { 'event' => 'text' } }
|
15
|
+
it { expect { subject }.to raise_error /event.+expected Hash/ }
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when created with an invalid json schema' do
|
19
|
+
let(:keys) { { 'a-event' => { 'kNo1' => { 'wtvr' => 'text' } } } }
|
20
|
+
it { expect { subject }.to raise_error /a-event.+kNo1.+expected String/ }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when given empty translations' do
|
24
|
+
let(:keys) { { 'event' => { 'kNo1' => nil } } }
|
25
|
+
it { expect { subject }.not_to raise_error }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
describe Evva::GoogleSheet do
|
2
|
+
let(:sheet) { Evva::GoogleSheet.new(sheet_id) }
|
3
|
+
let(:sheet_id) { 'abc1234567890' }
|
4
|
+
let(:url_info) { "https://spreadsheets.google.com/feeds/worksheets/#{sheet_id}/public/full" }
|
5
|
+
let(:url_sheet) { "https://spreadsheets.google.com/feeds/list/#{sheet_id}/od6/public/full" }
|
6
|
+
let(:enum_sheet) { "https://spreadsheets.google.com/feeds/list/#{sheet_id}/osju1vh/public/full" }
|
7
|
+
let(:people_sheet) { "https://spreadsheets.google.com/feeds/list/#{sheet_id}/ojyi830/public/full" }
|
8
|
+
let(:file_info) { File.read('spec/fixtures/sample_public_info.html') }
|
9
|
+
let(:file_sheet) { File.read('spec/fixtures/sample_public_sheet.html') }
|
10
|
+
let(:enum_file) { File.read('spec/fixtures/sample_public_enums.html') }
|
11
|
+
let(:people_file) { File.read('spec/fixtures/sample_public_people_properties.html') }
|
12
|
+
|
13
|
+
describe '#events' do
|
14
|
+
subject(:events) { sheet.events }
|
15
|
+
before do
|
16
|
+
stub_request(:get, url_info).to_return(status: 200, body: file_info, headers: {})
|
17
|
+
stub_request(:get, url_sheet).to_return(status: 200, body: file_sheet, headers: {})
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when given a valid sheet' do
|
21
|
+
it do
|
22
|
+
expect { events }.not_to raise_error
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns an array with the corresponding events' do
|
26
|
+
expected = [Evva::MixpanelEvent.new('cp_page_view', { course_id: 'Long', course_name: 'String' }),
|
27
|
+
Evva::MixpanelEvent.new('nav_feed_tap', {}),
|
28
|
+
Evva::MixpanelEvent.new('cp_view_scorecard', { course_id: 'Long', course_name: 'String' })]
|
29
|
+
expect(events).to eq(expected)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when given an inexistent sheet' do
|
34
|
+
before { stub_request(:get, url_info).to_return(status: 400, body: 'Not Found', headers: {}) }
|
35
|
+
|
36
|
+
it do
|
37
|
+
expect { events }.to raise_error /Cannot access sheet/
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when given a private sheet' do
|
42
|
+
before { stub_request(:get, url_info).to_return(status: 302, body: '<HTML></HTML>', headers: {}) }
|
43
|
+
|
44
|
+
it do
|
45
|
+
expect { events }.to raise_error /Cannot access sheet/
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when url content is not XML' do
|
50
|
+
before { stub_request(:get, url_info).to_return(status: 200, body: 'This is not XML; { this: "is json" }', headers: {}) }
|
51
|
+
|
52
|
+
it do
|
53
|
+
expect { events }.to raise_error /Cannot parse. Expected XML/
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#enum_classes' do
|
59
|
+
subject(:enum_classes) { sheet.enum_classes }
|
60
|
+
let(:expected_enum) do
|
61
|
+
[
|
62
|
+
Evva::MixpanelEnum.new('PageViewSourceScreen', ['course_discovery','synced_courses','nearby','deal']),
|
63
|
+
Evva::MixpanelEnum.new('PremiumClickBuy', ['notes','hi_res_maps','whatever'])
|
64
|
+
]
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when given a valid sheet' do
|
68
|
+
before do
|
69
|
+
stub_request(:get, url_info).to_return(status: 200, body: file_info, headers: {})
|
70
|
+
stub_request(:get, enum_sheet).to_return(status: 200, body: enum_file, headers: {})
|
71
|
+
end
|
72
|
+
|
73
|
+
it do
|
74
|
+
expect { enum_classes }.not_to raise_error
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns an array with the corresponding events' do
|
78
|
+
expect(enum_classes).to eq expected_enum
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#people_properties' do
|
84
|
+
subject(:people_properties) { sheet.people_properties }
|
85
|
+
let(:expected_people_properties) do
|
86
|
+
[
|
87
|
+
'rounds_with_wear',
|
88
|
+
'total_friends'
|
89
|
+
]
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when given a valid sheet' do
|
93
|
+
before do
|
94
|
+
stub_request(:get, url_info).to_return(status: 200, body: file_info, headers: {})
|
95
|
+
stub_request(:get, people_sheet).to_return(status: 200, body: people_file, headers: {})
|
96
|
+
end
|
97
|
+
|
98
|
+
it do
|
99
|
+
expect { people_properties }.not_to raise_error
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'returns an array with the corresponding events' do
|
103
|
+
expect(people_properties).to eq expected_people_properties
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
describe Evva::Logger do
|
2
|
+
let(:summary) { Evva::Logger.summary }
|
3
|
+
let(:info) { summary[:info] }
|
4
|
+
let(:warn) { summary[:warn] }
|
5
|
+
let(:error) { summary[:error] }
|
6
|
+
|
7
|
+
context 'when logging to info' do
|
8
|
+
before { Evva::Logger.info 'msg' }
|
9
|
+
|
10
|
+
it 'returns the expected totals' do
|
11
|
+
expect(info).to eq 1
|
12
|
+
expect(warn).to eq 0
|
13
|
+
expect(error).to eq 0
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when logging to warn' do
|
18
|
+
before { Evva::Logger.warn 'msg' }
|
19
|
+
|
20
|
+
it 'returns the expected totals' do
|
21
|
+
expect(info).to eq 0
|
22
|
+
expect(warn).to eq 1
|
23
|
+
expect(error).to eq 0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when logging to error' do
|
28
|
+
before { Evva::Logger.error 'msg' }
|
29
|
+
|
30
|
+
it 'returns the expected totals' do
|
31
|
+
expect(info).to eq 0
|
32
|
+
expect(warn).to eq 0
|
33
|
+
expect(error).to eq 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
describe Object do
|
2
|
+
describe '#deep_symbolize' do
|
3
|
+
subject { obj.deep_symbolize }
|
4
|
+
|
5
|
+
context 'when object is an Hash' do
|
6
|
+
let(:obj) { { 'key1' => 'value1', key2: 'value2' } }
|
7
|
+
it { should eq(key1: 'value1', key2: 'value2') }
|
8
|
+
|
9
|
+
context 'with inner Hashes' do
|
10
|
+
before { obj['key2'] = { 'key3' => { 'key4' => 'value4' } } }
|
11
|
+
it { should eq(key1: 'value1', key2: { key3: { key4: 'value4' } }) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'when object is an Array' do
|
16
|
+
let(:obj) { ['value1'] }
|
17
|
+
it { should eq obj }
|
18
|
+
|
19
|
+
context 'with inner Hashes' do
|
20
|
+
let(:obj) { [{ 'key1' => 'value1' }, { 'key2' => 'value2' }] }
|
21
|
+
it { should eq([{ key1: 'value1' }, { key2: 'value2' }]) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when object is something else' do
|
26
|
+
let(:obj) { 'value1' }
|
27
|
+
it { should eq obj }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#validate_structure!' do
|
32
|
+
subject { obj.validate_structure!(structure) }
|
33
|
+
|
34
|
+
context 'when object type differs of the structure type' do
|
35
|
+
let(:obj) { 'a string' }
|
36
|
+
let(:structure) { { type: Integer } }
|
37
|
+
it { expect { subject }.to raise_error /expected Integer, got String/i }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when object is a Nil' do
|
41
|
+
let(:obj) { nil }
|
42
|
+
let(:structure) { { type: Integer } }
|
43
|
+
it { expect { subject }.to raise_error /expected Integer, got NilClass/i }
|
44
|
+
|
45
|
+
context 'but is also optional' do
|
46
|
+
before { structure[:optional] = true }
|
47
|
+
it { expect { subject }.not_to raise_error }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when object is an Array' do
|
52
|
+
let(:obj) { %w[elem1 elem2] }
|
53
|
+
let(:structure) { { type: Array } }
|
54
|
+
it { expect { subject }.not_to raise_error }
|
55
|
+
|
56
|
+
context 'given an element structure' do
|
57
|
+
before { structure[:elements] = { type: String } }
|
58
|
+
it { expect { subject }.not_to raise_error }
|
59
|
+
|
60
|
+
context 'which is not respected' do
|
61
|
+
before { structure[:elements] = { type: Integer } }
|
62
|
+
it { expect { subject }.to raise_error /expected Integer, got String/i }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when object is an Hash' do
|
68
|
+
let(:obj) { { key1: 'value1', key2: 'value2', key3: 'value3' } }
|
69
|
+
let(:structure) { { type: Hash } }
|
70
|
+
it { expect { subject }.not_to raise_error }
|
71
|
+
|
72
|
+
context 'when given an element structure' do
|
73
|
+
let(:structure) { { type: Hash, elements: { key1: { type: String }, key2: { type: String }, key3: { type: String } } } }
|
74
|
+
it { expect { subject }.not_to raise_error }
|
75
|
+
|
76
|
+
context 'which is not respected' do
|
77
|
+
before { obj[:key2] = {} }
|
78
|
+
it { expect { subject }.to raise_error /expected String, got Hash/i }
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when missing keys' do
|
82
|
+
before do
|
83
|
+
obj.delete(:key1)
|
84
|
+
obj.delete(:key3)
|
85
|
+
end
|
86
|
+
it { expect { subject }.to raise_error /missing keys: key1, key3/i }
|
87
|
+
|
88
|
+
context 'which are optional' do
|
89
|
+
before do
|
90
|
+
structure[:elements][:key1][:optional] = true
|
91
|
+
structure[:elements][:key3][:optional] = true
|
92
|
+
end
|
93
|
+
it { expect { subject }.not_to raise_error }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|