ruby_native 0.9.0 → 0.9.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1dba22f3ed73cde73344241945e6862510cb3d0f42560f77225b5f9ad9e78d4
4
- data.tar.gz: 7cf291e917138449546715e585cc3926842b94dcafcf7c85d3ea439f7b17edd6
3
+ metadata.gz: 8f882623a2d70e309c284e7a67a9dd4119579024f5c8da2afbb8cefbf5185127
4
+ data.tar.gz: e05abee9a75d6f830fa0e575517746bfa0e4bc728aeff5d2da75f893de09289e
5
5
  SHA512:
6
- metadata.gz: 0daccf8ec40c9f0c4514b618fff3bcbaffcda78e8777a6ddc2b5b5aca0bbe7844f3a7ab5e1a5c83c3875c4889b6531cf65bd09b949353636f66d0c3f3d62a8fb
7
- data.tar.gz: e8a3d231802d4f4324a664f1e6bcaaba5a69ca9a95ad03d531d24185e1b816df739c8baa363587b300a95a5377c8513aea8a888e56a498aa2af45dec92bcbf8e
6
+ metadata.gz: a598a22166379ba712b9b0fb9417434cb94786937085055363bac1eb676c06a684addaecd4537e5b45098a9ed0ca810d0569d4c1af12c5adbe170cad1d0de6b2
7
+ data.tar.gz: 3fd6fe2454af67d3ea515862dc3161b2a2695dbf1534774baa3afa03e72e8ae564cb147a24d6ff7ad53feebaf1d41520025bdd849a2bb5dc139e583874d2ae0e
data/README.md CHANGED
@@ -79,6 +79,26 @@ The companion app persists the scanned URL across launches. Long-press the app i
79
79
  - `GET /native/config` - returns the YAML config as JSON
80
80
  - `POST /native/push/devices` - registers a push notification token (requires `current_user` from host app)
81
81
 
