Module: RubyNative
- Defined in:
- lib/ruby_native.rb,
lib/ruby_native/cli.rb,
lib/ruby_native/engine.rb,
lib/ruby_native/helper.rb,
lib/ruby_native/version.rb,
lib/ruby_native/cli/login.rb,
lib/ruby_native/iap/event.rb,
lib/ruby_native/cli/deploy.rb,
lib/ruby_native/cli/preview.rb,
lib/ruby_native/iap/decodable.rb,
lib/ruby_native/iap/verifiable.rb,
lib/ruby_native/native_version.rb,
lib/ruby_native/cli/credentials.rb,
lib/ruby_native/inertia_support.rb,
lib/ruby_native/iap/normalizable.rb,
lib/ruby_native/native_detection.rb,
lib/ruby_native/oauth_middleware.rb,
lib/ruby_native/set_cookie_header.rb,
lib/generators/ruby_native/iap_generator.rb,
lib/ruby_native/tunnel_cookie_middleware.rb,
app/models/ruby_native/iap/purchase_intent.rb,
lib/ruby_native/screenshots/sign_in_helper.rb,
app/controllers/ruby_native/aasa_controller.rb,
lib/ruby_native/iap/apple_webhook_processor.rb,
lib/generators/ruby_native/install_generator.rb,
app/controllers/ruby_native/config_controller.rb,
app/controllers/ruby_native/assetlinks_controller.rb,
app/controllers/ruby_native/auth/start_controller.rb,
app/controllers/ruby_native/iap/restores_controller.rb,
app/controllers/ruby_native/push/devices_controller.rb,
app/controllers/ruby_native/auth/sessions_controller.rb,
app/controllers/ruby_native/iap/purchases_controller.rb,
app/controllers/ruby_native/webhooks/apple_controller.rb,
app/controllers/ruby_native/iap/completions_controller.rb,
app/controllers/ruby_native/screenshots/sessions_controller.rb
Defined Under Namespace
Modules: Auth, Generators, Helper, IAP, InertiaSupport, NativeDetection, Push, Screenshots, SetCookieHeader, Webhooks Classes: AasaController, AssetlinksController, CLI, ConfigController, Engine, NativeVersion, OAuthMiddleware, TunnelCookieMiddleware
Constant Summary collapse
- ERROR_SCREEN_STATES =
The native fallback screen has two states:
offline(no connectivity) andgeneric(any other load failure). Each can carry a per-platform icon and localized copy. %i[offline generic].freeze
- ERROR_SCREEN_COPY_KEYS =
%i[title message].freeze
- VERSION =
"0.15.0"
Class Method Summary collapse
-
.backfill_error_icons ⇒ Object
Mirrors
backfill_tab_iconsfor the error screen: fills a state's flaticonfrom its per-platformicons(ios first, then android), so the iOS app, which reads only the flaticon, still renders one. -
.backfill_tab_icons ⇒ Object
Mirrors per-platform
icons:into the legacy flaticon:field so native binaries that only readtab.iconkeep rendering an icon. -
.config_as_json ⇒ Object
The JSON served at GET /native/config.
- .configure {|_self| ... } ⇒ Object
-
.error_screen_config(yaml_errors) ⇒ Object
Merges per-state error-screen icons (from YAML) with localized copy (from I18n) into the shape the native apps decode.
-
.error_screen_translations(subkey) ⇒ Object
Reads
ruby_native.errors.<subkey>for every available locale, keeping only the locales the developer actually translated. - .fire_subscription_callbacks(event) ⇒ Object
- .load_config ⇒ Object
-
.normalize_linked_paths ⇒ Object
Entries are path prefixes: "/pair/" links every URL under it.
-
.normalize_oauth_paths ⇒ Object
auth.oauth_pathsmust list only OAuth authorize paths, never their callbacks. - .on_subscription_change(&block) ⇒ Object
-
.render_config(path) ⇒ Object
config/ruby_native.yml is rendered as ERB before it is parsed, so a developer can interpolate Rails helpers into it.
Class Method Details
.backfill_error_icons ⇒ Object
Mirrors backfill_tab_icons for the error screen: fills a state's flat
icon from its per-platform icons (ios first, then android), so the iOS
app, which reads only the flat icon, still renders one. An explicit
icon: wins.
102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/ruby_native.rb', line 102 def self.backfill_error_icons errors = self.config[:errors] return unless errors.is_a?(Hash) ERROR_SCREEN_STATES.each do |state| state_config = errors[state] next unless state_config.is_a?(Hash) icons = state_config[:icons] next unless icons.is_a?(Hash) state_config[:icon] ||= icons[:ios] || icons[:android] end end |
.backfill_tab_icons ⇒ Object
Mirrors per-platform icons: into the legacy flat icon: field so native
binaries that only read tab.icon keep rendering an icon. Explicit icon:
wins; otherwise falls back to icons.ios, then icons.android.
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/ruby_native.rb', line 87 def self.backfill_tab_icons Array(self.config[:tabs]).each do |tab| next unless tab.is_a?(Hash) icons = tab[:icons] next unless icons.is_a?(Hash) tab[:icon] ||= icons[:ios] || icons[:android] end end |
.config_as_json ⇒ Object
The JSON served at GET /native/config. Identical to config, except the
errors block is enriched: per-state icons from config/ruby_native.yml are
merged with localized title/message pulled from the host app's I18n
(ruby_native.errors.<state>.<key>), one entry per available locale. Only
values the developer actually provided are emitted; the native apps fall
back to bundled English copy for anything missing. Built on a deep copy so
the in-memory config the server reads for view helpers is never mutated.
130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/ruby_native.rb', line 130 def self.config_as_json return config if config.nil? payload = config.deep_dup errors = error_screen_config(payload[:errors]) if errors.empty? payload.delete(:errors) else payload[:errors] = errors end payload end |
.configure {|_self| ... } ⇒ Object
32 33 34 |
# File 'lib/ruby_native.rb', line 32 def self.configure yield self end |
.error_screen_config(yaml_errors) ⇒ Object
Merges per-state error-screen icons (from YAML) with localized copy (from
I18n) into the shape the native apps decode. Omits any state with neither an
icon nor copy, so an untouched app sends no errors block at all.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/ruby_native.rb', line 146 def self.error_screen_config(yaml_errors) config = ERROR_SCREEN_STATES.each_with_object({}) do |state, result| entry = {} state_config = yaml_errors[state] if yaml_errors.is_a?(Hash) if state_config.is_a?(Hash) entry[:icon] = state_config[:icon] if state_config[:icon] entry[:icons] = state_config[:icons] if state_config[:icons] end ERROR_SCREEN_COPY_KEYS.each do |key| translations = error_screen_translations("#{state}.#{key}") entry[key] = translations unless translations.empty? end result[state] = entry unless entry.empty? end # The Retry button label is shared by both states, so it sits at the top of # the block rather than under a state. retry_label = error_screen_translations("retry") config[:retry] = retry_label unless retry_label.empty? config end |
.error_screen_translations(subkey) ⇒ Object
Reads ruby_native.errors.<subkey> for every available locale, keeping only
the locales the developer actually translated. Copy lives in the host app's
own locale files; the gem ships none.
171 172 173 174 175 176 |
# File 'lib/ruby_native.rb', line 171 def self.error_screen_translations(subkey) I18n.available_locales.each_with_object({}) do |locale, result| value = I18n.t("ruby_native.errors.#{subkey}", locale: locale, default: nil) result[locale] = value unless value.nil? end end |
.fire_subscription_callbacks(event) ⇒ Object
40 41 42 |
# File 'lib/ruby_native.rb', line 40 def self.fire_subscription_callbacks(event) subscription_callbacks.each { |cb| cb.call(event) } end |
.load_config ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/ruby_native.rb', line 44 def self.load_config path = Rails.root.join("config", "ruby_native.yml") return unless path.exist? parsed = YAML.load(render_config(path), filename: path.to_s, aliases: true) # An empty file, comments only, or ERB rendering to nothing parses to nil; # treat it like a missing file instead of crashing Rails boot. unless parsed.is_a?(Hash) Rails.logger.warn("[RubyNative] #{path} is empty or not a YAML mapping; ignoring it.") return end self.config = parsed.deep_symbolize_keys self.config[:app] ||= {} self.config[:app][:entry_path] ||= self.config.dig(:tabs, 0, :path) || "/" self.config[:auth] ||= {} normalize_oauth_paths normalize_linked_paths backfill_tab_icons backfill_error_icons end |
.normalize_linked_paths ⇒ Object
Entries are path prefixes: "/pair/" links every URL under it. A leading slash is added when missing, and a trailing "*" (an easy slip, since the AASA file uses one) is stripped so both platforms receive a plain prefix. Left absent when unconfigured so config.json doesn't grow an empty key.
207 208 209 210 211 212 213 214 |
# File 'lib/ruby_native.rb', line 207 def self.normalize_linked_paths return unless self.config.key?(:linked_paths) self.config[:linked_paths] = Array(self.config[:linked_paths]) .map { |path| path.to_s.strip.sub(/\*+\z/, "") } .reject(&:empty?) .map { |path| path.start_with?("/") ? path : "/#{path}" } end |
.normalize_oauth_paths ⇒ Object
auth.oauth_paths must list only OAuth authorize paths, never their
callbacks. The native app treats every listed path as a sign-in trigger and
derives the provider from the last path segment, so a callback entry like
"/auth/google/callback" would launch a bogus flow for a provider named
"callback" and send sign-in into a loop. The callback round-trip is handled
automatically by OAuthMiddleware's tracking cookie, so it never needs
listing. Drop any entry that is the "/callback" child of another listed
path and warn, so a copied-in callback can't break native sign-in. The value
is always written back as an array: a scalar in YAML fails the whole native
config decode on both platforms.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/ruby_native.rb', line 188 def self.normalize_oauth_paths auth = self.config[:auth] return unless auth.is_a?(Hash) && auth.key?(:oauth_paths) paths = Array(auth[:oauth_paths]) callbacks = paths.select { |path| paths.any? { |start| path == "#{start}/callback" } } if callbacks.any? Rails.logger.warn( "[RubyNative] Ignoring OAuth callback path(s) in config/ruby_native.yml " \ "(#{callbacks.join(", ")}). List only the authorize path; callbacks are handled automatically." ) end auth[:oauth_paths] = paths - callbacks end |
.on_subscription_change(&block) ⇒ Object
36 37 38 |
# File 'lib/ruby_native.rb', line 36 def self.on_subscription_change(&block) subscription_callbacks << block end |
.render_config(path) ⇒ Object
config/ruby_native.yml is rendered as ERB before it is parsed, so a
developer can interpolate Rails helpers into it. The motivating case is the
navbar logo: logo: '<%= image_url("logo.png") %>' resolves to a
fingerprinted asset URL the native app downloads and caches, and because the
digest changes whenever the asset changes, the cache busts itself. A full
URL (a CDN, say) works just as well; the app only ever sees a URL to fetch.
The template renders against the controller helper proxy, so image_url and
friends behave exactly as they do in a view. With no request or asset host
they degrade to a relative path -- asset helpers never raise "missing host"
the way routing helpers do -- and the native app resolves any relative URL
against the base URL it already fetched the config from.
79 80 81 82 |
# File 'lib/ruby_native.rb', line 79 def self.render_config(path) helpers = ActionController::Base.helpers ERB.new(path.read, trim_mode: "-").result(helpers.instance_eval { binding }) end |