evva 0.1.4.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: eb97d0f1d8232f0bd8be466a5bb44d0d6969d620
4
- data.tar.gz: eafd1e8f81baa14886ba4fcb466ea18b59f9b199
2
+ SHA256:
3
+ metadata.gz: c9078e38ca6bba5b85a3349fb9e1c3f9c31b8de357080ffb1ab105e6bcbbdaa1
4
+ data.tar.gz: 16b264637125fafdc14a991a9cb984e440b948bdc7d934776329b8aa8ef2d89f
5
5
  SHA512:
6
- metadata.gz: 6f35827bcadcc08b17693f45d7b5106c75fddb277df50f837eb38e8ab10e06612d53ee550a37ad173e85ffa9e830a5b4debb2136c361dff0e4335c15054449b7
7
- data.tar.gz: eec447eb9229129b08415d30382e23fcab70b354a6512e906d756fe7c87ef734011fe2ba4da7cc09706ac00bcd2310e6047698f36e5eaafcb3be2d9521b9ef0d
6
+ metadata.gz: 6efc1abd4ea8d72f7ac2edeccbf111deac25922fa0b1464d754d21e7bfef921efffddd228bb6d6913f3b64a4acfcd8763082f0b36b16e85e28b930d933df879c
7
+ data.tar.gz: 77a4ad5df7074b646def8c880654b551fcde2e10523e3dec622d202fba48ac7bdda6d1a882abcd5c54bc2e6175b2bb68a52c30ab77d1c522f29362bc9a297db3
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  *.sublime-*
2
- .ruby-version
3
2
  running
4
3
  coverage
5
4
  .DS_Store*
6
- .byebug_history
5
+ .byebug_history
data/.rspec CHANGED
@@ -1,5 +1,4 @@
1
1
  --color
2
2
  --format documentation
3
3
  --profile
4
- --drb
5
4
  --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.7.3
data/.travis.yml ADDED
@@ -0,0 +1 @@
1
+ language: ruby
data/Gemfile CHANGED
@@ -1,13 +1,15 @@
1
1
  source 'https://rubygems.org'
2
- ruby '2.3.3'
2
+ ruby '2.7.3'
3
3
 
4
4
  gem 'colorize'
5
- gem 'rubocop'
6
5
  gem 'safe_yaml'
7
- gem 'xml-simple'
8
6
 
9
- gem 'rspec'
10
- gem 'rspec-its'
11
- gem 'simplecov', require: false, group: :test
12
- gem 'simplecov-rcov', require: false
13
- gem 'webmock', '~> 1.20'
7
+ group :test do
8
+ gem 'rake'
9
+ gem 'rspec'
10
+ gem 'rspec-its'
11
+ gem 'simplecov', require: false, group: :test
12
+ gem 'simplecov-rcov', require: false
13
+ gem 'webmock', '~> 1.20'
14
+ gem 'rubocop'
15
+ end
data/Gemfile.lock CHANGED
@@ -10,7 +10,7 @@ GEM
10
10
  diff-lcs (1.3)
11
11
  docile (1.1.5)
12
12
  hashdiff (0.3.7)
13
- json (2.1.0)
13
+ json (2.3.0)
14
14
  parallel (1.12.0)
15
15
  parser (2.4.0.0)
16
16
  ast (~> 2.2)
@@ -18,7 +18,7 @@ GEM
18
18
  public_suffix (3.0.0)
19
19
  rainbow (2.2.2)
20
20
  rake
21
- rake (12.1.0)
21
+ rake (12.3.3)
22
22
  rspec (3.7.0)
23
23
  rspec-core (~> 3.7.0)
24
24
  rspec-expectations (~> 3.7.0)
@@ -56,13 +56,13 @@ GEM
56
56
  addressable (>= 2.3.6)
57
57
  crack (>= 0.3.2)
58
58
  hashdiff
59
- xml-simple (1.1.5)
60
59
 
61
60
  PLATFORMS
62
61
  ruby
63
62
 
64
63
  DEPENDENCIES
65
64
  colorize
65
+ rake
66
66
  rspec
67
67
  rspec-its
68
68
  rubocop
@@ -70,10 +70,9 @@ DEPENDENCIES
70
70
  simplecov
71
71
  simplecov-rcov
72
72
  webmock (~> 1.20)
73
- xml-simple
74
73
 
75
74
  RUBY VERSION
76
- ruby 2.3.3p222
75
+ ruby 2.7.3p183
77
76
 
78
77
  BUNDLED WITH