82
+ ## Push notifications
83
+
84
+ Delivery uses the companion `action_push_native` gem. Ruby Native owns the registration handshake (the `native_push_tag` helper prompts for permission, `/native/push/devices` stores the token) and the tap-handling conventions on the native side.
85
+
86
+ When sending a push, two destination keys are supported via `with_data`:
87
+
88
+ - `path` — internal route appended to your base URL and loaded in the in-app WebView (e.g. `/sources/42`).
89
+ - `url` — full external URL opened in `SFSafariViewController`, leaving the WebView in place behind it (e.g. `https://dashboard.stripe.com/payments/pi_abc`).
90
+
91
+ If both are present, `url` wins. `http` and `https` URLs open in `SFSafariViewController`; other valid schemes (`mailto:`, `tel:`, `maps:`, third-party app schemes, etc.) hand off to the appropriate app via `UIApplication.open`. Malformed `url` strings are dropped (the tap does not fall back to `path`).
92
+
93
+ ```ruby
94
+ ApplicationPushNotification
95
+ .with_data(path: source_path(source), url: notification.external_url)
96
+ .new(title: "New payment", body: "$49.99 from joe@example.com")
97
+ .deliver_later_to(user.push_devices)
98
+ ```
99
+
100
+ For model/migration setup, see [action_push_native](https://github.com/basecamp/action_push_native).
101
+
82
102
  ## Normal and Advanced Modes
83
103
 
84
104
  Normal Mode works with any frontend framework and requires no JavaScript. You get tabs, form page marking, push notifications, and history management.
@@ -27,7 +27,7 @@ module RubyNative
27
27
  return
28
28
  end
29
29
 
30
- RubyNative.screenshot_sign_in.call(self)
30
+ RubyNative.screenshot_sign_in.call(SignInHelper.new(self))
31
31
 
32
32
  cookies[:_ruby_native_screenshot_session] = {
33
33
  value: "1",
@@ -129,6 +129,26 @@ The gem auto-mounts at `/native`. No route configuration needed.
129
129
  - `GET /native/config` returns the YAML config as JSON
130
130
  - `POST /native/push/devices` registers a push notification device token
131
131
 
132
+ ## Push notifications
133
+
134
+ Delivery uses the companion `action_push_native` gem. Ruby Native owns the registration (the `native_push_tag` helper prompts for permission, `/native/push/devices` stores the token) and defines the tap conventions on the native side.
135
+
136
+ Two destination keys are supported via `with_data`:
137
+
138
+ - `path` — internal route appended to your base URL, loaded in the WebView.
139
+ - `url` — full external URL, opened in `SFSafariViewController`.
140
+
141
+ If both are present, `url` wins. `http` and `https` open in `SFSafariViewController`; other valid schemes (`mailto:`, `tel:`, `maps:`, third-party app schemes) open via `UIApplication.open`. Malformed `url` does not fall back to `path`.
142
+
143
+ ```ruby
144
+ ApplicationPushNotification
145
+ .with_data(path: source_path(source), url: notification.external_url)
146
+ .new(title: "New payment", body: "$49.99 from joe@example.com")
147
+ .deliver_later_to(user.push_devices)
148
+ ```
149
+
150
+ For model/migration setup, see the [action_push_native](https://github.com/basecamp/action_push_native) README.
151
+
132
152
  ## CLI
133
153
 
134
154
  ### Deploy from CI
@@ -16,6 +16,7 @@ module RubyNative
16
16
 
17
17
  def initialize(argv)
18
18
  @if_needed = argv.include?("--if-needed")
19
+ @platform = parse_platform(argv)
19
20
  end
20
21
 
21
22
  def run
@@ -105,7 +106,7 @@ module RubyNative
105
106
  req = Net::HTTP::Post.new(uri)
106
107
  req["Authorization"] = "Token #{Credentials.token}"
107
108
  req["Content-Type"] = "application/json"
108
- req.body = JSON.generate(gem_version: RubyNative::VERSION)
109
+ req.body = JSON.generate(build_payload)
109
110
 
110
111
  response = make_request(uri, req)
111
112
 
@@ -174,7 +175,7 @@ module RubyNative
174
175
  puts " Version: v#{data["version"]} (#{data["number"]})"
175
176
  puts " Ruby Native: #{data["native_version"]}" if data["native_version"]
176
177
  puts ""
177
- puts "Your build is being submitted to TestFlight."
178
+ puts success_destination_message(data)
178
179
  break
179
180
  when "failure", "failed", "cancelled"
180
181
  puts ""
@@ -208,15 +209,59 @@ module RubyNative
208
209
  end
209
210
 
210
211
  def print_status(status)
211
- labels = {
212
- "queued" => "Queued",
213
- "building" => "Building",
214
- "processing" => "Submitting to App Store Connect"
215
- }
216
- label = labels[status]
212
+ label = status_labels[status]
217
213
  puts " #{label}..." if label
218
214
  end
219
215
 
216
+ def status_labels
217
+ if android?
218
+ {
219
+ "queued" => "Queued",
220
+ "building" => "Building Android AAB",
221
+ "processing" => "Uploading to Play Internal Testing"
222
+ }
223
+ else
224
+ {
225
+ "queued" => "Queued",
226
+ "building" => "Building",
227
+ "processing" => "Submitting to App Store Connect"
228
+ }
229
+ end
230
+ end
231
+
232
+ def success_destination_message(data)
233
+ platform = data["platform"] || requested_platform
234
+ case platform
235
+ when "android"
236
+ "Your build is being uploaded to Play Internal Testing."
237
+ else
238
+ "Your build is being submitted to TestFlight."
239
+ end
240
+ end
241
+
242
+ def parse_platform(argv)
243
+ return "android" if argv.include?("--android")
244
+
245
+ flag = argv.find { |a| a.start_with?("--platform=") }
246
+ return flag.split("=", 2).last if flag
247
+
248
+ "ios"
249
+ end
250
+
251
+ def requested_platform
252
+ @platform
253
+ end
254
+
255
+ def android?
256
+ @platform == "android"
257
+ end
258
+
259
+ def build_payload
260
+ payload = { gem_version: RubyNative::VERSION }
261
+ payload[:platform] = @platform unless @platform == "ios"
262
+ payload
263
+ end
264
+
220
265
  # --- App linking ---
221
266
 
222
267
  def link_app
@@ -26,7 +26,7 @@ module RubyNative
26
26
  puts "Usage: ruby_native <command>"
27
27
  puts ""
28
28
  puts "Commands:"
29
- puts " deploy Trigger an iOS build"
29
+ puts " deploy Trigger an iOS build (use --android for Android)"
30
30
  puts " login Authenticate with Ruby Native"
31
31
  puts " logout Remove stored credentials"
32
32
  puts " preview Start a tunnel and display a QR code"
@@ -53,8 +53,10 @@ module RubyNative
53
53
  tag.div(data: { native_navbar: title.to_s }, hidden: true) { builder.to_html }
54
54
  end
55
55
 
56
- def native_fab_tag(icon:, href: nil, click: nil)
57
- data = { native_fab: true, native_icon: icon }
56
+ def native_fab_tag(icon: nil, icons: nil, href: nil, click: nil)
57
+ resolved = RubyNative::Helper.resolve_icon(icon: icon, icons: icons, platform: try(:native_platform))
58
+ raise ArgumentError, "native_fab_tag requires an icon" if resolved.nil?
59
+ data = { native_fab: true, native_icon: resolved }
58
60
  data[:native_href] = href if href
59
61
  data[:native_click] = click if click
60
62
  tag.div(data: data, hidden: true)
@@ -71,16 +73,31 @@ module RubyNative
71
73
  data
72
74
  end
73
75
 
76
+ # Picks the right icon name for the current native platform. Accepts the
77
+ # single `icon:` form (applied to every platform) and/or the `icons:` hash
78
+ # form (`{ ios: "...", android: "..." }`). When both are given, a matching
79
+ # `icons[platform]` wins; otherwise falls back to `icon`. Returns nil when
80
+ # nothing resolves.
81
+ def self.resolve_icon(icon: nil, icons: nil, platform: nil)
82
+ if icons.is_a?(Hash) && platform
83
+ key = platform.to_sym
84
+ per_platform = icons[key] || icons[key.to_s]
85
+ return per_platform if per_platform
86
+ end
87
+ icon
88
+ end
89
+
74
90
  class NavbarBuilder
75
91
  def initialize(context)
76
92
  @context = context
77
93
  @items = []
78
94
  end
79
95
 
80
- def button(title = nil, icon: nil, href: nil, click: nil, position: :trailing, selected: false, &block)
96
+ def button(title = nil, icon: nil, icons: nil, href: nil, click: nil, position: :trailing, selected: false, &block)
97
+ resolved = RubyNative::Helper.resolve_icon(icon: icon, icons: icons, platform: @context.try(:native_platform))
81
98
  data = { native_button: "" }
82
99
  data[:native_title] = title if title
83
- data[:native_icon] = icon if icon
100
+ data[:native_icon] = resolved if resolved
84
101
  data[:native_href] = href if href
85
102
  data[:native_click] = click if click
86
103
  data[:native_position] = position.to_s
@@ -114,11 +131,12 @@ module RubyNative
114
131
  @items = []
115
132
  end
116
133
 
117
- def item(title, href: nil, click: nil, icon: nil, selected: false)
134
+ def item(title, href: nil, click: nil, icon: nil, icons: nil, selected: false)
135
+ resolved = RubyNative::Helper.resolve_icon(icon: icon, icons: icons, platform: @context.try(:native_platform))
118
136
  data = { native_menu_item: "", native_title: title }
119
137
  data[:native_href] = href if href
120
138
  data[:native_click] = click if click
121
- data[:native_icon] = icon if icon
139
+ data[:native_icon] = resolved if resolved
122
140
  data[:native_selected] = "" if selected
123
141
  @items << @context.tag.div(data: data)
124
142
  end
@@ -3,7 +3,7 @@ module RubyNative
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- helper_method :native_app?, :native_version if respond_to?(:helper_method)
6
+ helper_method :native_app?, :native_version, :native_platform if respond_to?(:helper_method)
7
7
  end
8
8
 
9
9
  def native_app?
@@ -14,5 +14,14 @@ module RubyNative
14
14
  match = request.user_agent.to_s.match(/RubyNative\/([\d.]+)/)
15
15
  NativeVersion.new(match ? match[1] : "0")
16
16
  end
17
+
18
+ # Returns "ios" or "android" for native requests, nil for web browsers.
19
+ # Used by view helpers to pick the right icon from `icons: { ios:, android: }`.
20
+ def native_platform
21
+ ua = request.user_agent.to_s
22
+ return "ios" if ua.include?("Ruby Native iOS")
23
+ return "android" if ua.include?("Ruby Native Android")
24
+ nil
25
+ end
17
26
  end
18
27
  end
@@ -0,0 +1,26 @@
1
+ module RubyNative
2
+ module Screenshots
3
+ # Yielded to the customer's `screenshot_sign_in` lambda. Exposes the
4
+ # request, session, and cookies as public accessors so the lambda never
5
+ # touches `ActionController` internals directly. `cookies` in particular
6
+ # is private on `ActionController::Base` in Rails 8.1+, so customers
7
+ # would otherwise have to fall back to `controller.send(:cookies)`.
8
+ class SignInHelper
9
+ def initialize(controller)
10
+ @controller = controller
11
+ end
12
+
13
+ def cookies
14
+ @controller.send(:cookies)
15
+ end
16
+
17
+ def request
18
+ @controller.request
19
+ end
20
+
21
+ def session
22
+ @controller.session
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module RubyNative
2
- VERSION = "0.9.0"
2
+ VERSION = "0.9.1"
3
3
  end
data/lib/ruby_native.rb CHANGED
@@ -10,6 +10,7 @@ require "ruby_native/iap/verifiable"
10
10
  require "ruby_native/iap/decodable"
11
11
  require "ruby_native/iap/normalizable"
12
12
  require "ruby_native/iap/apple_webhook_processor"
13
+ require "ruby_native/screenshots/sign_in_helper"
13
14
  require "ruby_native/engine"
14
15
 
15
16
  module RubyNative
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_native
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Masilotti
@@ -101,6 +101,7 @@ files:
101
101
  - lib/ruby_native/native_detection.rb
102
102
  - lib/ruby_native/native_version.rb
103
103
  - lib/ruby_native/oauth_middleware.rb
104
+ - lib/ruby_native/screenshots/sign_in_helper.rb
104
105
  - lib/ruby_native/tunnel_cookie_middleware.rb
105
106
  - lib/ruby_native/version.rb
106
107
  homepage: https://github.com/ruby-native/gem