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/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/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, Webhooks Classes: AasaController, CLI, ConfigController, Engine, NativeVersion, OAuthMiddleware, TunnelCookieMiddleware

Constant Summary collapse

ERROR_SCREEN_STATES =

The native fallback screen has two states: ‘offline` (no connectivity) and `generic` (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.10.8"

Class Method Summary collapse

Class Method Details

.backfill_error_iconsObject

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.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ruby_native.rb', line 67

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_iconsObject

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`.



52
53
54
55
56
57
58
59
60
61
# File 'lib/ruby_native.rb', line 52

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_jsonObject

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.



95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/ruby_native.rb', line 95

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

Yields:

  • (_self)

Yield Parameters:

  • _self (RubyNative)

    the object that the method was called on



24
25
26
# File 'lib/ruby_native.rb', line 24

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.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/ruby_native.rb', line 111

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.



136
137
138
139
140
141
# File 'lib/ruby_native.rb', line 136

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



32
33
34
# File 'lib/ruby_native.rb', line 32

def self.fire_subscription_callbacks(event)
  subscription_callbacks.each { |cb| cb.call(event) }
end

.load_configObject



36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/ruby_native.rb', line 36

def self.load_config
  path = Rails.root.join("config", "ruby_native.yml")
  return unless path.exist?

  self.config = YAML.load_file(path).deep_symbolize_keys
  self.config[:app] ||= {}
  self.config[:app][:entry_path] ||= self.config.dig(:tabs, 0, :path) || "/"
  self.config[:auth] ||= {}
  normalize_oauth_paths
  backfill_tab_icons
  backfill_error_icons
end

.normalize_oauth_pathsObject

‘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.



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/ruby_native.rb', line 151

def self.normalize_oauth_paths
  paths = Array(self.config.dig(:auth, :oauth_paths))
  callbacks = paths.select { |path| paths.any? { |start| path == "#{start}/callback" } }
  return if callbacks.empty?

  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."
  )
  self.config[:auth][:oauth_paths] = paths - callbacks
end

.on_subscription_change(&block) ⇒ Object



28
29
30
# File 'lib/ruby_native.rb', line 28

def self.on_subscription_change(&block)
  subscription_callbacks << block
end