ruby_native 0.3.2 → 0.4.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 +4 -4
- data/app/assets/stylesheets/ruby_native.css +4 -0
- data/app/javascript/ruby_native/react.js +40 -1
- data/app/javascript/ruby_native/vue.js +73 -0
- data/lib/ruby_native/cli/deploy.rb +253 -0
- data/lib/ruby_native/cli.rb +4 -0
- data/lib/ruby_native/helper.rb +64 -0
- data/lib/ruby_native/inertia_support.rb +1 -1
- data/lib/ruby_native/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2d194d87a1b21e7364717af329e474650f57137e80daa10c9ed6772e8144003
|
|
4
|
+
data.tar.gz: acd0e6d63d29fbeaa31341e283e10f6e581e07db233d2cf47f7bd4b64cd77299
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 70c00f3ac29b500054e4921781ea16f23a16b0eb0ccde6a7758ab417f14ca21998bae0ea78d1175b56968d734fb36df5a3073bc619b45bcb71737e4827ec1cfd
|
|
7
|
+
data.tar.gz: 802f1a3e5979e8ea4c4946b555b5df59afa0478cb9e0c8b7807b6dfe538f9b4cda27971addb0dca88a05757dc0a873871007765f2ca9408b5be73c1a2b1c8957
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createElement } from "react"
|
|
2
|
+
import { router } from "@inertiajs/react"
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
window.__inertiaRouter = router
|
|
5
|
+
|
|
6
|
+
export function NativeTabs({ enabled = true }) {
|
|
7
|
+
if (!enabled) return null
|
|
4
8
|
return createElement("div", { "data-native-tabs": true, hidden: true })
|
|
5
9
|
}
|
|
6
10
|
|
|
@@ -11,3 +15,38 @@ export function NativePush() {
|
|
|
11
15
|
export function NativeForm() {
|
|
12
16
|
return createElement("div", { "data-native-form": true, hidden: true })
|
|
13
17
|
}
|
|
18
|
+
|
|
19
|
+
export function NativeNavbar({ title, children }) {
|
|
20
|
+
return createElement("div", { "data-native-navbar": title, hidden: true }, children)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function NativeButton({ position = "trailing", icon, title, href, click, selected, children }) {
|
|
24
|
+
const props = { "data-native-button": true }
|
|
25
|
+
if (icon) props["data-native-icon"] = icon
|
|
26
|
+
if (title) props["data-native-title"] = title
|
|
27
|
+
if (href) props["data-native-href"] = href
|
|
28
|
+
if (click) props["data-native-click"] = click
|
|
29
|
+
if (position) props["data-native-position"] = position
|
|
30
|
+
if (selected) props["data-native-selected"] = ""
|
|
31
|
+
return createElement("div", props, children)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function NativeMenuItem({ title, href, click, icon, selected }) {
|
|
35
|
+
const props = { "data-native-menu-item": true }
|
|
36
|
+
if (title) props["data-native-title"] = title
|
|
37
|
+
if (href) props["data-native-href"] = href
|
|
38
|
+
if (click) props["data-native-click"] = click
|
|
39
|
+
if (icon) props["data-native-icon"] = icon
|
|
40
|
+
if (selected) props["data-native-selected"] = ""
|
|
41
|
+
return createElement("div", props)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function NativeSubmitButton({ title = "Save", click = "[type='submit']" }) {
|
|
45
|
+
return createElement("div", {
|
|
46
|
+
"data-native-submit-button": true,
|
|
47
|
+
"data-native-title": title,
|
|
48
|
+
"data-native-click": click,
|
|
49
|
+
hidden: true
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { defineComponent, h } from "vue"
|
|
2
|
+
import { router } from "@inertiajs/vue3"
|
|
3
|
+
|
|
4
|
+
window.__inertiaRouter = router
|
|
2
5
|
|
|
3
6
|
export const NativeTabs = defineComponent({
|
|
4
7
|
name: "NativeTabs",
|
|
8
|
+
props: {
|
|
9
|
+
enabled: { type: Boolean, default: true }
|
|
10
|
+
},
|
|
5
11
|
render() {
|
|
12
|
+
if (!this.enabled) return null
|
|
6
13
|
return h("div", { "data-native-tabs": true, hidden: true })
|
|
7
14
|
}
|
|
8
15
|
})
|
|
@@ -20,3 +27,69 @@ export const NativeForm = defineComponent({
|
|
|
20
27
|
return h("div", { "data-native-form": true, hidden: true })
|
|
21
28
|
}
|
|
22
29
|
})
|
|
30
|
+
|
|
31
|
+
export const NativeNavbar = defineComponent({
|
|
32
|
+
name: "NativeNavbar",
|
|
33
|
+
props: { title: { type: String, required: true } },
|
|
34
|
+
render() {
|
|
35
|
+
return h("div", { "data-native-navbar": this.title, hidden: true }, this.$slots.default?.())
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export const NativeButton = defineComponent({
|
|
40
|
+
name: "NativeButton",
|
|
41
|
+
props: {
|
|
42
|
+
position: { type: String, default: "trailing" },
|
|
43
|
+
icon: String,
|
|
44
|
+
title: String,
|
|
45
|
+
href: String,
|
|
46
|
+
click: String,
|
|
47
|
+
selected: { type: Boolean, default: undefined }
|
|
48
|
+
},
|
|
49
|
+
render() {
|
|
50
|
+
const attrs = { "data-native-button": true }
|
|
51
|
+
if (this.icon) attrs["data-native-icon"] = this.icon
|
|
52
|
+
if (this.title) attrs["data-native-title"] = this.title
|
|
53
|
+
if (this.href) attrs["data-native-href"] = this.href
|
|
54
|
+
if (this.click) attrs["data-native-click"] = this.click
|
|
55
|
+
if (this.position) attrs["data-native-position"] = this.position
|
|
56
|
+
if (this.selected) attrs["data-native-selected"] = ""
|
|
57
|
+
return h("div", attrs, this.$slots.default?.())
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
export const NativeMenuItem = defineComponent({
|
|
62
|
+
name: "NativeMenuItem",
|
|
63
|
+
props: {
|
|
64
|
+
title: String,
|
|
65
|
+
href: String,
|
|
66
|
+
click: String,
|
|
67
|
+
icon: String,
|
|
68
|
+
selected: { type: Boolean, default: undefined }
|
|
69
|
+
},
|
|
70
|
+
render() {
|
|
71
|
+
const attrs = { "data-native-menu-item": true }
|
|
72
|
+
if (this.title) attrs["data-native-title"] = this.title
|
|
73
|
+
if (this.href) attrs["data-native-href"] = this.href
|
|
74
|
+
if (this.click) attrs["data-native-click"] = this.click
|
|
75
|
+
if (this.icon) attrs["data-native-icon"] = this.icon
|
|
76
|
+
if (this.selected) attrs["data-native-selected"] = ""
|
|
77
|
+
return h("div", attrs)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
export const NativeSubmitButton = defineComponent({
|
|
82
|
+
name: "NativeSubmitButton",
|
|
83
|
+
props: {
|
|
84
|
+
title: { type: String, default: "Save" },
|
|
85
|
+
click: { type: String, default: "[type='submit']" }
|
|
86
|
+
},
|
|
87
|
+
render() {
|
|
88
|
+
return h("div", {
|
|
89
|
+
"data-native-submit-button": true,
|
|
90
|
+
"data-native-title": this.title,
|
|
91
|
+
"data-native-click": this.click,
|
|
92
|
+
hidden: true
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
})
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "net/http"
|
|
3
|
+
require "uri"
|
|
4
|
+
require "ruby_native/cli/credentials"
|
|
5
|
+
|
|
6
|
+
module RubyNative
|
|
7
|
+
class CLI
|
|
8
|
+
class Deploy
|
|
9
|
+
CONFIG_PATH = "config/ruby_native.yml"
|
|
10
|
+
HOST = ENV.fetch("RUBY_NATIVE_HOST", "https://rubynative.com")
|
|
11
|
+
POLL_INTERVAL = 5
|
|
12
|
+
POLL_TIMEOUT = 600
|
|
13
|
+
|
|
14
|
+
TokenExpiredError = Class.new(StandardError)
|
|
15
|
+
|
|
16
|
+
def initialize(argv)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def run
|
|
20
|
+
load_config!
|
|
21
|
+
ensure_authenticated!
|
|
22
|
+
app_id = resolve_app_id!
|
|
23
|
+
build = trigger_build(app_id)
|
|
24
|
+
poll_build_status(app_id, build)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def ensure_authenticated!
|
|
30
|
+
unless Credentials.token
|
|
31
|
+
puts "Not logged in. Run `ruby_native login` first."
|
|
32
|
+
exit 1
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def load_config!
|
|
37
|
+
unless File.exist?(CONFIG_PATH)
|
|
38
|
+
puts "config/ruby_native.yml not found. Run `rails generate ruby_native:install` first."
|
|
39
|
+
exit 1
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
require "yaml"
|
|
43
|
+
@config = YAML.load_file(CONFIG_PATH, symbolize_names: true) || {}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def resolve_app_id!
|
|
47
|
+
app_id = @config.dig(:ruby_native, :app_id)
|
|
48
|
+
app_id = link_app unless app_id
|
|
49
|
+
unless app_id
|
|
50
|
+
puts "No app selected. Run `ruby_native deploy` again."
|
|
51
|
+
exit 1
|
|
52
|
+
end
|
|
53
|
+
app_id
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# --- Build ---
|
|
57
|
+
|
|
58
|
+
def trigger_build(app_id)
|
|
59
|
+
puts "Triggering build..."
|
|
60
|
+
|
|
61
|
+
uri = URI("#{HOST}/api/v1/apps/#{app_id}/builds")
|
|
62
|
+
req = Net::HTTP::Post.new(uri)
|
|
63
|
+
req["Authorization"] = "Token #{Credentials.token}"
|
|
64
|
+
req["Content-Type"] = "application/json"
|
|
65
|
+
|
|
66
|
+
response = make_request(uri, req)
|
|
67
|
+
|
|
68
|
+
case response
|
|
69
|
+
when Net::HTTPUnauthorized
|
|
70
|
+
raise TokenExpiredError
|
|
71
|
+
when Net::HTTPCreated
|
|
72
|
+
build = JSON.parse(response.body)
|
|
73
|
+
puts "Build ##{build["number"]} (v#{build["version"]}) queued."
|
|
74
|
+
build
|
|
75
|
+
when Net::HTTPTooManyRequests
|
|
76
|
+
puts "Build limit reached. Try again later."
|
|
77
|
+
exit 1
|
|
78
|
+
when Net::HTTPConflict
|
|
79
|
+
data = JSON.parse(response.body)
|
|
80
|
+
puts data["error"]
|
|
81
|
+
exit 1
|
|
82
|
+
when Net::HTTPUnprocessableEntity
|
|
83
|
+
data = JSON.parse(response.body)
|
|
84
|
+
puts "Cannot build: #{data["error"]}"
|
|
85
|
+
exit 1
|
|
86
|
+
when Net::HTTPNotFound
|
|
87
|
+
puts "App not found. Remove ruby_native.app_id from config/ruby_native.yml and run `ruby_native deploy` again to re-link."
|
|
88
|
+
exit 1
|
|
89
|
+
else
|
|
90
|
+
puts "Failed to trigger build: #{response.code} #{response.message}"
|
|
91
|
+
exit 1
|
|
92
|
+
end
|
|
93
|
+
rescue TokenExpiredError
|
|
94
|
+
puts "Token expired. Run `ruby_native login` again."
|
|
95
|
+
exit 1
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# --- Polling ---
|
|
99
|
+
|
|
100
|
+
def poll_build_status(app_id, build)
|
|
101
|
+
build_id = build["id"]
|
|
102
|
+
last_status = build["status"]
|
|
103
|
+
puts ""
|
|
104
|
+
puts "Waiting for build to complete. Ctrl+C to exit (your build will continue)."
|
|
105
|
+
print_status(last_status)
|
|
106
|
+
|
|
107
|
+
started_at = Time.now
|
|
108
|
+
|
|
109
|
+
loop do
|
|
110
|
+
sleep POLL_INTERVAL
|
|
111
|
+
|
|
112
|
+
if Time.now - started_at > POLL_TIMEOUT
|
|
113
|
+
puts ""
|
|
114
|
+
puts "Timed out waiting for build. Check the Ruby Native dashboard for status."
|
|
115
|
+
exit 1
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
data = fetch_build_status(app_id, build_id)
|
|
119
|
+
next unless data
|
|
120
|
+
|
|
121
|
+
if data["status"] != last_status
|
|
122
|
+
last_status = data["status"]
|
|
123
|
+
print_status(last_status)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
case last_status
|
|
127
|
+
when "success", "ready"
|
|
128
|
+
puts ""
|
|
129
|
+
puts "Build succeeded!"
|
|
130
|
+
puts " Version: v#{data["version"]} (#{data["number"]})"
|
|
131
|
+
puts " Ruby Native: #{data["native_version"]}" if data["native_version"]
|
|
132
|
+
puts ""
|
|
133
|
+
puts "Your build is being submitted to TestFlight."
|
|
134
|
+
break
|
|
135
|
+
when "failure", "failed", "cancelled"
|
|
136
|
+
puts ""
|
|
137
|
+
puts "Build failed."
|
|
138
|
+
puts " Error: #{data["error_message"]}" if data["error_message"]
|
|
139
|
+
exit 1
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
rescue Interrupt
|
|
143
|
+
puts ""
|
|
144
|
+
puts ""
|
|
145
|
+
puts "Stopped polling. Your build is still running."
|
|
146
|
+
puts "Check the Ruby Native dashboard for status."
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def fetch_build_status(app_id, build_id)
|
|
150
|
+
uri = URI("#{HOST}/api/v1/apps/#{app_id}/builds/#{build_id}")
|
|
151
|
+
req = Net::HTTP::Get.new(uri)
|
|
152
|
+
req["Authorization"] = "Token #{Credentials.token}"
|
|
153
|
+
|
|
154
|
+
response = make_request(uri, req)
|
|
155
|
+
|
|
156
|
+
case response
|
|
157
|
+
when Net::HTTPSuccess
|
|
158
|
+
JSON.parse(response.body)
|
|
159
|
+
when Net::HTTPUnauthorized
|
|
160
|
+
puts ""
|
|
161
|
+
puts "Token expired. Run `ruby_native login` again."
|
|
162
|
+
exit 1
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def print_status(status)
|
|
167
|
+
labels = {
|
|
168
|
+
"queued" => "Queued",
|
|
169
|
+
"building" => "Building",
|
|
170
|
+
"processing" => "Submitting to App Store Connect"
|
|
171
|
+
}
|
|
172
|
+
label = labels[status]
|
|
173
|
+
puts " #{label}..." if label
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# --- App linking ---
|
|
177
|
+
|
|
178
|
+
def link_app
|
|
179
|
+
apps = fetch_apps
|
|
180
|
+
return unless apps
|
|
181
|
+
|
|
182
|
+
if apps.empty?
|
|
183
|
+
puts "No apps found on your account."
|
|
184
|
+
return
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
app = if apps.length == 1
|
|
188
|
+
puts "Using app: #{apps[0]["name"]}"
|
|
189
|
+
apps[0]
|
|
190
|
+
else
|
|
191
|
+
puts "Which app?"
|
|
192
|
+
apps.each_with_index do |a, i|
|
|
193
|
+
puts " #{i + 1}. #{a["name"]}"
|
|
194
|
+
end
|
|
195
|
+
print "> "
|
|
196
|
+
choice = ($stdin.gets&.strip || "").to_i
|
|
197
|
+
unless choice.between?(1, apps.length)
|
|
198
|
+
puts "Invalid choice."
|
|
199
|
+
return
|
|
200
|
+
end
|
|
201
|
+
apps[choice - 1]
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
app_id = app["public_id"]
|
|
205
|
+
write_app_id_to_config(app_id)
|
|
206
|
+
app_id
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def fetch_apps
|
|
210
|
+
uri = URI("#{HOST}/api/v1/apps")
|
|
211
|
+
req = Net::HTTP::Get.new(uri)
|
|
212
|
+
req["Authorization"] = "Token #{Credentials.token}"
|
|
213
|
+
|
|
214
|
+
response = make_request(uri, req)
|
|
215
|
+
|
|
216
|
+
case response
|
|
217
|
+
when Net::HTTPUnauthorized
|
|
218
|
+
raise TokenExpiredError
|
|
219
|
+
when Net::HTTPSuccess
|
|
220
|
+
JSON.parse(response.body)
|
|
221
|
+
else
|
|
222
|
+
puts "Failed to fetch apps: #{response.code}"
|
|
223
|
+
nil
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def write_app_id_to_config(app_id)
|
|
228
|
+
raw = File.read(CONFIG_PATH)
|
|
229
|
+
|
|
230
|
+
if raw.match?(/^ruby_native:/)
|
|
231
|
+
raw = raw.gsub(/^(ruby_native:\s*\n)/, "\\1 app_id: #{app_id}\n")
|
|
232
|
+
else
|
|
233
|
+
raw = raw.rstrip + "\n\nruby_native:\n app_id: #{app_id}\n"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
File.write(CONFIG_PATH, raw)
|
|
237
|
+
|
|
238
|
+
require "yaml"
|
|
239
|
+
@config = YAML.load_file(CONFIG_PATH, symbolize_names: true) || {}
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# --- HTTP ---
|
|
243
|
+
|
|
244
|
+
def make_request(uri, req)
|
|
245
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
246
|
+
http.use_ssl = uri.scheme == "https"
|
|
247
|
+
http.open_timeout = 10
|
|
248
|
+
http.read_timeout = 30
|
|
249
|
+
http.request(req)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
data/lib/ruby_native/cli.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "ruby_native/cli/credentials"
|
|
2
|
+
require "ruby_native/cli/deploy"
|
|
2
3
|
require "ruby_native/cli/login"
|
|
3
4
|
require "ruby_native/cli/preview"
|
|
4
5
|
require "ruby_native/cli/screenshots"
|
|
@@ -8,6 +9,8 @@ module RubyNative
|
|
|
8
9
|
def self.start(argv)
|
|
9
10
|
command = argv.shift
|
|
10
11
|
case command
|
|
12
|
+
when "deploy"
|
|
13
|
+
RubyNative::CLI::Deploy.new(argv).run
|
|
11
14
|
when "preview"
|
|
12
15
|
RubyNative::CLI::Preview.new(argv).run
|
|
13
16
|
when "screenshots"
|
|
@@ -21,6 +24,7 @@ module RubyNative
|
|
|
21
24
|
puts "Usage: ruby_native <command>"
|
|
22
25
|
puts ""
|
|
23
26
|
puts "Commands:"
|
|
27
|
+
puts " deploy Trigger an iOS build"
|
|
24
28
|
puts " login Authenticate with Ruby Native"
|
|
25
29
|
puts " logout Remove stored credentials"
|
|
26
30
|
puts " preview Start a tunnel and display a QR code"
|
data/lib/ruby_native/helper.rb
CHANGED
|
@@ -77,6 +77,13 @@ module RubyNative
|
|
|
77
77
|
])
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
def native_navbar_tag(title, &block)
|
|
81
|
+
builder = NavbarBuilder.new(self)
|
|
82
|
+
capture(builder, &block) if block
|
|
83
|
+
|
|
84
|
+
tag.div(data: { native_navbar: title }, hidden: true) { builder.to_html }
|
|
85
|
+
end
|
|
86
|
+
|
|
80
87
|
def native_haptic_data(feedback = :success, **data)
|
|
81
88
|
feedback = feedback.to_s
|
|
82
89
|
feedback = "success" if feedback.empty?
|
|
@@ -112,5 +119,62 @@ module RubyNative
|
|
|
112
119
|
@context.safe_join(@items)
|
|
113
120
|
end
|
|
114
121
|
end
|
|
122
|
+
|
|
123
|
+
class NavbarBuilder
|
|
124
|
+
def initialize(context)
|
|
125
|
+
@context = context
|
|
126
|
+
@items = []
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def button(icon: nil, title: nil, href: nil, click: nil, position: :trailing, selected: false, &block)
|
|
130
|
+
data = { native_button: "" }
|
|
131
|
+
data[:native_icon] = icon if icon
|
|
132
|
+
data[:native_title] = title if title
|
|
133
|
+
data[:native_href] = href if href
|
|
134
|
+
data[:native_click] = click if click
|
|
135
|
+
data[:native_position] = position.to_s
|
|
136
|
+
data[:native_selected] = "" if selected
|
|
137
|
+
|
|
138
|
+
if block
|
|
139
|
+
menu = NavbarMenuBuilder.new(@context)
|
|
140
|
+
@context.capture(menu, &block)
|
|
141
|
+
@items << @context.tag.div(data: data) { menu.to_html }
|
|
142
|
+
else
|
|
143
|
+
@items << @context.tag.div(data: data)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def submit_button(title: "Save", click: "[type='submit']")
|
|
148
|
+
@items << @context.tag.div(data: {
|
|
149
|
+
native_submit_button: "",
|
|
150
|
+
native_title: title,
|
|
151
|
+
native_click: click
|
|
152
|
+
})
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def to_html
|
|
156
|
+
@context.safe_join(@items)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
class NavbarMenuBuilder
|
|
161
|
+
def initialize(context)
|
|
162
|
+
@context = context
|
|
163
|
+
@items = []
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def item(title, href: nil, click: nil, icon: nil, selected: false)
|
|
167
|
+
data = { native_menu_item: "", native_title: title }
|
|
168
|
+
data[:native_href] = href if href
|
|
169
|
+
data[:native_click] = click if click
|
|
170
|
+
data[:native_icon] = icon if icon
|
|
171
|
+
data[:native_selected] = "" if selected
|
|
172
|
+
@items << @context.tag.div(data: data)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def to_html
|
|
176
|
+
@context.safe_join(@items)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
115
179
|
end
|
|
116
180
|
end
|
data/lib/ruby_native/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joe Masilotti
|
|
@@ -75,6 +75,7 @@ files:
|
|
|
75
75
|
- lib/ruby_native.rb
|
|
76
76
|
- lib/ruby_native/cli.rb
|
|
77
77
|
- lib/ruby_native/cli/credentials.rb
|
|
78
|
+
- lib/ruby_native/cli/deploy.rb
|
|
78
79
|
- lib/ruby_native/cli/login.rb
|
|
79
80
|
- lib/ruby_native/cli/preview.rb
|
|
80
81
|
- lib/ruby_native/cli/screenshots.rb
|