scarpe-components 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +35 -0
  3. data/Rakefile +12 -0
  4. data/lib/scarpe/logger.rb +108 -0
  5. metadata +51 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d2033509feb830b1bc7fa18f1275c7e1735e7ef7212808d0c4e3618873016b23
4
+ data.tar.gz: 1c8f7bf639f4e4b00f578263ea9be8a2be4b1773aa002457f50866e89159ad73
5
+ SHA512:
6
+ metadata.gz: 7afb7e7b1e63029d673d3274f0c7b5cbe00871d2af4178863d2211f375eb222b65572adae625252da985b341c5df7e2c671df4c184b3f05fde2484a567199a72
7
+ data.tar.gz: 6b4b76d33119f9a1688d6614e06c07ac63fca5a9741f4a627ced93ed10bf5ff22e8d110a844f6a7a9c6badbcc88d80e709eb9cc5c51534997ff18dccee308825
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Scarpe-Components
2
+
3
+ Scarpe is several things. We package up the Shoes API with as few implementation details as possible (called Lacci.) We have a Webview-based display library, in both local and over-a-socket (relay) versions. We have a Wasm display library (Scarpe-Wasm) in a separate gem.
4
+
5
+ And we have various default implementations of different reusable components. Scarpe's hierarchical logger isn't particularly specific to your display library or underlying GUI library, but we'd like it to be usable by multiple display libs. CatsCradle is only useful if you're trying to fix up an impedance mismatch between Ruby and an evented under-layer, but it's not really specific to Webview. Scarpe's default downloader, using Ruby's built-in HTTP libraries, is good in many cases but you might want to replace it (e.g. with Typhoeus for better parallel downloads or Hystrix for robustness to bad network connections) in some cases.
6
+
7
+ Part of our solution is the Scarpe-Components gem, which lives in the same repository as Scarpe (for now?). These components can live there. Any specific display library can pick and choose what it wants or needs and handle its dependencies as it sees fit.
8
+
9
+ ## Dependency Hell
10
+
11
+ A "gem full of optional reusable components" presents an awkward challenge for Rubygems. What are the gem's dependencies? Do you make it depend on every possible library, requiring FastImage and Nokogiri and SQLite and whatever else any component might ever require? That would be as bad as making ActiveRecord need MySQL and SQLite and Postgres and Oracle and... But if you don't give it *any* dependencies, you're going to have a harsh surprise at runtime when Bundler can't find any of the gems you need.
12
+
13
+ You can break everything up into tiny pieces, like ActiveRecord having a separate adapter for certain databases. You could do the same with an activerecord-mysql and activerecord-postgres and activerecord-sqlite gem to complete the set. Or, like, ActiveRecord, you can say "declare activerecord as a dependency, and also one or more other database gems, and we'll figure out what's available at runtime." That approach can be pretty fragile and it adds extra complexity -- how do you make sure everything is required at the right time and ActiveRecord can find everything that's available? How do you balance gem dependencies that are built into ActiveRecord with those for unusual databases that get added by other gems, later? Your plugin system (like ActiveRecord's) can get extensive and fragile.
14
+
15
+ Scarpe-Components kicks that problem down the road to the display libraries. Would a particular display library like to use the FastImage-based image implementation? Great! It can declare a dependency on the FastImage gem. Would it like to optionally allow the built-in Ruby downloader, but also have an optional robustified version using RestClient? Lovely. It can either create multiple gems (e.g. scarpe-gtk-rcdownload and scarpe-gtk-plaindownload) or look for RestClient being available at runtime, as it pleases. The FastImage implementation lives in Scarpe-Components, but the dependency is declared by the display library or the app.
16
+
17
+ ## Using an Implementation
18
+
19
+ Components in Scarpe-Components are designed to be used individually. They can require each other, but they should only require components they will actually use. The whole library is designed to be used a la carte, and normally no display service will use every component.
20
+
21
+ A display library will declare scarpe-components as a dependency. Then, usually from its named require file (e.g. wv_local.rb, wv_relay.rb, wasm_local.rb) it will set up those dependencies by requiring components that it wants and if necessary creating and configuring them. That way a specific display service (e.g. wv_local) can require a specific component (e.g. Logging-gem-based hierarchical logging) and configure it how it wants (e.g. ENV var pointing to a local JSON config file.)
22
+
23
+ ## How is this Different from Lacci?
24
+
25
+ Scarpe already has a gem full of reusable components, one that every Scarpe-based application already has to use. Lacci declares the Shoes API, and is 100% required for every Scarpe-based Shoes application everywhere.
26
+
27
+ So how do you tell what goes into Scarpe-Components vs what goes into Lacci?
28
+
29
+ Lacci is, at heart, an API with as little implementation as possible. It declares the Shoes GUI objects, but not how to display them. Ordinarily a Shoes application will require (in the sense of Kernel#require) every part of Lacci -- it will load the entire library. Even if we add optional components (e.g. a Lacci-based Bloops API with no implementation behind it), those components will be loaded by every Shoes app that uses that part of the API.
30
+
31
+ As a result, Lacci should have minimal (preferably zero) dependencies. Any code doing "real work" should be removed from Lacci completely, as soon as possible. Since *every* Shoes app is going to require Lacci, it should be possible to use it with essentially no dependencies, minimal memory footprint, minimal load time. It is a skeleton to hang functionality on, not a thing that functions for itself.
32
+
33
+ Scarpe-Components is a grab bag of default implementations, intended to be replaceable. But each of them does something. Most of them have dependencies. It's fine for a Scarpe-Component implementation to depend on the Logging gem, provided it says it does. Nokogiri? 100% fair. Does it do something with a nontrivial amount of computation, like rendering HTML output? No problem. This is very different from Lacci.
34
+
35
+ If a component should be reused, it's probably fine to put it into Scarpe-Components. It *might* be fine to put it in Lacci. For Scarpe-Components, ask yourself, "will more than one Scarpe display service possibly want to use this?" For Lacci, the test is more strict: "will *every* Scarpe display service want to use this? Does it have no dependencies at all, and do very little computation and take very little memory?"
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ task default: [:test]
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logging"
4
+ require "json"
5
+
6
+ require "shoes/log"
7
+
8
+ # Requirements: logging gem
9
+
10
+ class Scarpe
11
+ class LogImpl
12
+ include Shoes::Log # for constants
13
+
14
+ def logger_for_component(component)
15
+ Logging.logger[component]
16
+ end
17
+
18
+ private
19
+
20
+ def name_to_severity(data)
21
+ case data
22
+ when "debug"
23
+ :debug
24
+ when "info"
25
+ :info
26
+ when "warn"
27
+ :warn
28
+ when "err", "error"
29
+ :error
30
+ when "fatal"
31
+ :fatal
32
+ else
33
+ raise "Don't know how to treat #{data.inspect} as a logger severity!"
34
+ end
35
+ end
36
+
37
+ def json_to_appender(data)
38
+ case data.downcase
39
+ when "stdout"
40
+ Logging.appenders.stdout layout: @custom_log_layout
41
+ when "stderr"
42
+ Logging.appenders.stderr layout: @custom_log_layout
43
+ when String
44
+ Logging.appenders.file data, layout: @custom_log_layout
45
+ else
46
+ raise "Don't know how to convert #{data.inspect} to an appender!"
47
+ end
48
+ end
49
+
50
+ def json_configure_logger(logger, data)
51
+ case data
52
+ in String
53
+ sev = name_to_severity(data)
54
+ logger.level = sev
55
+ in [level, *locations]
56
+ if logger.name != "root"
57
+ # The Logging gem doesn't have an additive property on the root logger
58
+ logger.additive = false # Don't also log to parent/root loggers
59
+ end
60
+
61
+ logger.appenders = locations.map { |where| json_to_appender(where) }
62
+
63
+ logger.level = name_to_severity(level)
64
+ else
65
+ raise "Don't know how to use #{data.inspect} to specify a logger!"
66
+ end
67
+ end
68
+
69
+ def freeze_log_config(log_config)
70
+ log_config.each do |k, v|
71
+ k.freeze
72
+ v.freeze
73
+ v.each(&:freeze) if v.is_a?(Array)
74
+ end
75
+ log_config.freeze
76
+ end
77
+
78
+ public
79
+
80
+ def configure_logger(log_config)
81
+ # TODO: custom coloring? https://github.com/TwP/logging/blob/master/examples/colorization.rb
82
+ @custom_log_layout = Logging.layouts.pattern pattern: '[%r] %-5l %c: %m\n'
83
+
84
+ if log_config.is_a?(String) && File.exist?(log_config)
85
+ log_config = JSON.load_file(log_config)
86
+ end
87
+
88
+ log_config = freeze_log_config(log_config) unless log_config.nil?
89
+ @current_log_config = log_config # Save a copy for later
90
+
91
+ Logging.reset # Reset all Logging settings to defaults
92
+ Logging.reopen # For log-reconfig (e.g. test failures), often important to *not* store an open handle to a moved file
93
+ return if log_config.nil?
94
+
95
+ Logging.logger.root.appenders = [Logging.appenders.stdout]
96
+
97
+ default_logger = log_config[DEFAULT_COMPONENT] || "info"
98
+ json_configure_logger(Logging.logger.root, default_logger)
99
+
100
+ log_config.each do |component, logger_data|
101
+ next if component == DEFAULT_COMPONENT
102
+
103
+ sublogger = Logging.logger[component]
104
+ json_configure_logger(sublogger, logger_data)
105
+ end
106
+ end
107
+ end
108
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scarpe-components
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Marco Concetto Rudilosso
8
+ - Noah Gibbs
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2023-08-08 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email:
16
+ - marcoc.r@outlook.com
17
+ - the.codefolio.guy@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - README.md
23
+ - Rakefile
24
+ - lib/scarpe/logger.rb
25
+ homepage: https://github.com/scarpe-team/scarpe
26
+ licenses:
27
+ - MIT
28
+ metadata:
29
+ homepage_uri: https://github.com/scarpe-team/scarpe
30
+ source_code_uri: https://github.com/scarpe-team/scarpe
31
+ changelog_uri: https://github.com/scarpe-team/scarpe/blob/main/CHANGELOG.md
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 3.2.0
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubygems_version: 3.4.1
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: Reusable components for Scarpe display libraries
51
+ test_files: []