Class: Everywhere::Config
- Inherits:
-
Object
- Object
- Everywhere::Config
- Defined in:
- lib/everywhere/config.rb
Overview
Loads config/everywhere.yml:
app:
name: My Really Awesome App
entry_path: /dashboard # initial url on app launch
appearance:
tint_color: "#CC342D" # a hex string...
background_color: # ...or split by system theme
light: "#FAF9F7"
dark: "#1C1B1A"
Colors always normalize to { "light" => ..., "dark" => ... }.
Constant Summary collapse
- FILE =
File.join("config", "everywhere.yml")
- DEFAULT_TAB_ICONS =
{ "ios" => "circle", "android" => "circle" }.freeze
- MOBILE_PATH_RULES =
The Hotwire Native path-configuration rules every RubyEverywhere mobile shell starts from. Single source of truth: the builder bakes this into the app bundle and MobileConfigEndpoint serves it live.
[ { "patterns" => [".*"], "properties" => { "context" => "default", "pull_to_refresh_enabled" => true } }, { "patterns" => ["/new$", "/edit$"], "properties" => { "context" => "modal", "pull_to_refresh_enabled" => false } } ].freeze
- MOBILE_PERMISSIONS =
Native permissions the app declares (top-level
permissions:). Only declared permissions can be requested at runtime — the shell answersundeclaredfor everything else, so an app that doesn't need a permission can never prompt for it. For camera and location the value is the user-facing usage string iOS shows in the permission prompt (the "why"); it's mandatory there because requesting without one crashes the app. Notifications need no string — declare withtrue.permissions: notifications: true camera: "Scan QR codes to pair devices." location: ios: "Find build agents near you." biometrics: "Unlock your account with Face ID."Returns { name => { "ios" => usage-or-nil, ... } }.
%w[notifications camera location biometrics].freeze
- IOS_USAGE_KEYS =
Info.plist usage-description keys per permission. Presence here makes the usage string mandatory on iOS.
{ "camera" => "NSCameraUsageDescription", "location" => "NSLocationWhenInUseUsageDescription", "biometrics" => "NSFaceIDUsageDescription" }.freeze
- PACKAGE_REQUIREMENT_KEYS =
Third-party Swift packages this app pins for its native/ios code. Each entry is one SPM dependency;
every buildwrites them into the shell's local NativeExtensions package and re-exports every product, so native/ios code reaches them with a singleimport NativeExtensions.packages: - url: https://github.com/simibac/ConfettiSwiftUI from: "1.1.0" # or exact: / branch: / revision: products: [ConfettiSwiftUI] # optional; defaults to the repo nameReturns normalized entries: { "url", "requirement" => { "kind", "value" }, "products" => [...] }. Assumes the config validated clean (see native_ios_package_errors) — the requirement is always present here.
%w[from exact branch revision].freeze
- SWIFT_TYPE_NAME =
Everything declared here is interpolated into generated Swift, so names are validated hard: type names must be plain Swift identifiers, screen identifiers must be safe inside a Swift string literal.
/\A[A-Za-z_][A-Za-z0-9_]*\z/- SCREEN_IDENTIFIER =
/\A[A-Za-z0-9_-]+\z/- PACKAGE_URL =
A URL SPM accepts (https for the common case, git@ for SSH remotes) and a loose semver for from:/exact: — enough to catch typos without reimplementing SPM's resolver, which reports anything subtler at build time.
%r{\A(https?://|git@).+}- PACKAGE_VERSION =
/\A\d+(\.\d+){0,2}(-[0-9A-Za-z.-]+)?\z/
Instance Attribute Summary collapse
-
#root ⇒ Object
readonly
Returns the value of attribute root.
Class Method Summary collapse
- .load(root = Dir.pwd) ⇒ Object
-
.os_of(target) ⇒ Object
Every target (os-arch string) resolves to a single platform (its os), and a platform may override the app-level identity/version keys — see
platforms:below.
Instance Method Summary collapse
- #background_color ⇒ Object
-
#build ⇒ Object
The
build:section — durable build knobs that were onceevery buildflags (ruby, targets, permissions). -
#build_ruby ⇒ Object
Ruby version to package.
-
#bundle_id(target: nil) ⇒ Object
Reverse-DNS identifier: macOS bundle id, and the name of the per-user app-data directory on every platform.
- #entry_path ⇒ Object
-
#icon ⇒ Object
App icon source: a PNG (ideally square, 1024px+).
-
#initialize(data, root) ⇒ Config
constructor
A new instance of Config.
-
#menu ⇒ Object
Custom native menu items (macOS app menu).
-
#mobile_rules ⇒ Object
App-declared Hotwire Native path-configuration rules (everywhere.yml top-level
rules:), appended after MOBILE_PATH_RULES — later rules win per property, so apps can make routes modal, disable pull-to-refresh, or set any other Hotwire path property without touching native code:. -
#mode ⇒ Object
"local" — the app is tebako-pressed and runs on-device (default) "remote" — thin shell around an already-deployed app: no press, no sidecar.
- #name(target: nil) ⇒ Object
-
#native_ios ⇒ Object
Native code extensions ("supernative"): Swift the app repo carries in native/ios/ that
every buildcompiles into the shell, plus this declaration of what to hook up. - #native_ios? ⇒ Boolean
- #native_ios_components ⇒ Object
- #native_ios_errors ⇒ Object
-
#native_ios_lazy_load_tabs ⇒ Object
Whether the native tab bar defers each non-selected tab's initial visit until the tab is first selected (Hotwire Native
lazyLoadTabs). - #native_ios_package_errors ⇒ Object
- #native_ios_packages ⇒ Object
- #native_ios_packages? ⇒ Boolean
- #native_ios_screens ⇒ Object
- #native_ios_splash ⇒ Object
-
#native_ios_splash_min_seconds ⇒ Object
Minimum seconds a custom splash stays on screen (default 1.0 shell-side; a fast server would otherwise dismiss a branded splash in a ~50ms flash).
-
#package_identity(url) ⇒ Object
SPM's package identity: the URL's last path segment without a .git suffix (github.com/simibac/ConfettiSwiftUI → "ConfettiSwiftUI").
-
#path_configuration_hash(os, tabs: nil) ⇒ Object
The full path-configuration document for one mobile platform — rules plus our settings (tabs live in settings, per Hotwire Native convention).
- #path_configuration_json(os, tabs: nil) ⇒ Object
-
#permission_errors(os) ⇒ Object
Problems with the permissions declaration for one platform, as human-readable strings — unknown names, and camera/location missing the mandatory usage string.
-
#permissions ⇒ Object
OS-integration permissions the app declares.
- #remote? ⇒ Boolean
-
#remote_instances? ⇒ Boolean
Multi-instance apps (
remote.instances: true): the mobile shell boots into remote.url — a hosted instance-picker page — and lets that page re-root the app onto a chosen instance via Everywhere.instance.set, persisted across launches until Everywhere.instance.clear. - #remote_url ⇒ Object
-
#splash ⇒ Object
Optional custom splash page (HTML file, path relative to the app root).
-
#tabs ⇒ Object
Mobile tab bar, platform-neutral.
-
#tabs_for(os) ⇒ Object
Tabs with the icon resolved for one platform: icons.
, then the shared icon, then a safe default. -
#targets ⇒ Object
Build targets (os-arch strings) — the CI matrix, expressed once.
- #tint_color ⇒ Object
-
#to_shell_hash(target: nil) ⇒ Object
The subset the shell needs, shipped as JSON (env var in dev, Resources/everywhere.json inside a bundled .app).
- #to_shell_json(target: nil) ⇒ Object
-
#tray ⇒ Object
System tray: same entry shape as
menu:plusaction: quit | show. -
#updates ⇒ Object
The
updates:section — self-hosted auto-updates. -
#updates_auto ⇒ Object
off — never check; check — check + notify (default); download — also fetch/verify in the background; install — silent auto-update.
- #updates_channel ⇒ Object
- #updates_interval ⇒ Object
- #updates_public_key ⇒ Object
- #updates_s3 ⇒ Object
-
#updates_shell_hash ⇒ Object
The client subset for the shell; nil (and thus absent from everywhere.json) until both url and public_key are configured — an unsigned update feed is not a thing.
- #updates_url ⇒ Object
-
#version(target: nil) ⇒ Object
Marketing/display version of the app (CFBundleShortVersionString, and the
versionrecorded in the build receipt).
Constructor Details
Instance Attribute Details
#root ⇒ Object (readonly)
Returns the value of attribute root.
83 84 85 |
# File 'lib/everywhere/config.rb', line 83 def root @root end |
Class Method Details
.load(root = Dir.pwd) ⇒ Object
77 78 79 80 81 |
# File 'lib/everywhere/config.rb', line 77 def self.load(root = Dir.pwd) path = File.join(root, FILE) data = File.exist?(path) ? YAML.safe_load_file(path, aliases: true) || {} : {} new(data, root) end |
.os_of(target) ⇒ Object
Every target (os-arch string) resolves to a single platform (its os), and
a platform may override the app-level identity/version keys — see
platforms: below. Pass target: to any of these accessors to apply the
matching override; omit it for the shared app-level value.
94 |
# File 'lib/everywhere/config.rb', line 94 def self.os_of(target) = target.to_s.split("-", 2).first |
Instance Method Details
#background_color ⇒ Object
218 219 220 |
# File 'lib/everywhere/config.rb', line 218 def background_color normalize_color(appearance["background_color"]) end |
#build ⇒ Object
The build: section — durable build knobs that were once every build
flags (ruby, targets, permissions). CLI flags still override per-run.
See platform/docs/build-engine.md §2.
144 |
# File 'lib/everywhere/config.rb', line 144 def build = @data.fetch("build", nil) || {} |
#build_ruby ⇒ Object
Ruby version to package. nil here means "let the CLI decide its default".
147 |
# File 'lib/everywhere/config.rb', line 147 def build_ruby = build["ruby"] |
#bundle_id(target: nil) ⇒ Object
Reverse-DNS identifier: macOS bundle id, and the name of the per-user
app-data directory on every platform. Set it explicitly and never change
it — renaming moves users' data. A platform may override it (e.g. the App
Store often wants com.example.app.ios).
104 105 106 107 |
# File 'lib/everywhere/config.rb', line 104 def bundle_id(target: nil) resolved(target)["bundle_id"] || "com.rubyeverywhere.#{name(target: target).downcase.gsub(/[^a-z0-9]+/, "-").gsub(/\A-|-\z/, "")}" end |
#entry_path ⇒ Object
128 129 130 131 |
# File 'lib/everywhere/config.rb', line 128 def entry_path path = app["entry_path"] || "/" path.start_with?("/") ? path : "/#{path}" end |
#icon ⇒ Object
App icon source: a PNG (ideally square, 1024px+). Explicit app.icon
path relative to the app root, or icon.png at the root by convention.
111 112 113 114 115 116 117 118 |
# File 'lib/everywhere/config.rb', line 111 def icon if (explicit = app["icon"]) File.(explicit, root) else default = File.join(root, "icon.png") File.exist?(default) ? default : nil end end |
#menu ⇒ Object
Custom native menu items (macOS app menu). Each entry: label + path (+ optional accelerator), or "separator". Clicks arrive in the page as "everywhere:menu" events and navigate via Turbo.
520 521 522 523 524 525 526 527 528 529 530 531 532 |
# File 'lib/everywhere/config.rb', line 520 def entries = @data["menu"] return [] unless entries.is_a?(Array) entries.filter_map do |item| if item == "separator" || (item.is_a?(Hash) && item["separator"]) { "separator" => true } elsif item.is_a?(Hash) && item["label"] && item["path"] { "label" => item["label"], "path" => item["path"], "accelerator" => item["accelerator"] }.compact end end end |
#mobile_rules ⇒ Object
App-declared Hotwire Native path-configuration rules (everywhere.yml
top-level rules:), appended after MOBILE_PATH_RULES — later rules win
per property, so apps can make routes modal, disable pull-to-refresh,
or set any other Hotwire path property without touching native code:
rules:
- patterns: ["/preferences$"]
properties:
context: default # not a modal, unlike other /edit routes
- patterns: ["/live/"]
properties:
pull_to_refresh_enabled: false
342 343 344 345 346 347 348 349 350 351 |
# File 'lib/everywhere/config.rb', line 342 def mobile_rules entries = @data["rules"] return [] unless entries.is_a?(Array) entries.filter_map do |rule| next unless rule.is_a?(Hash) && rule["patterns"].is_a?(Array) && rule["properties"].is_a?(Hash) { "patterns" => rule["patterns"].map(&:to_s), "properties" => rule["properties"] } end end |
#mode ⇒ Object
"local" — the app is tebako-pressed and runs on-device (default) "remote" — thin shell around an already-deployed app: no press, no sidecar
158 159 160 |
# File 'lib/everywhere/config.rb', line 158 def mode app["mode"] || (remote_url ? "remote" : "local") end |
#name(target: nil) ⇒ Object
96 97 98 |
# File 'lib/everywhere/config.rb', line 96 def name(target: nil) resolved(target)["name"] || File.basename(File.(root)).split(/[-_]/).map(&:capitalize).join(" ") end |
#native_ios ⇒ Object
Native code extensions ("supernative"): Swift the app repo carries in
native/ios/ that every build compiles into the shell, plus this
declaration of what to hook up. Build-time only — stores forbid
downloading native code, so extensions ship with the app binary.
native:
ios:
components: [ChartComponent] # BridgeComponent subclasses to register
screens: # path rules with view_controller: <id>
map: MapScreen # SwiftUI View or UIViewController, init(url:)
splash: LaunchSplash # SwiftUI View or UIViewController, init()
splash_min_seconds: 1.0 # how long the custom splash stays up at minimum
lazy_load_tabs: true # defer each tab's first visit until it's selected
383 384 385 386 |
# File 'lib/everywhere/config.rb', line 383 def native_ios section = @data.dig("native", "ios") section.is_a?(Hash) ? section : {} end |
#native_ios? ⇒ Boolean
454 |
# File 'lib/everywhere/config.rb', line 454 def native_ios? = !(native_ios_components.empty? && native_ios_screens.empty? && native_ios_splash.nil?) |
#native_ios_components ⇒ Object
388 |
# File 'lib/everywhere/config.rb', line 388 def native_ios_components = Array(native_ios["components"]).map(&:to_s) |
#native_ios_errors ⇒ Object
462 463 464 465 466 467 468 469 470 471 472 473 474 |
# File 'lib/everywhere/config.rb', line 462 def native_ios_errors types = native_ios_components + native_ios_screens.values + [native_ios_splash].compact errors = types.reject { |t| t.match?(SWIFT_TYPE_NAME) }.map do |t| "native.ios: #{t.inspect} is not a Swift type name (letters, digits, _)" end errors += native_ios_screens.keys.reject { |id| id.match?(SCREEN_IDENTIFIER) }.map do |id| "native.ios.screens: identifier #{id.inspect} must match #{SCREEN_IDENTIFIER.inspect}" end raw = native_ios["splash_min_seconds"] errors << "native.ios.splash_min_seconds must be a number (seconds)" if raw && !raw.is_a?(Numeric) errors + native_ios_package_errors end |
#native_ios_lazy_load_tabs ⇒ Object
Whether the native tab bar defers each non-selected tab's initial visit
until the tab is first selected (Hotwire Native lazyLoadTabs). Tri-state
so apps can opt in or opt out explicitly: true lazy-loads, false loads
every tab up front, and an absent key leaves the shell on its default
(eager).
412 413 414 415 |
# File 'lib/everywhere/config.rb', line 412 def native_ios_lazy_load_tabs value = native_ios["lazy_load_tabs"] value if value == true || value == false end |
#native_ios_package_errors ⇒ Object
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 |
# File 'lib/everywhere/config.rb', line 482 def native_ios_package_errors raw = native_ios["packages"] return [] if raw.nil? return ["native.ios.packages must be a list"] unless raw.is_a?(Array) raw.each_with_index.flat_map do |entry, i| at = "native.ios.packages[#{i}]" next ["#{at} must be a mapping with a url:"] unless entry.is_a?(Hash) errors = [] url = entry["url"].to_s.strip if url.empty? errors << "#{at}.url is required" elsif !url.match?(PACKAGE_URL) errors << "#{at}.url #{url.inspect} must be an https:// or git@ URL" end present = PACKAGE_REQUIREMENT_KEYS.select { |k| entry.key?(k) && !entry[k].to_s.strip.empty? } if present.empty? errors << "#{at} needs one version requirement (#{PACKAGE_REQUIREMENT_KEYS.join(" / ")})" elsif present.length > 1 errors << "#{at} sets conflicting requirements (#{present.join(", ")}) — pick one" elsif %w[from exact].include?(present.first) && !entry[present.first].to_s.strip.match?(PACKAGE_VERSION) errors << "#{at}.#{present.first} #{entry[present.first].to_s.inspect} must be a version like \"1.2.0\"" end Array(entry["products"]).each do |product| next if product.to_s.match?(SWIFT_TYPE_NAME) errors << "#{at}.products: #{product.to_s.inspect} is not a module name (letters, digits, _)" end errors end end |
#native_ios_packages ⇒ Object
432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
# File 'lib/everywhere/config.rb', line 432 def native_ios_packages Array(native_ios["packages"]).filter_map do |entry| next unless entry.is_a?(Hash) url = entry["url"].to_s.strip kind = PACKAGE_REQUIREMENT_KEYS.find { |k| entry.key?(k) && !entry[k].to_s.strip.empty? } products = Array(entry["products"]).map { |p| p.to_s.strip }.reject(&:empty?) products = [package_identity(url)] if products.empty? { "url" => url, "requirement" => (kind && { "kind" => kind, "value" => entry[kind].to_s.strip }), "products" => products } end end |
#native_ios_packages? ⇒ Boolean
447 |
# File 'lib/everywhere/config.rb', line 447 def native_ios_packages? = !native_ios_packages.empty? |
#native_ios_screens ⇒ Object
390 391 392 393 394 395 |
# File 'lib/everywhere/config.rb', line 390 def native_ios_screens entries = native_ios["screens"] return {} unless entries.is_a?(Hash) entries.to_h { |id, type| [id.to_s, type.to_s] } end |
#native_ios_splash ⇒ Object
397 |
# File 'lib/everywhere/config.rb', line 397 def native_ios_splash = native_ios["splash"]&.to_s |
#native_ios_splash_min_seconds ⇒ Object
Minimum seconds a custom splash stays on screen (default 1.0 shell-side; a fast server would otherwise dismiss a branded splash in a ~50ms flash). Clamped to the shell's 8s give-up timeout.
402 403 404 405 |
# File 'lib/everywhere/config.rb', line 402 def native_ios_splash_min_seconds value = native_ios["splash_min_seconds"] value.to_f.clamp(0.0, 8.0) if value.is_a?(Numeric) end |
#package_identity(url) ⇒ Object
SPM's package identity: the URL's last path segment without a .git suffix
(github.com/simibac/ConfettiSwiftUI → "ConfettiSwiftUI"). Used as the
default product name and as the package: key in .product(name:package:).
452 |
# File 'lib/everywhere/config.rb', line 452 def package_identity(url) = File.basename(url.to_s.sub(%r{/+\z}, "")).sub(/\.git\z/, "") |
#path_configuration_hash(os, tabs: nil) ⇒ Object
The full path-configuration document for one mobile platform — rules plus
our settings (tabs live in settings, per Hotwire Native convention).
Pass tabs: to override the resolved list (the mobile config endpoint
passes a per-request-filtered list; build-time stamping omits it and
bakes them all).
358 359 360 361 362 363 |
# File 'lib/everywhere/config.rb', line 358 def path_configuration_hash(os, tabs: nil) settings = {} platform_tabs = tabs || tabs_for(os) settings["tabs"] = platform_tabs unless platform_tabs.empty? { "settings" => settings, "rules" => MOBILE_PATH_RULES + mobile_rules } end |
#path_configuration_json(os, tabs: nil) ⇒ Object
365 366 367 368 |
# File 'lib/everywhere/config.rb', line 365 def path_configuration_json(os, tabs: nil) require "json" JSON.generate(path_configuration_hash(os, tabs: tabs)) end |
#permission_errors(os) ⇒ Object
Problems with the permissions declaration for one platform, as human-readable strings — unknown names, and camera/location missing the mandatory usage string. Empty means buildable.
315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/everywhere/config.rb', line 315 def (os) .flat_map do |name, usage| unless MOBILE_PERMISSIONS.include?(name) next ["unknown permission #{name.inspect} — supported: #{MOBILE_PERMISSIONS.join(", ")}"] end if os == "ios" && IOS_USAGE_KEYS.key?(name) && usage["ios"].to_s.strip.empty? next ["#{name} needs a usage string — the sentence iOS shows when asking. " \ "In config/everywhere.yml:\n permissions:\n #{name}: \"Why the app needs #{name}.\""] end [] end end |
#permissions ⇒ Object
OS-integration permissions the app declares. Recorded in the receipt and (later) used to generate the shell's capabilities.
151 |
# File 'lib/everywhere/config.rb', line 151 def = Array(build["permissions"]) |
#remote? ⇒ Boolean
162 |
# File 'lib/everywhere/config.rb', line 162 def remote? = mode == "remote" |
#remote_instances? ⇒ Boolean
Multi-instance apps (remote.instances: true): the mobile shell boots
into remote.url — a hosted instance-picker page — and lets that page
re-root the app onto a chosen instance via Everywhere.instance.set,
persisted across launches until Everywhere.instance.clear. Opt-in
because it lets page JS repoint the whole app: apps that aren't
multi-instance shouldn't carry that surface.
174 175 176 |
# File 'lib/everywhere/config.rb', line 174 def remote_instances? @data.dig("remote", "instances") == true end |
#remote_url ⇒ Object
164 165 166 |
# File 'lib/everywhere/config.rb', line 164 def remote_url @data.dig("remote", "url")&.chomp("/") end |
#splash ⇒ Object
Optional custom splash page (HTML file, path relative to the app root). Shown while the packaged server boots; the shell injects window.EVERYWHERE_CONFIG so it can use the app's name and colors.
123 124 125 126 |
# File 'lib/everywhere/config.rb', line 123 def splash path = app["splash"] File.(path, root) if path end |
#tabs ⇒ Object
Mobile tab bar, platform-neutral. Icons are per-platform (ios = SF
Symbol, android = Material icon later), with a shared icon: fallback:
tabs:
- name: Builds
path: /builds
icons:
ios: hammer
android: build
Tabs ship two ways from the same source: baked into the app's bundled
path-configuration.json at build time (offline/first-launch), and served
live from /everywhere/
236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/everywhere/config.rb', line 236 def tabs entries = @data["tabs"] return [] unless entries.is_a?(Array) entries.filter_map do |tab| next unless tab.is_a?(Hash) && tab["name"] && tab["path"] path = tab["path"].start_with?("/") ? tab["path"] : "/#{tab["path"]}" { "name" => tab["name"], "path" => path, "icon" => tab["icon"], "icons" => (tab["icons"] if tab["icons"].is_a?(Hash)) }.compact end end |
#tabs_for(os) ⇒ Object
Tabs with the icon resolved for one platform: icons.
251 252 253 254 255 256 |
# File 'lib/everywhere/config.rb', line 251 def tabs_for(os) tabs.map do |tab| { "title" => tab["name"], "path" => tab["path"], "icon" => tab.dig("icons", os.to_s) || tab["icon"] || DEFAULT_TAB_ICONS[os.to_s] }.compact end end |
#targets ⇒ Object
Build targets (os-arch strings) — the CI matrix, expressed once.
154 |
# File 'lib/everywhere/config.rb', line 154 def targets = Array(build["targets"]) |
#tint_color ⇒ Object
214 215 216 |
# File 'lib/everywhere/config.rb', line 214 def tint_color normalize_color(appearance["tint_color"]) end |
#to_shell_hash(target: nil) ⇒ Object
The subset the shell needs, shipped as JSON (env var in dev,
Resources/everywhere.json inside a bundled .app). Pass target: so the
packaged config reflects the platform being built (its bundle id / name).
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
# File 'lib/everywhere/config.rb', line 561 def to_shell_hash(target: nil) { "name" => name(target: target), "bundle_id" => bundle_id(target: target), "version" => version(target: target), "mode" => mode, "remote_url" => remote_url, "remote_instances" => (true if remote_instances?), "entry_path" => entry_path, "tint_color" => tint_color, "background_color" => background_color, "menu" => ( unless .empty?), "tray" => (tray unless tray.empty?), "lazy_load_tabs" => native_ios_lazy_load_tabs, "splash_min_seconds" => native_ios_splash_min_seconds, "updates" => updates_shell_hash, "permissions" => (.keys unless .empty?) }.compact end |
#to_shell_json(target: nil) ⇒ Object
581 582 583 584 |
# File 'lib/everywhere/config.rb', line 581 def to_shell_json(target: nil) require "json" JSON.generate(to_shell_hash(target: target)) end |
#tray ⇒ Object
System tray: same entry shape as menu: plus action: quit | show.
tray:
- label: "Open My App"
action: show
- label: "New Note"
path: /notes/new
- separator
- label: Quit
action: quit
544 545 546 547 548 549 550 551 552 553 554 555 556 |
# File 'lib/everywhere/config.rb', line 544 def tray entries = @data["tray"] return [] unless entries.is_a?(Array) entries.filter_map do |item| if item == "separator" || (item.is_a?(Hash) && item["separator"]) { "separator" => true } elsif item.is_a?(Hash) && item["label"] && (item["path"] || item["action"]) { "label" => item["label"], "path" => item["path"], "action" => item["action"] }.compact end end end |
#updates ⇒ Object
The updates: section — self-hosted auto-updates. Two halves:
a client half (url/channel/public_key/auto/interval) that ships to the
shell, and a publish-side s3: half that NEVER leaves the dev machine.
channel here is a release channel (stable/beta) — not the receipt's
distribution_channel (direct vs app stores).
183 |
# File 'lib/everywhere/config.rb', line 183 def updates = @data.fetch("updates", nil) || {} |
#updates_auto ⇒ Object
off — never check; check — check + notify (default); download — also fetch/verify in the background; install — silent auto-update.
195 |
# File 'lib/everywhere/config.rb', line 195 def updates_auto = updates["auto"] || "check" |
#updates_channel ⇒ Object
187 |
# File 'lib/everywhere/config.rb', line 187 def updates_channel = updates["channel"] || "stable" |
#updates_interval ⇒ Object
197 |
# File 'lib/everywhere/config.rb', line 197 def updates_interval = [(updates["interval"] || 21_600).to_i, 300].max |
#updates_public_key ⇒ Object
191 |
# File 'lib/everywhere/config.rb', line 191 def updates_public_key = updates["public_key"] |
#updates_s3 ⇒ Object
185 |
# File 'lib/everywhere/config.rb', line 185 def updates_s3 = updates.fetch("s3", nil) || {} |
#updates_shell_hash ⇒ Object
The client subset for the shell; nil (and thus absent from everywhere.json) until both url and public_key are configured — an unsigned update feed is not a thing.
202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/everywhere/config.rb', line 202 def updates_shell_hash return nil unless updates_url && updates_public_key { "url" => updates_url, "channel" => updates_channel, "public_key" => updates_public_key, "auto" => updates_auto, "interval" => updates_interval } end |
#updates_url ⇒ Object
189 |
# File 'lib/everywhere/config.rb', line 189 def updates_url = updates["url"]&.chomp("/") |
#version(target: nil) ⇒ Object
Marketing/display version of the app (CFBundleShortVersionString, and the
version recorded in the build receipt). One shared app.version is the
default for every target; a platform may override it when a store forces a
different number. Defaults conservatively.
137 138 139 |
# File 'lib/everywhere/config.rb', line 137 def version(target: nil) resolved(target)["version"] || "0.1.0" end |