vapid 0.1.2 → 0.1.3
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/CHANGELOG.md +4 -0
- data/lib/vapid.rb +2 -0
- data/lib/vapid/api.rb +74 -0
- data/lib/vapid/builder.rb +18 -14
- data/lib/vapid/cli.rb +83 -3
- data/lib/vapid/controllers/dashboard_controller.rb +5 -2
- data/lib/vapid/controllers/project_controller.rb +24 -2
- data/lib/vapid/deployer.rb +179 -0
- data/lib/vapid/generator_template/assets/favicon.ico +0 -0
- data/lib/vapid/generator_template/config/settings.yml.tt +4 -3
- data/lib/vapid/generator_template/templates/index.html.erb +34 -7
- data/lib/vapid/models/group.rb +1 -1
- data/lib/vapid/server.rb +14 -15
- data/lib/vapid/settings.rb +29 -7
- data/lib/vapid/template.rb +5 -2
- data/lib/vapid/template/node.rb +3 -1
- data/lib/vapid/template/parser.rb +2 -0
- data/lib/vapid/version.rb +1 -1
- data/vapid.gemspec +4 -1
- metadata +48 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9dca8a0632172a7cd5c13c853dada608dd278b60
|
4
|
+
data.tar.gz: 4ec0be44b5311e4e49718c9588c4ee604389c1ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ffff0e3149dafc97c930f1eb5fc275070ff40ea2938a3523319062c0f1fb32e605b1e60a7905a4c7056685f90f21c941abd62a5ff8697e1206313fc97c74aae
|
7
|
+
data.tar.gz: 3ab1f4b9be6f3039c6b360d6d7506f5ac6456189a85b2d23060a6e9a8e5e679b724b9eca873fd8776cf86b3993570460d8a925c6ceaeab93cf02d163956892d3
|
data/CHANGELOG.md
CHANGED
data/lib/vapid.rb
CHANGED
@@ -9,8 +9,10 @@ end
|
|
9
9
|
|
10
10
|
# Top-level namespace
|
11
11
|
module Vapid
|
12
|
+
autoload :API, "vapid/api"
|
12
13
|
autoload :Builder, "vapid/builder"
|
13
14
|
autoload :CLI, "vapid/cli"
|
15
|
+
autoload :Deployer, "vapid/deployer"
|
14
16
|
autoload :Directive, "vapid/directive"
|
15
17
|
autoload :Directives, "vapid/directives"
|
16
18
|
autoload :Models, "vapid/models"
|
data/lib/vapid/api.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "base64"
|
3
|
+
|
4
|
+
module Vapid
|
5
|
+
# This is a big ol' placeholder class
|
6
|
+
# TODO: Think through an API
|
7
|
+
class API
|
8
|
+
def initialize(token = nil)
|
9
|
+
@conn = conn(token)
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def base_url
|
14
|
+
ENV["API_URL"] || "https://api.vapid.com"
|
15
|
+
end
|
16
|
+
|
17
|
+
def base_uri
|
18
|
+
URI.parse(self.base_url).host
|
19
|
+
end
|
20
|
+
|
21
|
+
def version
|
22
|
+
1
|
23
|
+
end
|
24
|
+
|
25
|
+
def version_url
|
26
|
+
"#{base_url}/v#{version}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def login(email, password)
|
31
|
+
basic_auth_conn = conn(email, password)
|
32
|
+
basic_auth_conn.get "auth/login"
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_site(site_name)
|
36
|
+
post "sites", name: site_name
|
37
|
+
end
|
38
|
+
|
39
|
+
def update_site(site_id, params)
|
40
|
+
patch "sites/#{site_id}", params
|
41
|
+
end
|
42
|
+
|
43
|
+
def presigned_posts(site_id, manifest)
|
44
|
+
post "sites/#{site_id}/presigned_posts", manifest: manifest
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def post(path, params)
|
50
|
+
@conn.post path do |req|
|
51
|
+
req.headers["Content-Type"] = "application/json"
|
52
|
+
req.body = params.to_json
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def patch(path, params)
|
57
|
+
@conn.patch path do |req|
|
58
|
+
req.headers["Content-Type"] = "application/json"
|
59
|
+
req.body = params.to_json
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def conn(email_or_token, password = nil)
|
64
|
+
::Faraday.new(url: self.class.version_url) do |f|
|
65
|
+
if password.nil?
|
66
|
+
f.token_auth(email_or_token)
|
67
|
+
else
|
68
|
+
f.basic_auth(email_or_token, password)
|
69
|
+
end
|
70
|
+
f.adapter :net_http
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/vapid/builder.rb
CHANGED
@@ -3,12 +3,28 @@ module Vapid
|
|
3
3
|
class Builder
|
4
4
|
class << self
|
5
5
|
def build!(template_files, &block)
|
6
|
-
|
7
|
-
groups = consolidate_into_groups(project_tree)
|
6
|
+
groups = generate_tree(template_files, &block)
|
8
7
|
update_data_model(groups)
|
9
8
|
remove_unused_groups(groups)
|
10
9
|
end
|
11
10
|
|
11
|
+
def generate_tree(template_files, &block)
|
12
|
+
project_tree = build_project_tree(template_files, &block)
|
13
|
+
consolidate_into_groups(project_tree)
|
14
|
+
end
|
15
|
+
|
16
|
+
def update_data_model(groups)
|
17
|
+
groups.each do |name, attributes|
|
18
|
+
group = Models::Group.named(name) || Models::Group.new(name: name)
|
19
|
+
group.update_attributes attributes
|
20
|
+
prune_orphaned_content(group)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove_unused_groups(used_groups)
|
25
|
+
Models::Group.where.not(name: used_groups.keys).destroy_all
|
26
|
+
end
|
27
|
+
|
12
28
|
private
|
13
29
|
|
14
30
|
def build_project_tree(template_files)
|
@@ -28,14 +44,6 @@ module Vapid
|
|
28
44
|
end
|
29
45
|
end
|
30
46
|
|
31
|
-
def update_data_model(groups)
|
32
|
-
groups.each do |name, attributes|
|
33
|
-
group = Models::Group.named(name) || Models::Group.new(name: name)
|
34
|
-
group.update_attributes attributes
|
35
|
-
prune_orphaned_content(group)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
47
|
def parse_file(file)
|
40
48
|
html = File.open(file).read
|
41
49
|
Template.new(html).tree
|
@@ -58,10 +66,6 @@ module Vapid
|
|
58
66
|
end
|
59
67
|
end
|
60
68
|
|
61
|
-
def remove_unused_groups(used_groups)
|
62
|
-
Models::Group.where.not(name: used_groups.keys).destroy_all
|
63
|
-
end
|
64
|
-
|
65
69
|
def content_directives
|
66
70
|
@content_directives ||= Directives.content_changers.keys
|
67
71
|
end
|
data/lib/vapid/cli.rb
CHANGED
@@ -2,6 +2,8 @@ require "thor"
|
|
2
2
|
require "guard"
|
3
3
|
require "guard/commander"
|
4
4
|
require "thread"
|
5
|
+
require "netrc"
|
6
|
+
require "json"
|
5
7
|
|
6
8
|
module Vapid
|
7
9
|
# Command line interface
|
@@ -55,7 +57,7 @@ module Vapid
|
|
55
57
|
ensure_user_exists(options.reset_login)
|
56
58
|
|
57
59
|
say "== Parsing templates for fields"
|
58
|
-
template_files =
|
60
|
+
template_files = files("templates")
|
59
61
|
Builder.build!(template_files) do |filepath|
|
60
62
|
say_status :parse, filepath
|
61
63
|
end
|
@@ -63,6 +65,24 @@ module Vapid
|
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
68
|
+
desc "deploy", "Deploy to Vapid's hosting service"
|
69
|
+
map %w(d -d --deploy) => :deploy
|
70
|
+
def deploy(target = ".")
|
71
|
+
inside_target(target) do
|
72
|
+
@api_key = ensure_api_key
|
73
|
+
site_id = ensure_site_id
|
74
|
+
|
75
|
+
project_files = files("templates", "assets")
|
76
|
+
Deployer.new(@api_key, site_id).deploy(project_files) do |message, color, status|
|
77
|
+
if status
|
78
|
+
say_status status, message, color
|
79
|
+
else
|
80
|
+
say "== #{message}", color
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
66
86
|
desc "version", "Show the Vapid version number"
|
67
87
|
map %w(v -v --version) => :version
|
68
88
|
def version
|
@@ -75,7 +95,7 @@ module Vapid
|
|
75
95
|
if !File.exist? File.join(target, "config.ru")
|
76
96
|
say_status :error, "Could not find a Vapid project in #{expanded_target(target)}", :red
|
77
97
|
else
|
78
|
-
|
98
|
+
ensure_cache_exists(target)
|
79
99
|
inside(target, &block)
|
80
100
|
end
|
81
101
|
end
|
@@ -84,6 +104,61 @@ module Vapid
|
|
84
104
|
File.expand_path(target)
|
85
105
|
end
|
86
106
|
|
107
|
+
def ensure_cache_exists(target)
|
108
|
+
FileUtils.mkdir_p File.join(target, ".cache")
|
109
|
+
FileUtils.touch File.join(target, ".cache", "livereload.txt")
|
110
|
+
end
|
111
|
+
|
112
|
+
def ensure_api_key
|
113
|
+
email = nil
|
114
|
+
token = nil
|
115
|
+
invalid = false
|
116
|
+
|
117
|
+
loop do
|
118
|
+
n = ::Netrc.read
|
119
|
+
email, token = n[API.base_uri]
|
120
|
+
break if token
|
121
|
+
|
122
|
+
say "== Enter your Vapid credentials, or signup at vapid.com:" unless invalid
|
123
|
+
email = ask "Email:", default: email
|
124
|
+
password = ask "Password:", echo: false
|
125
|
+
say ""
|
126
|
+
|
127
|
+
response = API.new(token).login(email, password)
|
128
|
+
if response.status == 200
|
129
|
+
token = ::JSON.parse(response.body)["api_key"]
|
130
|
+
n[API.base_uri] = email, token
|
131
|
+
n.save
|
132
|
+
else
|
133
|
+
invalid = true
|
134
|
+
say "== Invalid credentials"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
token
|
139
|
+
end
|
140
|
+
|
141
|
+
def ensure_site_id
|
142
|
+
site_id = Settings.site_id
|
143
|
+
|
144
|
+
loop do
|
145
|
+
break if site_id.present?
|
146
|
+
|
147
|
+
if yes? "== Is this a new website? (yes/no)"
|
148
|
+
site_name = ask "Enter a name:"
|
149
|
+
response = API.new(@api_key).create_site(site_name)
|
150
|
+
if response.status == 200
|
151
|
+
site_id = ::JSON.parse(response.body)["id"]
|
152
|
+
end
|
153
|
+
else
|
154
|
+
site_id = ask "Enter the site_id (found on vapid.com):"
|
155
|
+
end
|
156
|
+
Settings.replace_site_id(site_id) if site_id.present?
|
157
|
+
end
|
158
|
+
|
159
|
+
site_id
|
160
|
+
end
|
161
|
+
|
87
162
|
def ensure_user_exists(force)
|
88
163
|
return if !force && Models::User.any?
|
89
164
|
|
@@ -102,7 +177,7 @@ module Vapid
|
|
102
177
|
end
|
103
178
|
|
104
179
|
loop do
|
105
|
-
user.password = ask
|
180
|
+
user.password = ask "Enter a password:", echo: false
|
106
181
|
user.valid?
|
107
182
|
say ""
|
108
183
|
break unless user.errors[:password].any?
|
@@ -120,5 +195,10 @@ module Vapid
|
|
120
195
|
raise
|
121
196
|
end
|
122
197
|
end
|
198
|
+
|
199
|
+
def files(*directories)
|
200
|
+
paths = directories.map { |dir| File.join(Dir.pwd, dir, "**/*") }
|
201
|
+
Dir[*paths].reject { |f| File.directory? f }
|
202
|
+
end
|
123
203
|
end
|
124
204
|
end
|
@@ -123,7 +123,10 @@ module Vapid
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def livereload
|
126
|
-
|
126
|
+
if settings.development?
|
127
|
+
cache_clear
|
128
|
+
FileUtils.touch(settings.reload)
|
129
|
+
end
|
127
130
|
end
|
128
131
|
|
129
132
|
def run_builder
|
@@ -183,7 +186,7 @@ module Vapid
|
|
183
186
|
@code = Settings.install_code
|
184
187
|
|
185
188
|
# TODO: Should display contextually-relevant error message
|
186
|
-
if (@code.nil? || @code.casecmp(params[:code])
|
189
|
+
if (@code.nil? || @code.casecmp(params[:code]).zero?) && user.save
|
187
190
|
run_builder
|
188
191
|
session[:user_id] = user.id
|
189
192
|
flash[:success] = "Successfully created admin account."
|
@@ -7,11 +7,25 @@ module Vapid
|
|
7
7
|
# rubocop:disable Metrics/AbcSize, MethodLength
|
8
8
|
def self.registered(app)
|
9
9
|
app.configure do
|
10
|
+
root_path = Pathname.new(app.settings.root)
|
11
|
+
tmp_path = Pathname.new(app.settings.project_cache)
|
12
|
+
relative_path = tmp_path.relative_path_from(root_path)
|
13
|
+
|
14
|
+
app.set :cache_enabled, false
|
15
|
+
app.set :cache_path, File.join(relative_path, "server")
|
10
16
|
app.set :layouts, File.join(app.settings.project_views, "layouts")
|
11
17
|
end
|
12
18
|
|
19
|
+
app.configure :production do
|
20
|
+
app.set :cache_enabled, true
|
21
|
+
end
|
22
|
+
|
13
23
|
# rubocop:disable Lint/NestedMethodDefinition
|
14
24
|
app.helpers do
|
25
|
+
def asset_timestamp(*args)
|
26
|
+
super(args) unless settings.deploy
|
27
|
+
end
|
28
|
+
|
15
29
|
def dynamic_layout(path)
|
16
30
|
path_layout = path.split("/")[1]
|
17
31
|
if path_layout && Dir.glob(File.join(settings.layouts, "#{path_layout}.*")).any?
|
@@ -31,8 +45,16 @@ module Vapid
|
|
31
45
|
|
32
46
|
app.get "*" do
|
33
47
|
path = params[:splat].first.to_s
|
34
|
-
|
35
|
-
|
48
|
+
|
49
|
+
cache_block "#{path}_" do
|
50
|
+
html = render_or_index(path)
|
51
|
+
if app.deploy
|
52
|
+
headers["Content-Disposition"] = "inline"
|
53
|
+
html
|
54
|
+
else
|
55
|
+
Template.new(html).render
|
56
|
+
end
|
57
|
+
end
|
36
58
|
end
|
37
59
|
end
|
38
60
|
# rubocop:enable Metrics/AbcSize, MethodLength
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require "rack/test"
|
2
|
+
require "rack/mime"
|
3
|
+
require "faraday"
|
4
|
+
|
5
|
+
# TODO: DRY for path, relative_path, and build_path
|
6
|
+
|
7
|
+
module Vapid
|
8
|
+
# Deploys to Vapid's hosting service
|
9
|
+
class Deployer
|
10
|
+
MIMES = {
|
11
|
+
"text/css" => "css",
|
12
|
+
"application/javascript" => "js",
|
13
|
+
"text/html" => "html"
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize(api_key, site_id)
|
17
|
+
@api = API.new(api_key)
|
18
|
+
@site_id = site_id
|
19
|
+
end
|
20
|
+
|
21
|
+
def deploy(project_files, &cli)
|
22
|
+
@cli = cli
|
23
|
+
@project_files = project_files
|
24
|
+
@manifest = {}
|
25
|
+
@site_changed = false
|
26
|
+
|
27
|
+
begin
|
28
|
+
config_server
|
29
|
+
prepare_build_dir
|
30
|
+
build_site
|
31
|
+
return unless upload_files
|
32
|
+
update_site
|
33
|
+
rescue Exception => e
|
34
|
+
cli_say "Deploy Error: #{e.message}", color: :red
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def config_server
|
41
|
+
Server.set :deploy, true
|
42
|
+
end
|
43
|
+
|
44
|
+
def prepare_build_dir
|
45
|
+
FileUtils.mkdir_p build_dir
|
46
|
+
FileUtils.rm_rf "#{build_dir}/."
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_site
|
50
|
+
cli_say "Building site"
|
51
|
+
@project_files.each do |file|
|
52
|
+
path = relative_path(file)
|
53
|
+
next unless renderable?(path)
|
54
|
+
|
55
|
+
save_path = render_and_save(path)
|
56
|
+
@manifest["/#{path}"] = Digest::MD5.file(save_path).hexdigest
|
57
|
+
|
58
|
+
cli_say file, color: :blue, status: :build
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def upload_files
|
63
|
+
presigned_posts = request_presigned_posts
|
64
|
+
|
65
|
+
if presigned_posts.empty?
|
66
|
+
cli_say "Nothing new to deploy"
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
|
70
|
+
cli_say "Uploading files"
|
71
|
+
@manifest.each do |path, _digest|
|
72
|
+
post = presigned_posts[path]
|
73
|
+
say_path = path.gsub(/^\//, '')
|
74
|
+
|
75
|
+
if post
|
76
|
+
conn = Faraday.new(post["url"]) do |f|
|
77
|
+
f.request :multipart
|
78
|
+
f.request :url_encoded
|
79
|
+
f.adapter :net_http
|
80
|
+
end
|
81
|
+
|
82
|
+
file_path = File.join(build_dir, path)
|
83
|
+
file_mime = Rack::Mime.mime_type(File.extname(path))
|
84
|
+
file = Faraday::UploadIO.new(file_path, file_mime)
|
85
|
+
|
86
|
+
payload = post["fields"].merge file: file
|
87
|
+
conn.post "/", payload
|
88
|
+
|
89
|
+
cli_say say_path, color: :yellow, status: :upload
|
90
|
+
else
|
91
|
+
cli_say say_path, color: :blue, status: :exists
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def update_site
|
97
|
+
fields = Builder.generate_tree(Dir.glob("#{build_dir}/templates/**/*"))
|
98
|
+
response = @api.update_site @site_id, { manifest: @manifest, fields: fields }
|
99
|
+
json = ::JSON.parse(response.body)
|
100
|
+
if response.status == 200
|
101
|
+
site_url = json["url"]
|
102
|
+
cli_say "#{site_url} deployed to Vapid"
|
103
|
+
else
|
104
|
+
cli_say "Deploy Error: #{json["errors"]}", color: :red
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def render_and_save(path)
|
109
|
+
content = render(path)
|
110
|
+
write_file(path, content)
|
111
|
+
end
|
112
|
+
|
113
|
+
def request_presigned_posts
|
114
|
+
response = @api.presigned_posts @site_id, @manifest
|
115
|
+
::JSON.parse(response.body)["presigned_posts"] if response.status == 200
|
116
|
+
end
|
117
|
+
|
118
|
+
def relative_path(file)
|
119
|
+
path = Pathname.new(file).relative_path_from(cwd).to_s
|
120
|
+
convert_extensions(path)
|
121
|
+
end
|
122
|
+
|
123
|
+
def convert_extensions(path)
|
124
|
+
exts = File.basename(path).split(".")[1..-1].reverse
|
125
|
+
|
126
|
+
exts.each do |ext|
|
127
|
+
template_engine = Tilt.template_for(ext)
|
128
|
+
break unless template_engine
|
129
|
+
|
130
|
+
path.chomp! ".#{ext}"
|
131
|
+
|
132
|
+
# Special case for the last ext
|
133
|
+
if ext == exts[-1]
|
134
|
+
# TODO: Replace with Rack::Mime
|
135
|
+
ext = MIMES[template_engine.metadata[:mime_type]]
|
136
|
+
path += ".#{ext}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
path
|
141
|
+
end
|
142
|
+
|
143
|
+
def render(path)
|
144
|
+
uri = URI.escape path.sub(/^templates\//, '')
|
145
|
+
response = request.get uri
|
146
|
+
response.body
|
147
|
+
end
|
148
|
+
|
149
|
+
def write_file(path, contents)
|
150
|
+
save_path = File.join(build_dir, path)
|
151
|
+
save_dir = File.dirname(save_path)
|
152
|
+
FileUtils.mkdir_p(save_dir) unless File.directory?(save_dir)
|
153
|
+
File.open(save_path, "wb") { |f| f.write(contents) }
|
154
|
+
save_path
|
155
|
+
end
|
156
|
+
|
157
|
+
def renderable?(path)
|
158
|
+
!File.basename(path).start_with?("_") && !path.start_with?("templates/layouts")
|
159
|
+
end
|
160
|
+
|
161
|
+
def cwd
|
162
|
+
Pathname.new(Dir.pwd)
|
163
|
+
end
|
164
|
+
|
165
|
+
def build_dir
|
166
|
+
File.join(Dir.pwd, ".cache", "build")
|
167
|
+
end
|
168
|
+
|
169
|
+
def request
|
170
|
+
@request ||= ::Rack::MockRequest.new(Server)
|
171
|
+
end
|
172
|
+
|
173
|
+
def cli_say(message, color: :green, status: nil)
|
174
|
+
return unless @cli
|
175
|
+
|
176
|
+
@cli.call message, color, status
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
Binary file
|
@@ -1,11 +1,12 @@
|
|
1
|
-
|
1
|
+
shared: &shared
|
2
2
|
debug: false
|
3
|
+
site_id: ''
|
3
4
|
|
4
5
|
development:
|
5
|
-
<<: *
|
6
|
+
<<: *shared
|
6
7
|
secret_key: <%= SecureRandom.hex(64) %>
|
7
8
|
|
8
9
|
production:
|
9
|
-
<<: *
|
10
|
+
<<: *shared
|
10
11
|
install_code: <%= SecureRandom.hex(3) %>
|
11
12
|
secret_key: <%%= ENV["SECRET_KEY"] %>
|
@@ -6,13 +6,30 @@
|
|
6
6
|
<main>
|
7
7
|
<div>
|
8
8
|
<img src="https://cdn.vapid.com/logo.png" class="logo" alt="Vapid logo" onerror="this.style.display='none'">
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
|
10
|
+
<!-- BEGIN EXAMPLE (delete to uncomment) ->
|
11
|
+
|
12
|
+
<h1 vp-text="headline"></h1>
|
13
|
+
<div vp-richtext="body"></div>
|
14
|
+
|
15
|
+
<!- END EXAMPLE (delete to uncomment) -->
|
16
|
+
|
17
|
+
<div vp-hide="headline">
|
18
|
+
<p>Welcome to Vapid. This is an example page, to help you get started and understand the basics.</p>
|
19
|
+
<p>To begin, open up <code>template/index.html.erb</code> and uncomment the following HTML:</p>
|
20
|
+
|
21
|
+
<pre><code><h1 vp-text="headline"></h1>
|
12
22
|
<div vp-richtext="body"></div>
|
13
23
|
</code></pre>
|
14
|
-
|
15
|
-
|
24
|
+
|
25
|
+
<p>The <code>vp-</code> attributes you see tell Vapid two things: 1) what type of content you'd like placed inside of the tag (e.g. <code>vp-richtext</code>); and 2) what you'd like to name the variable (e.g. <code>body</code>).</p>
|
26
|
+
|
27
|
+
<p>Now visit <a href="/dashboard" target="_blank">the dashboard</a> to edit content.</p>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<div class="congrats" vp-show="headline">
|
31
|
+
Congrats, you did it!<br> Now, head on over to <a href="https://www.vapid.com" target="_blank">vapid.com</a> to learn more.
|
32
|
+
</div>
|
16
33
|
</div>
|
17
34
|
</main>
|
18
35
|
|
@@ -36,6 +53,10 @@
|
|
36
53
|
text-decoration: underline;
|
37
54
|
}
|
38
55
|
|
56
|
+
h1 {
|
57
|
+
margin-bottom: 0.5em;
|
58
|
+
}
|
59
|
+
|
39
60
|
main {
|
40
61
|
display: flex;
|
41
62
|
height: 100%;
|
@@ -48,7 +69,8 @@
|
|
48
69
|
max-width: 100%;
|
49
70
|
}
|
50
71
|
|
51
|
-
code
|
72
|
+
code,
|
73
|
+
.congrats {
|
52
74
|
display: inline-block;
|
53
75
|
background: #333;
|
54
76
|
padding: 0 0.25em;
|
@@ -56,12 +78,17 @@
|
|
56
78
|
vertical-align: bottom;
|
57
79
|
}
|
58
80
|
|
59
|
-
pre > code
|
81
|
+
pre > code,
|
82
|
+
.congrats {
|
60
83
|
display: block;
|
61
84
|
margin: 2em 0;
|
62
85
|
padding: 1em;
|
63
86
|
}
|
64
87
|
|
88
|
+
.congrats {
|
89
|
+
font-size: 14px;
|
90
|
+
}
|
91
|
+
|
65
92
|
p {
|
66
93
|
margin-bottom: 1em;
|
67
94
|
line-height: 1.3em;
|
data/lib/vapid/models/group.rb
CHANGED
@@ -9,7 +9,7 @@ module Vapid
|
|
9
9
|
has_many :records, dependent: :destroy
|
10
10
|
accepts_nested_attributes_for :records, allow_destroy: true
|
11
11
|
|
12
|
-
default_scope { order("name = '#{DEFAULT_NAME}' DESC, created_at DESC") }
|
12
|
+
default_scope { order("groups.name = '#{DEFAULT_NAME}' DESC, groups.created_at DESC") }
|
13
13
|
scope :with_fields, -> { where("fields <> ?", "{}") }
|
14
14
|
|
15
15
|
def self.named(name)
|
data/lib/vapid/server.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "sinatra/base"
|
2
2
|
require "sinatra/activerecord"
|
3
|
+
require "sinatra/cacher"
|
3
4
|
require "sinatra/extension"
|
4
5
|
require "sinatra/flash"
|
5
6
|
require "sinatra/namespace"
|
@@ -19,25 +20,23 @@ module Vapid
|
|
19
20
|
enable :logging
|
20
21
|
set :quiet, true
|
21
22
|
|
22
|
-
set :
|
23
|
-
set :gem_assets, File.join(
|
24
|
-
set :gem_views, File.join(
|
23
|
+
set :gem_dir, File.dirname(__FILE__)
|
24
|
+
set :gem_assets, File.join(gem_dir, "assets")
|
25
|
+
set :gem_views, File.join(gem_dir, "views")
|
25
26
|
|
26
|
-
set :
|
27
|
-
set :project_assets, File.join(
|
28
|
-
set :project_views, File.join(
|
27
|
+
set :project_dir, Dir.pwd
|
28
|
+
set :project_assets, File.join(project_dir, "assets")
|
29
|
+
set :project_views, File.join(project_dir, "templates")
|
30
|
+
set :project_cache, File.join(project_dir, ".cache")
|
29
31
|
set :project_uploads, File.join(project_assets, "uploads")
|
30
32
|
|
31
|
-
set :vendor_assets, File.join(gem_pwd, "vendor")
|
32
|
-
|
33
|
-
set :uri_root, "assets"
|
34
33
|
set :public_folder, project_assets
|
35
|
-
|
36
|
-
set :sprockets, Sprockets::Environment.new
|
37
|
-
|
38
|
-
set :cache, File.join(project_pwd, ".cache")
|
39
|
-
set :reload, File.join(cache, "livereload.txt")
|
40
34
|
set :protect_from_csrf, true
|
35
|
+
set :reload, File.join(project_cache, "livereload.txt")
|
36
|
+
set :sprockets, Sprockets::Environment.new
|
37
|
+
set :uri_root, "assets"
|
38
|
+
set :vendor_assets, File.join(gem_dir, "vendor")
|
39
|
+
set :deploy, false
|
41
40
|
|
42
41
|
sprockets.append_path project_assets
|
43
42
|
sprockets.append_path gem_assets
|
@@ -53,7 +52,6 @@ module Vapid
|
|
53
52
|
set :show_exceptions, :after_handler
|
54
53
|
use Rack::LiveReload, no_swf: true,
|
55
54
|
ignore: [%r{^/(dashboard)}]
|
56
|
-
FileUtils.touch settings.reload
|
57
55
|
end
|
58
56
|
|
59
57
|
configure :production do
|
@@ -84,6 +82,7 @@ module Vapid
|
|
84
82
|
render "errors/500", layout: :modal
|
85
83
|
end
|
86
84
|
|
85
|
+
register Sinatra::Cacher
|
87
86
|
register Sinatra::Flash
|
88
87
|
register Sinatra::Namespace
|
89
88
|
register Padrino::Helpers
|
data/lib/vapid/settings.rb
CHANGED
@@ -1,13 +1,35 @@
|
|
1
|
-
# Settings
|
2
1
|
module Vapid
|
2
|
+
# Settings
|
3
3
|
class Settings
|
4
|
-
|
5
|
-
(
|
6
|
-
|
4
|
+
class << self
|
5
|
+
def replace_site_id(site_id)
|
6
|
+
content = File.read(settings_file)
|
7
|
+
new_content = content.gsub(/site_id:(.*)/, "site_id: #{site_id}")
|
8
|
+
File.open(settings_file, "w") {|file| file.puts new_content }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def method_missing(sym, *)
|
14
|
+
@settings ||= load_settings
|
15
|
+
if @settings
|
16
|
+
@settings[sym.to_s]
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to_missing?(_method_name, _include_private = false)
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def settings_file
|
27
|
+
File.join(Dir.pwd, "config", "settings.yml")
|
28
|
+
end
|
7
29
|
|
8
|
-
|
9
|
-
|
10
|
-
|
30
|
+
def load_settings
|
31
|
+
File.exist?(settings_file) && YAML.load_file(settings_file)[Server.environment.to_s] || {}
|
32
|
+
end
|
11
33
|
end
|
12
34
|
end
|
13
35
|
end
|
data/lib/vapid/template.rb
CHANGED
@@ -26,8 +26,10 @@ module Vapid
|
|
26
26
|
branches
|
27
27
|
end
|
28
28
|
|
29
|
+
# rubocop:disable Metrics/AbcSize
|
29
30
|
def render
|
30
31
|
@parser = Parser.new(@html, default_group_content)
|
32
|
+
|
31
33
|
@parser.walk set_context: method(:fetch_content) do |node, content|
|
32
34
|
node.clone(content.size - 1) if node.cloneable?
|
33
35
|
record = node.clone? ? content[node.clone_index] : content.first
|
@@ -38,6 +40,7 @@ module Vapid
|
|
38
40
|
|
39
41
|
@parser.to_html
|
40
42
|
end
|
43
|
+
# rubocop:enable Metrics/AbcSize
|
41
44
|
|
42
45
|
private
|
43
46
|
|
@@ -64,7 +67,7 @@ module Vapid
|
|
64
67
|
# TODO: Figure out a better way to do this
|
65
68
|
# Maybe let the directive determine the placeholder?
|
66
69
|
def add_development_styles
|
67
|
-
(@parser.doc.at("head") || @parser.doc.at("body")).after "
|
70
|
+
(@parser.doc.at("head") || @parser.doc.at("body")).after "
|
68
71
|
<style>
|
69
72
|
[vp-placeholder]:before { opacity: 0.5; }
|
70
73
|
[vp-placeholder][vp-text]:before { content: '{ ' attr(vp-text) ' }'; }
|
@@ -73,7 +76,7 @@ module Vapid
|
|
73
76
|
[vp-placeholder][vp-audio]:before { content: '{ ' attr(vp-audio) ' }'; }
|
74
77
|
[vp-placeholder][vp-video]:before { content: '{ ' attr(vp-video) ' }'; }
|
75
78
|
</style>
|
76
|
-
"
|
79
|
+
"
|
77
80
|
end
|
78
81
|
end
|
79
82
|
end
|
data/lib/vapid/template/node.rb
CHANGED
@@ -48,9 +48,10 @@ module Vapid
|
|
48
48
|
|
49
49
|
# TODO: Abstract this, so that directives only need to pass in
|
50
50
|
# html, text, etc, and don't have to know about Nokogiri methods/concepts
|
51
|
+
# rubocop:disable MethodLength
|
51
52
|
def modify(obj)
|
52
53
|
return unless obj
|
53
|
-
|
54
|
+
|
54
55
|
obj.each do |prop, value|
|
55
56
|
if value.is_a?(String)
|
56
57
|
@node.send "#{prop}=", value
|
@@ -63,6 +64,7 @@ module Vapid
|
|
63
64
|
end
|
64
65
|
end
|
65
66
|
end
|
67
|
+
# rubocop:enable MethodLength
|
66
68
|
|
67
69
|
private
|
68
70
|
|
@@ -11,6 +11,7 @@ module Vapid
|
|
11
11
|
@context_stack = [initial_context]
|
12
12
|
end
|
13
13
|
|
14
|
+
# rubocop:disable MethodLength
|
14
15
|
def walk(node = doc_root, set_context: nil, &block)
|
15
16
|
if node.group?
|
16
17
|
context = set_context.present? ? set_context.call(node.group_expr) : node.group_expr
|
@@ -27,6 +28,7 @@ module Vapid
|
|
27
28
|
|
28
29
|
pop_context if node.group?
|
29
30
|
end
|
31
|
+
# rubocop:enable MethodLength
|
30
32
|
|
31
33
|
def to_html
|
32
34
|
@doc.to_html
|
data/lib/vapid/version.rb
CHANGED
data/vapid.gemspec
CHANGED
@@ -22,8 +22,9 @@ Gem::Specification.new do |spec|
|
|
22
22
|
|
23
23
|
spec.add_dependency "thor", "~> 0.20"
|
24
24
|
spec.add_dependency "sinatra", "~> 2.0"
|
25
|
-
spec.add_dependency "sinatra-contrib", "~> 2.0"
|
26
25
|
spec.add_dependency "sinatra-activerecord", "~> 2.0"
|
26
|
+
spec.add_dependency "sinatra-cacher", "~> 1.0"
|
27
|
+
spec.add_dependency "sinatra-contrib", "~> 2.0"
|
27
28
|
spec.add_dependency "sinatra-flash", "~> 0.3"
|
28
29
|
spec.add_dependency "padrino-helpers", "~> 0.1"
|
29
30
|
spec.add_dependency "sprockets", "~> 3.7"
|
@@ -38,6 +39,8 @@ Gem::Specification.new do |spec|
|
|
38
39
|
spec.add_dependency "rack-livereload", "~> 0.3"
|
39
40
|
spec.add_dependency "guard-livereload", "~> 2.5"
|
40
41
|
spec.add_dependency "ruby-oembed", "~> 0.12"
|
42
|
+
spec.add_dependency "netrc", "~> 0.11"
|
43
|
+
spec.add_dependency "faraday", "~> 0.13"
|
41
44
|
|
42
45
|
spec.add_development_dependency "bundler", "~> 1.1"
|
43
46
|
spec.add_development_dependency "rspec", "~> 3.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vapid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Robbin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name: sinatra-
|
42
|
+
name: sinatra-activerecord
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
@@ -53,7 +53,21 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name: sinatra-
|
56
|
+
name: sinatra-cacher
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sinatra-contrib
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
@@ -262,6 +276,34 @@ dependencies:
|
|
262
276
|
- - "~>"
|
263
277
|
- !ruby/object:Gem::Version
|
264
278
|
version: '0.12'
|
279
|
+
- !ruby/object:Gem::Dependency
|
280
|
+
name: netrc
|
281
|
+
requirement: !ruby/object:Gem::Requirement
|
282
|
+
requirements:
|
283
|
+
- - "~>"
|
284
|
+
- !ruby/object:Gem::Version
|
285
|
+
version: '0.11'
|
286
|
+
type: :runtime
|
287
|
+
prerelease: false
|
288
|
+
version_requirements: !ruby/object:Gem::Requirement
|
289
|
+
requirements:
|
290
|
+
- - "~>"
|
291
|
+
- !ruby/object:Gem::Version
|
292
|
+
version: '0.11'
|
293
|
+
- !ruby/object:Gem::Dependency
|
294
|
+
name: faraday
|
295
|
+
requirement: !ruby/object:Gem::Requirement
|
296
|
+
requirements:
|
297
|
+
- - "~>"
|
298
|
+
- !ruby/object:Gem::Version
|
299
|
+
version: '0.13'
|
300
|
+
type: :runtime
|
301
|
+
prerelease: false
|
302
|
+
version_requirements: !ruby/object:Gem::Requirement
|
303
|
+
requirements:
|
304
|
+
- - "~>"
|
305
|
+
- !ruby/object:Gem::Version
|
306
|
+
version: '0.13'
|
265
307
|
- !ruby/object:Gem::Dependency
|
266
308
|
name: bundler
|
267
309
|
requirement: !ruby/object:Gem::Requirement
|
@@ -353,6 +395,7 @@ files:
|
|
353
395
|
- bin/setup
|
354
396
|
- exe/vapid
|
355
397
|
- lib/vapid.rb
|
398
|
+
- lib/vapid/api.rb
|
356
399
|
- lib/vapid/assets/images/vapid/favicon.ico
|
357
400
|
- lib/vapid/assets/images/vapid/logo.png
|
358
401
|
- lib/vapid/assets/images/vapid/logo_inverse.png
|
@@ -370,6 +413,7 @@ files:
|
|
370
413
|
- lib/vapid/db/migrate/20170202182310_create_groups.rb
|
371
414
|
- lib/vapid/db/migrate/20170202183017_create_records.rb
|
372
415
|
- lib/vapid/db/migrate/20170202185150_create_users.rb
|
416
|
+
- lib/vapid/deployer.rb
|
373
417
|
- lib/vapid/directive.rb
|
374
418
|
- lib/vapid/directives.rb
|
375
419
|
- lib/vapid/directives/audio.rb
|