79
- 1.15.4
78
+ 2.2.26
data/README.md CHANGED
@@ -1,4 +1,10 @@
1
1
  Evva
2
+ ========
3
+
4
+ [![Status](https://travis-ci.org/hole19/evva.svg?branch=master)](https://travis-ci.org/hole19/evva?branch=master)
5
+ [![Gem](https://img.shields.io/gem/v/evva.svg?style=flat)](http://rubygems.org/gems/evva "View this project in Rubygems")
6
+
7
+ Evva automatically generates code for triggering events based on a Google Sheets specification. It generated code for both iOS (Swift) and Android (Kotlin).
2
8
 
3
9
  # Instalation
4
10
 
@@ -19,9 +25,13 @@ Evva
19
25
  data_source:
20
26
  type: google_sheet
21
27
  sheet_id: <GOOGLE-DRIVE-SHEET-ID>
28
+ events_url: <GOOGLE-DRIVE-EVENTS-SHEET-URL>
29
+ people_properties_url: <GOOGLE-DRIVE-PEOPLE-PROPERTIES-SHEET-URL>
30
+ enum_classes_url: <GOOGLE-DRIVE-ENUM-CLASSES-SHEET-URL>
22
31
 
23
32
  out_path: /folder/where/analytics/classes/are
24
33
  event_file_name: /file/with/tracking/functions
25
34
  event_enum_file_name: /file/with/event/names
26
35
  people_file_name: /file/with/people/properties
36
+ special_enum_file_name: //file/with/special/enum/properties/
27
37
  ```
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "rake"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.pattern = Dir.glob("spec/**/*_spec.rb")
6
+ t.rspec_opts = "--format documentation"
7
+ end
8
+
9
+ task default: :spec
data/changelog.md CHANGED
@@ -1,4 +1,20 @@
1
1
  # Change Log
2
+
3
+ ## [0.2.0] - 2021/08/30
4
+ - Google Spreadsheet option stopped working due to a change in the API. This version fixes that.
5
+
6
+ Note: You'll need a new setup. View README.
7
+
8
+ ## [0.1.4.4] - 2019-02-04
9
+ - Adds support for dynamic android package name
10
+
11
+ ## [0.1.4.3] - 2018-10-12
12
+ - Fixes swift and kotlin tabs, indentation and property names
13
+ - Merges all special enums in a single file
14
+
15
+ ## [0.1.4.2] - 2018-02-14
16
+ - Replaces Swift headers
17
+
2
18
  ## [0.1.4.1] - 2018-02-08
3
19
  - DRYs methods in swift event generation
4
20
 
@@ -17,4 +33,5 @@
17
33
  - Fixes mismatch between file name and class name generated.
18
34
 
19
35
  ## [0.1.0] - 2017/10/26
20
- - Initial Release.
36
+ - Initial Release.
37
+
data/evva.gemspec CHANGED
@@ -18,7 +18,4 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_runtime_dependency 'safe_yaml', '~> 1.0'
20
20
  s.add_runtime_dependency 'colorize', '~> 0.7'
21
- s.add_runtime_dependency 'xml-simple', '~> 1.1'
22
-
23
- s.add_development_dependency 'webmock', '~> 1.20'
24
21
  end
data/evva_config.yml CHANGED
@@ -3,8 +3,12 @@ type: iOS
3
3
  data_source:
4
4
  type: google_sheet
5
5
  sheet_id: 1LaJd68os3g_GFlerogC64grNIlXb2iukMznOvdml7A4
6
+ events_url: https://path-to-csv
7
+ people_properties_url: https://path-to-csv
8
+ enum_classes_url: https://path-to-csv
6
9
 
7
10
  out_path: analytics
8
11
  event_file_name: MixpanelAnalytics
9
12
  event_enum_file_name: MixpanelEvent
10
- people_file_name: MixpanelProperties
13
+ people_file_name: MixpanelProperties
14
+ package_name: com.hole19golf.hole19.analytics
@@ -1,48 +1,78 @@
1
1
  module Evva
2
2
  class AndroidGenerator
3
- KOTLIN_EVENT_HEADER =
4
- "package com.hole19golf.hole19.analytics\n\n"\
5
- "import com.hole19golf.hole19.analytics.Event\n"\
6
- "import com.hole19golf.hole19.analytics.MixpanelAnalyticsMask\n"\
7
- "import org.json.JSONObject\n\n".freeze
8
-
9
- KOTLIN_PEOPLE_HEADER =
10
- "package com.hole19golf.hole19.analytics\n"\
11
- "import com.hole19golf.hole19.analytics.Event\n\n".freeze
12
-
13
- KOTLIN_BUNDLE_HEADER =
14
- "package com.hole19golf.hole19.analytics\n"\
15
- "import com.hole19golf.hole19.analytics.Event\n\n".freeze
16
-
17
- KOTIN_PEOPLE_FUNCTIONS =
18
- "\nopen fun updateProperties(property: MixpanelProperties, value: Any) {\n"\
19
- "\t\tmixpanelMask.updateProperties(property.key, value)"\
20
- "\t\n} \n"\
21
- "\nopen fun incrementCounter(property: MixpanelProperties) {\n"\
22
- "\t\tmixpanelMask.incrementCounter(property.key)"\
23
- "\t\n} \n".freeze
3
+ attr_accessor :package_name
4
+
5
+ def initialize(package_name)
6
+ @package_name = package_name
7
+ end
8
+
9
+ IMPORT_EVENT = "import packagename.Event".freeze
10
+ IMPORT_MASK = "import packagename.MixpanelAnalyticsMask".freeze
11
+ IMPORT_JSON = "import org.json.JSONObject".freeze
24
12
 
25
13
  NATIVE_TYPES = %w[Long Int String Double Float Boolean].freeze
26
14
 
27
15
  def events(bundle, file_name)
28
- event_file = KOTLIN_EVENT_HEADER + "open class #{file_name}(private val mixpanelMask: MixpanelAnalyticsMask) {\n".freeze
29
- bundle.each do |event|
30
- event_file += "\n#{kotlin_function(event)}"
16
+ header_footer_wrapper([IMPORT_EVENT, IMPORT_MASK, IMPORT_JSON]) do
17
+ """open class #{file_name}(private val mask: MixpanelAnalyticsMask) {
18
+
19
+ #{bundle.map { |e| kotlin_function(e) }.join("\n\n")}
20
+
21
+ \topen fun updateProperties(property: MixpanelProperties, value: Any) {
22
+ \t\tmask.updateProperties(property.key, value)
23
+ \t}
24
+
25
+ \topen fun incrementCounter(property: MixpanelProperties) {
26
+ \t\tmask.incrementCounter(property.key)
27
+ \t}
28
+ }"""
31
29
  end
32
- event_file += KOTIN_PEOPLE_FUNCTIONS
33
- event_file += "\n}"
34
30
  end
35
31
 
36
32
  def people_properties(people_bundle, file_name)
37
- properties = KOTLIN_PEOPLE_HEADER + "enum class #{file_name}(val key: String) {\n"
38
- properties += people_bundle.map { |prop| "\t\t#{prop.upcase}(\"#{prop}\")" }.join(",\n")
39
- properties += ";\n}\n"
33
+ header_footer_wrapper do
34
+ body = "enum class MixpanelProperties(val key: String) {\n"
35
+ body << people_bundle.map { |prop| "\t#{prop.upcase}(\"#{prop}\")" }.join(",\n")
36
+ body << ";\n}"
37
+ end
40
38
  end
41
39
 
42
40
  def event_enum(bundle, file_name)
43
- event_file = KOTLIN_BUNDLE_HEADER + "enum class #{file_name}(override val key: String) : Event {\n"
44
- event_file += bundle.map { |event| "\t\t#{event.event_name.upcase}(\"#{event.event_name}\")"}.join(", \n")
45
- event_file += "\n}\n"
41
+ header_footer_wrapper([IMPORT_EVENT]) do
42
+ body = "enum class #{file_name}(override val key: String) : Event {\n"
43
+ body << bundle.map(&:event_name).map { |prop| "\t#{prop.upcase}(\"#{prop}\")" }.join(",\n")
44
+ body << ";\n}"
45
+ end
46
+ end
47
+
48
+ def special_property_enums(enums)
49
+ header_footer_wrapper do
50
+ enums.map do |enum|
51
+ body = "enum class #{enum.enum_name}(val key: String) {\n"
52
+ body << enum.values.map { |vals| "\t#{vals.tr(' ', '_').upcase}(\"#{vals}\")"}.join(",\n")
53
+ body << ";\n}"
54
+ end.join("\n\n")
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def imports_header(imports = [])
61
+ return unless imports.length > 0
62
+ imports.map { |ev| ev. gsub("packagename", @package_name) }
63
+ .join("\n") + "\n\n"
64
+ end
65
+
66
+ def header_footer_wrapper(imports = [])
67
+ <<-Kotlin
68
+ package #{@package_name}
69
+
70
+ #{imports_header(imports)}/**
71
+ * This file was automatically generated by evva: https://github.com/hole19/evva
72
+ */
73
+
74
+ #{yield.gsub("\t", " ")}
75
+ Kotlin
46
76
  end
47
77
 
48
78
  def kotlin_function(event_data)
@@ -50,27 +80,18 @@ module Evva
50
80
  function_arguments = event_data.properties.map { |name, type| "#{name}: #{type}" }.join(', ')
51
81
  if !function_arguments.empty?
52
82
  props = json_props(event_data.properties)
53
- function_body =
54
- "open fun #{function_name}(#{function_arguments}) {"\
55
- "#{props}"\
56
- "\tmixpanelMask.trackEvent(MixpanelEvent.#{event_data.event_name.upcase}, properties)\n"
83
+ """\topen fun #{function_name}(#{function_arguments}) {
84
+ #{props}
85
+ \t\tmask.trackEvent(MixpanelEvent.#{event_data.event_name.upcase}, properties)
86
+ \t}"""
87
+
57
88
  else
58
- function_body =
59
- "open fun #{function_name}() {\n"\
60
- "\tmixpanelMask.trackEvent(MixpanelEvent.#{event_data.event_name.upcase})\n"
89
+ """\topen fun #{function_name}() {
90
+ \t\tmask.trackEvent(MixpanelEvent.#{event_data.event_name.upcase})
91
+ \t}"""
61
92
  end
62
- function_body += "}\n"
63
- end
64
-
65
- def special_property_enum(enum)
66
- enum_body = "package com.hole19golf.hole19.analytics\n\n"
67
- enum_body += "enum class #{enum.enum_name}(val key: String) {\n"
68
- enum_body += enum.values.map { |vals| "\t#{vals.tr(' ', '_').upcase}(\"#{vals}\")"}.join(",\n")
69
- enum_body += "\n}\n"
70
93
  end
71
94
 
72
- private
73
-
74
95
  def json_props(properties)
75
96
  split_properties =
76
97
  properties
@@ -89,12 +110,10 @@ module Evva
89
110
  end
90
111
  end
91
112
  end
92
- .map { |line| "\t\t#{line}" }
113
+ .map { |line| "\t\t\t#{line}" }
93
114
  .join("\n")
94
115
 
95
- resulting_json = "\n\tval properties = JSONObject().apply {\n" +
96
- +split_properties.to_s
97
- resulting_json += "\n\t}\n"
116
+ "\t\tval properties = JSONObject().apply {\n#{split_properties}\n\t\t}"
98
117
  end
99
118
 
100
119
  def special_property?(type)
data/lib/evva/config.rb CHANGED
@@ -39,6 +39,14 @@ module Evva
39
39
  @hash[:event_enum_file_name]
40
40
  end
41
41
 
42
+ def special_enum_file_name
43
+ @hash[:special_enum_file_name]
44
+ end
45
+
46
+ def package_name
47
+ @hash[:package_name]
48
+ end
49
+
42
50
  CONFIG_STRUCT = {
43
51
  type: Hash,
44
52
  elements: {
@@ -49,7 +57,8 @@ module Evva
49
57
  out_path: { type: String },
50
58
  event_file_name: { type: String },
51
59
  event_enum_file_name: { type: String },
52
- people_file_name: { type: String }
60
+ people_file_name: { type: String },
61
+ package_name: { type: String }
53
62
  }
54
63
  }.freeze
55
64
 
@@ -57,7 +66,9 @@ module Evva
57
66
  type: Hash,
58
67
  elements: {
59
68
  type: { type: String },
60
- sheet_id: { type: String }
69
+ events_url: { type: String },
70
+ people_properties_url: { type: String },
71
+ enum_classes_url: { type: String },
61
72
  }
62
73
  }.freeze
63
74
 
@@ -1,83 +1,86 @@
1
1
  require 'net/https'
2
- require 'xmlsimple'
2
+ require 'csv'
3
3
 
4
4
  module Evva
5
5
  class GoogleSheet
6
- def initialize(sheet_id)
7
- @sheet_id = sheet_id
6
+ def initialize(events_url, people_properties_url, enum_classes_url)
7
+ @events_url = events_url
8
+ @people_properties_url = people_properties_url
9
+ @enum_classes_url = enum_classes_url
8
10
  end
9
11
 
10
12
  def events
11
- raw = raw_data(@sheet_id, 0)
12
- Logger.info('Downloading dictionary from Google Sheet...')
13
- non_language_columns = %w[id updated category
14
- title content link]
13
+ Logger.info("Downloading data from Google Sheet at #{@events_url}")
14
+ csv = get_csv(@events_url)
15
+
15
16
  event_list = []
16
- raw['entry'].each do |entry|
17
- filtered_entry = entry.reject { |c| non_language_columns.include?(c) }
18
- event_name = filtered_entry['eventname'].first
19
- properties = hash_parser(filtered_entry['props'].first)
20
- event_list.push(Evva::MixpanelEvent.new(event_name, properties))
17
+ csv.each do |row|
18
+ event_name = row['Event Name']
19
+ properties = hash_parser(row['Event Properties'])
20
+ event_list << Evva::MixpanelEvent.new(event_name, properties)
21
21
  end
22
22
  event_list
23
23
  end
24
24
 
25
25
  def people_properties
26
- raw = raw_data(@sheet_id, 1)
26
+ Logger.info("Downloading data from Google Sheet at #{@people_properties_url}")
27
+ csv = get_csv(@people_properties_url)
28
+
27
29
  people_list = []
28
- Logger.info('Downloading dictionary from Google Sheet...')
29
- non_language_columns = %w[id updated category title content link]
30
- raw['entry'].each do |entry|
31
- filtered_entry = entry.reject { |c| non_language_columns.include?(c) }
32
- value = filtered_entry['value'].first
30
+ csv.each do |row|
31
+ value = row['Property Name']
33
32
  people_list << value
34
33
  end
35
34
  people_list
36
35
  end
37
36
 
38
37
  def enum_classes
39
- raw = raw_data(@sheet_id, 2)
40
- Logger.info('Downloading dictionary from Google Sheet...')
41
- non_language_columns = %w[id updated category title content link]
38
+ Logger.info("Downloading data from Google Sheet at #{@enum_classes_url}")
39
+ csv = get_csv(@enum_classes_url)
40
+
42
41
  enum_list = []
43
- raw['entry'].each do |entry|
44
- filtered_entry = entry.reject { |c| non_language_columns.include?(c) }
45
- enum_name = filtered_entry['enum'].first
46
- values = filtered_entry['values'].first.split(',')
47
- enum_list.push(Evva::MixpanelEnum.new(enum_name, values))
42
+ csv.each do |row|
43
+ enum_name = row['Enum Name']
44
+ values = row['Possible Values'].split(',')
45
+ enum_list << Evva::MixpanelEnum.new(enum_name, values)
48
46
  end
49
47
  enum_list
50
48
  end
51
49
 
52
- def xml_data(uri, headers = nil)
53
- uri = URI.parse(uri)
54
- http = Net::HTTP.new(uri.host, uri.port)
55
- http.use_ssl = true
56
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
57
- data = http.get(uri.path, headers)
58
- unless data.code.to_i == 200
59
- raise 'Cannot access sheet at #{uri} - HTTP #{data.code}'
60
- end
50
+ private
51
+
52
+ def get_csv(url)
53
+ data = get(url)
61
54
 
62
55
  begin
63
- XmlSimple.xml_in(data.body, 'KeyAttr' => 'name')
64
- rescue
65
- raise 'Cannot parse. Expected XML at #{uri}'
56
+ CSV.parse(data, headers: true)
57
+ rescue StandardError => e
58
+ raise "Cannot parse. Expected CSV at #{url}: #{e}"
66
59
  end
67
60
  end
68
61
 
69
- def raw_data(sheet_id, sheet_number)
70
- Logger.info('Downloading Google Sheet...')
71
- sheet = xml_data("https://spreadsheets.google.com/feeds/worksheets/#{sheet_id}/public/full")
72
- url = sheet['entry'][sheet_number]['link'][0]['href']
73
- xml_data(url)
74
- end
62
+ def get(url, max_redirects = 1)
63
+ raise "Too may redirects" if max_redirects == -1
75
64
 
76
- private
65
+ uri = URI(url)
66
+
67
+ http = Net::HTTP.new(uri.host, uri.port)
68
+ http.use_ssl = true
69
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
70
+
71
+ request = Net::HTTP::Get.new(uri.request_uri)
72
+ response = http.request(request)
73
+
74
+ return get(response['location'], max_redirects - 1) if response.is_a? Net::HTTPRedirection
75
+
76
+ raise "Http Error #{response.body}" if response.code.to_i >= 400
77
+
78
+ response.body
79
+ end
77
80
 
78
81
  def hash_parser(property_array)
79
82
  h = {}
80
- unless property_array.empty?
83
+ unless property_array.nil? || property_array.empty?
81
84
  property_array.split(',').each do |prop|
82
85
  split_prop = prop.split(':')
83
86
  prop_name = split_prop[0].to_sym