vapid 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|