evva 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|