rimless 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.
data/Rakefile ADDED
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rails/code_statistics'
6
+ require 'pp'
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task default: :spec
11
+
12
+ # Load some railties tasks
13
+ load 'rails/tasks/statistics.rake'
14
+ load 'rails/tasks/annotations.rake'
15
+
16
+ # Clear the default statistics directory constant
17
+ #
18
+ # rubocop:disable Style/MutableConstant because we define it
19
+ Object.send(:remove_const, :STATS_DIRECTORIES)
20
+ ::STATS_DIRECTORIES = []
21
+ # rubocop:enable Style/MutableConstant
22
+
23
+ # Monkey patch the Rails +CodeStatistics+ class to support configurable
24
+ # patterns per path. This is reuqired to support top-level only file matches.
25
+ class CodeStatistics
26
+ DEFAULT_PATTERN = /^(?!\.).*?\.(rb|js|coffee|rake)$/.freeze
27
+
28
+ # Pass the possible +pattern+ argument down to the
29
+ # +calculate_directory_statistics+ method call.
30
+ def calculate_statistics
31
+ Hash[@pairs.map do |pair|
32
+ [pair.first, calculate_directory_statistics(*pair[1..-1])]
33
+ end]
34
+ end
35
+
36
+ # Match the pattern against the individual file name and the relative file
37
+ # path. This allows top-level only matches.
38
+ def calculate_directory_statistics(directory, pattern = DEFAULT_PATTERN)
39
+ stats = CodeStatisticsCalculator.new
40
+
41
+ Dir.foreach(directory) do |file_name|
42
+ path = "#{directory}/#{file_name}"
43
+
44
+ if File.directory?(path) && (/^\./ !~ file_name)
45
+ stats.add(calculate_directory_statistics(path, pattern))
46
+ elsif file_name =~ pattern || path =~ pattern
47
+ stats.add_by_file_path(path)
48
+ end
49
+ end
50
+
51
+ stats
52
+ end
53
+ end
54
+
55
+ # Configure all code statistics directories
56
+ vendors = [
57
+ # @TODO: Add your library structure here
58
+ # [:unshift, 'Clients', 'lib/hausgold/client'],
59
+ # [:unshift, 'Top-levels', 'lib', %r{lib(/hausgold)?/[^/]+\.rb$}],
60
+ # [:unshift, 'Clients specs', 'spec/client'],
61
+ # [:unshift, 'Top-levels specs', 'spec',
62
+ # %r{spec/rimless(_spec\.rb|/[^/]+\.rb$)}]
63
+ ].reverse
64
+
65
+ vendors.each do |method, type, dir, pattern|
66
+ ::STATS_DIRECTORIES.send(method, [type, dir, pattern].compact)
67
+ ::CodeStatistics::TEST_TYPES << type if type.include? 'specs'
68
+ end
69
+
70
+ # Setup annotations
71
+ ENV['SOURCE_ANNOTATION_DIRECTORIES'] = 'spec,doc'
72
+
73
+ desc 'Enumerate all annotations'
74
+ task :notes do
75
+ SourceAnnotationExtractor.enumerate '@?OPTIMIZE|@?FIXME|@?TODO', tag: true
76
+ end
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'rimless'
6
+ require 'pp'
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ # require "pry"
13
+ # Pry.start
14
+
15
+ require 'irb'
16
+ IRB.start(__FILE__)
data/bin/run ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'rimless'
6
+ require 'pp'
7
+
8
+ $stdout.sync = true
9
+
10
+ exit 1 if ARGV.empty?
11
+
12
+ ARGV.each { |cmd| eval(cmd); $stdout.flush }
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ if [ -f ~/.bashrc ]; then
2
+ . ~/.bashrc
3
+ fi
@@ -0,0 +1,49 @@
1
+ # ~/.bashrc: executed by bash(1) for non-login shells.
2
+ # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
3
+ # for examples
4
+
5
+ _GEM_PATHS=$(ls -d1 ${HOME}/.gem/ruby/*/bin 2>/dev/null | paste -sd ':')
6
+ _APP_PATHS=$(ls -d1 /app/vendor/bundle/ruby/*/bin 2>/dev/null | paste -sd ':')
7
+
8
+ export PATH="${_GEM_PATHS}:${_APP_PATHS}:${PATH}"
9
+ export PATH="/app/node_modules/.bin:${HOME}/.bin:/app/bin:${PATH}"
10
+
11
+ # Disable the autostart of all supervisord units
12
+ sudo sed -i 's/autostart=.*/autostart=false/g' /etc/supervisor/conf.d/*
13
+
14
+ # Start the supervisord (empty, no units)
15
+ sudo supervisord >/dev/null 2>&1 &
16
+
17
+ # Wait for supervisord
18
+ while ! supervisorctl status >/dev/null 2>&1; do sleep 1; done
19
+
20
+ # Boot the mDNS stack
21
+ echo '# Start the mDNS stack'
22
+ sudo supervisorctl start dbus avahi
23
+ echo
24
+
25
+ export MAKE_ENV=baremetal
26
+
27
+ function watch-make-test()
28
+ {
29
+ while [ 1 ]; do
30
+ inotifywait --quiet -r `pwd` -e close_write --format '%e -> %w%f'
31
+ make test
32
+ done
33
+ }
34
+
35
+ function watch-make()
36
+ {
37
+ while [ 1 ]; do
38
+ inotifywait --quiet -r `pwd` -e close_write --format '%e -> %w%f'
39
+ make $@
40
+ done
41
+ }
42
+
43
+ function watch-run()
44
+ {
45
+ while [ 1 ]; do
46
+ inotifywait --quiet -r `pwd` -e close_write --format '%e -> %w%f'
47
+ bash -c "$@"
48
+ done
49
+ }
@@ -0,0 +1,17 @@
1
+ # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving
2
+ "\e[1;5C": forward-word
3
+ "\e[1;5D": backward-word
4
+ "\e[5C": forward-word
5
+ "\e[5D": backward-word
6
+ "\e\e[C": forward-word
7
+ "\e\e[D": backward-word
8
+
9
+ # handle common Home/End escape codes
10
+ "\e[1~": beginning-of-line
11
+ "\e[4~": end-of-line
12
+ "\e[7~": beginning-of-line
13
+ "\e[8~": end-of-line
14
+ "\eOH": beginning-of-line
15
+ "\eOF": end-of-line
16
+ "\e[H": beginning-of-line
17
+ "\e[F": end-of-line
@@ -0,0 +1,68 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4
+ xmlns:cc="http://creativecommons.org/ns#"
5
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6
+ xmlns:svg="http://www.w3.org/2000/svg"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ version="1.1"
9
+ id="Ebene_1"
10
+ x="0px"
11
+ y="0px"
12
+ viewBox="0 0 800 200"
13
+ xml:space="preserve"
14
+ width="800"
15
+ height="200"><metadata
16
+ id="metadata33"><rdf:RDF><cc:Work
17
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
18
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
19
+ id="defs31" />
20
+ <style
21
+ type="text/css"
22
+ id="style2">
23
+ .st0{fill-rule:evenodd;clip-rule:evenodd;fill:#E73E11;}
24
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#0371B9;}
25
+ .st2{fill:#132E48;}
26
+ .st3{font-family:'OpenSans-Bold';}
27
+ .st4{font-size:29.5168px;}
28
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:none;}
29
+ .st6{opacity:0.5;fill:#132E48;}
30
+ .st7{font-family:'OpenSans';}
31
+ .st8{font-size:12px;}
32
+ </style>
33
+ <g
34
+ transform="translate(0,1.53584)"
35
+ id="g828"><g
36
+ transform="translate(35.93985,35.66416)"
37
+ id="g8">
38
+ <path
39
+ style="clip-rule:evenodd;fill:#e73e11;fill-rule:evenodd"
40
+ id="path4"
41
+ d="m -0.1,124.4 c 0,0 33.7,-123.2 66.7,-123.2 12.8,0 26.9,21.9 38.8,47.2 -23.6,27.9 -66.6,59.7 -94,76 -7.1,0 -11.5,0 -11.5,0 z"
42
+ class="st0" />
43
+ <path
44
+ style="clip-rule:evenodd;fill:#0371b9;fill-rule:evenodd"
45
+ id="path6"
46
+ d="m 88.1,101.8 c 13.5,-10.4 18.4,-16.2 27.1,-25.4 10,25.7 16.7,48 16.7,48 0,0 -41.4,0 -78,0 14.6,-7.9 18.7,-10.7 34.2,-22.6 z"
47
+ class="st1" />
48
+ </g><text
49
+ y="106.40316"
50
+ x="192.43155"
51
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:29.51733398px;font-family:'Open Sans', sans-serif;-inkscape-font-specification:'OpenSans-Bold, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#132e48"
52
+ id="text10"
53
+ class="st2 st3 st4">rimless</text>
54
+ <rect
55
+ style="clip-rule:evenodd;fill:none;fill-rule:evenodd"
56
+ id="rect12"
57
+ height="24"
58
+ width="314.5"
59
+ class="st5"
60
+ y="118.06416"
61
+ x="194.23985" /><text
62
+ y="127.22146"
63
+ x="194.21715"
64
+ style="font-size:12px;font-family:'Open Sans', sans-serif;opacity:0.5;fill:#132e48;-inkscape-font-specification:'Open Sans, sans-serif, Normal';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;"
65
+ id="text14"
66
+ class="st6 st7 st8">A bundle of opinionated Apache Kafka / Confluent Schema Registry helpers</text>
67
+ </g>
68
+ </svg>
@@ -0,0 +1,8 @@
1
+ version: "3"
2
+ services:
3
+ test:
4
+ build: .
5
+ network_mode: bridge
6
+ working_dir: /app
7
+ volumes:
8
+ - .:/app
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ gem 'activesupport', '~> 4.2.11'
8
+ gem 'railties', '~> 4.2.11'
9
+
10
+ gemspec path: '../'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ gem 'activesupport', '~> 5.0.7'
8
+ gem 'railties', '~> 5.0.7'
9
+
10
+ gemspec path: '../'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ gem 'activesupport', '~> 5.1.6'
8
+ gem 'railties', '~> 5.1.6'
9
+
10
+ gemspec path: '../'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ gem 'activesupport', '~> 5.2.2'
8
+ gem 'railties', '~> 5.2.2'
9
+
10
+ gemspec path: '../'
data/lib/rimless.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/concern'
5
+ require 'active_support/configurable'
6
+ require 'active_support/time'
7
+ require 'active_support/time_with_zone'
8
+ require 'active_support/core_ext/object'
9
+ require 'active_support/core_ext/module'
10
+ require 'active_support/core_ext/hash'
11
+ require 'active_support/core_ext/string'
12
+ require 'waterdrop'
13
+ require 'avro_turf/messaging'
14
+ require 'sparsify'
15
+ require 'erb'
16
+ require 'pp'
17
+
18
+ # The top level namespace for the rimless gem.
19
+ module Rimless
20
+ # Top level elements
21
+ autoload :Configuration, 'rimless/configuration'
22
+ autoload :ConfigurationHandling, 'rimless/configuration_handling'
23
+ autoload :AvroHelpers, 'rimless/avro_helpers'
24
+ autoload :AvroUtils, 'rimless/avro_utils'
25
+ autoload :KafkaHelpers, 'rimless/kafka_helpers'
26
+ autoload :Dependencies, 'rimless/dependencies'
27
+
28
+ # Load standalone code
29
+ require 'rimless/version'
30
+ require 'rimless/railtie' if defined? Rails
31
+
32
+ # Include top-level features
33
+ include Rimless::ConfigurationHandling
34
+ include Rimless::AvroHelpers
35
+ include Rimless::KafkaHelpers
36
+ include Rimless::Dependencies
37
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rimless
4
+ # The top-level Apache Avro helpers.
5
+ module AvroHelpers
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ # A top-level avro instance
10
+ mattr_accessor :avro
11
+
12
+ # The Apache Avro Ruby gem requires simple typed hashes for encoding.
13
+ # This forces us to convert eg. Grape entity representations into simple
14
+ # string-keyed hashes. Use this method to prepare a hash for the Apache
15
+ # Avro serialization.
16
+ #
17
+ # Note about the implementation: JSON serialization and parsing is the
18
+ # simplest and fastest way to accomplish this.
19
+ #
20
+ # @param hash [Hash{Mixed => Mixed}] the hash to sanitize
21
+ # @return [Hash{String => Mixed}] the simple typed input hash
22
+ def avro_to_h(hash)
23
+ JSON.parse(hash.to_json)
24
+ end
25
+ alias_method :avro_sanitize, :avro_to_h
26
+
27
+ # Convert the given deep hash into a sparsed flat hash while transforming
28
+ # all values to strings. This allows to convert a schema-less hash to a
29
+ # Apache Avro compatible map.
30
+ #
31
+ # @see http://avro.apache.org/docs/current/spec.html#Maps
32
+ # @example Convert schema-less hash
33
+ # avro_schemaless_map(a: { b: { c: true } })
34
+ # # => { "a.b.c" => "true" }
35
+ #
36
+ # @param hash [Hash{Mixed => Mixed}] the deep hash
37
+ # @return [Hash{String => String}] the flatted and sparsed hash
38
+ def avro_schemaless_h(hash)
39
+ Sparsify(hash, sparse_array: true)
40
+ .transform_values(&:to_s)
41
+ .transform_keys { |key| key.delete('\\') }
42
+ end
43
+ alias_method :avro_schemaless_map, :avro_schemaless_h
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rimless
4
+ # Due to dynamic contrains on the Apache Avro schemas we need to compile our
5
+ # schema templates to actual ready-to-consume schemas. The namespace part of
6
+ # the schemas and cross-references to other schemas must be rendered
7
+ # according to the dynamic namespace prefix which reflects the application
8
+ # environment. Unfortunately we need to mess around with actual files to
9
+ # support the Avro and AvroTurf gems.
10
+ class AvroUtils
11
+ attr_reader :namespace, :env
12
+
13
+ # Create a new instance of the +AvroUtil+ class.
14
+ #
15
+ # @return [AvroUtil] the new instance
16
+ def initialize
17
+ @namespace = ENV.fetch('KAFKA_SCHEMA_SUBJECT_PREFIX',
18
+ Rimless.topic_prefix).tr('-', '_').gsub(/\.$/, '')
19
+ @env = @namespace.split('.').first
20
+ end
21
+
22
+ # Clean and recompile all templated Avro schema files to their respective
23
+ # output path.
24
+ def recompile_schemas
25
+ clear
26
+ Dir[base_path.join('**', '*.erb')].each { |src| render_file(src) }
27
+ end
28
+
29
+ # Render (compile) a single Avro schema template. The given source file
30
+ # path will serve to calculate the destination path. So even deep path'ed
31
+ # templates will keep their hierarchy.
32
+ #
33
+ # @param src [String] the Avro schema template file path
34
+ def render_file(src)
35
+ # Convert the template path to the destination path
36
+ dest = schema_path(src)
37
+ # Create the deep path when not yet existing
38
+ FileUtils.mkdir_p(File.dirname(dest))
39
+ # Write the rendered file contents to the destination
40
+ File.write(dest, ERB.new(File.read(src)).result(binding))
41
+ # Check the written file for correct JSON
42
+ validate_file(dest)
43
+ end
44
+
45
+ # Check the given file for valid JSON.
46
+ #
47
+ # @param dest [Pathname, File, IO] the file to check
48
+ # @raise [JSON::ParserError] when invalid
49
+ #
50
+ # rubocop:disable Security/JSONLoad because we wrote the file contents
51
+ def validate_file(dest)
52
+ JSON.load(dest)
53
+ rescue JSON::ParserError => err
54
+ path = File.expand_path(dest.is_a?(File) ? dest.path : dest.to_s)
55
+ prefix = "Invalid JSON detected: #{path}"
56
+ Rimless.logger.fatal("#{prefix}\n#{err.message}")
57
+ err.message.prepend("#{prefix} - ")
58
+ raise err
59
+ end
60
+ # rubocop:enable Security/JSONLoad
61
+
62
+ # Clear previous compiled Avro schema files to provide a clean rebuild.
63
+ def clear
64
+ FileUtils.rm_rf(output_path)
65
+ FileUtils.mkdir_p(output_path)
66
+ end
67
+
68
+ # Return the compiled Avro schema file path for the given Avro schema
69
+ # template.
70
+ #
71
+ # @param src [String] the Avro schema template file path
72
+ # @return [Pathname] the resulting schema file path
73
+ def schema_path(src)
74
+ # No trailing dot on the prefix namespace directory
75
+ prefix = env.remove(/\.$/)
76
+ # Calculate the destination path based on the source file
77
+ Pathname.new(src.gsub(/^#{base_path}/, output_path.join(prefix).to_s)
78
+ .gsub(/\.erb$/, ''))
79
+ end
80
+
81
+ # Return the base path of the Avro schemas on our project.
82
+ #
83
+ # @return [Pathname] the Avro schemas base path
84
+ def base_path
85
+ Rimless.configuration.avro_schema_path
86
+ end
87
+
88
+ # Return the path to the compiled Avro schemas. This path must be consumed
89
+ # by the +AvroTurf::Messaging+ constructor.
90
+ #
91
+ # @return [Pathname] the compiled Avro schemas path
92
+ def output_path
93
+ Rimless.configuration.compiled_avro_schema_path
94
+ end
95
+ end
96
+ end