spontaneous 0.2.0.beta9 → 0.2.0.beta10
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 +61 -0
- data/LICENSE +18 -17
- data/Rakefile +1 -1
- data/application/css/core.css.scss +1 -1
- data/application/css/dialogue.css.scss +8 -20
- data/application/js/preview.js +28 -7
- data/application/js/publish.js +15 -4
- data/application/js/top_bar.js +0 -16
- data/application/js/views/piece_view.js +1 -1
- data/lib/spontaneous/asset/environment.rb +16 -1
- data/lib/spontaneous/box.rb +68 -0
- data/lib/spontaneous/capistrano/deploy.rb +7 -4
- data/lib/spontaneous/capistrano/sync.rb +2 -2
- data/lib/spontaneous/cli/init.rb +70 -19
- data/lib/spontaneous/cli/init/db.rb +34 -55
- data/lib/spontaneous/cli/init/mysql.rb +5 -5
- data/lib/spontaneous/cli/init/postgresql.rb +8 -9
- data/lib/spontaneous/cli/init/sqlite.rb +1 -2
- data/lib/spontaneous/cli/migrate.rb +0 -1
- data/lib/spontaneous/cli/site.rb +4 -0
- data/lib/spontaneous/collections/entry_set.rb +11 -0
- data/lib/spontaneous/data_mapper/content_model.rb +2 -0
- data/lib/spontaneous/data_mapper/content_model/serialization.rb +2 -2
- data/lib/spontaneous/extensions/array.rb +12 -2
- data/lib/spontaneous/field/base.rb +10 -0
- data/lib/spontaneous/field/file.rb +32 -2
- data/lib/spontaneous/field/image.rb +24 -2
- data/lib/spontaneous/field/select.rb +8 -0
- data/lib/spontaneous/field/webvideo.rb +8 -0
- data/lib/spontaneous/generators/site/config/initializers/fields.rb +55 -0
- data/lib/spontaneous/json.rb +3 -2
- data/lib/spontaneous/logger.rb +2 -2
- data/lib/spontaneous/media/file.rb +3 -3
- data/lib/spontaneous/media/image/attributes.rb +72 -6
- data/lib/spontaneous/media/image/renderable.rb +53 -20
- data/lib/spontaneous/media/store.rb +3 -3
- data/lib/spontaneous/media/store/backend.rb +16 -0
- data/lib/spontaneous/media/store/cloud.rb +52 -12
- data/lib/spontaneous/media/store/local.rb +6 -3
- data/lib/spontaneous/model.rb +3 -0
- data/lib/spontaneous/model/core/entries.rb +34 -13
- data/lib/spontaneous/model/core/entry.rb +3 -1
- data/lib/spontaneous/model/page/controllers.rb +1 -2
- data/lib/spontaneous/model/page/paths.rb +18 -7
- data/lib/spontaneous/output/context.rb +0 -8
- data/lib/spontaneous/output/template/renderer.rb +2 -0
- data/lib/spontaneous/plugins/application/state.rb +0 -4
- data/lib/spontaneous/prototypes/field_prototype.rb +4 -0
- data/lib/spontaneous/publishing/immediate.rb +0 -5
- data/lib/spontaneous/publishing/progress.rb +2 -2
- data/lib/spontaneous/publishing/rerender.rb +1 -4
- data/lib/spontaneous/publishing/simultaneous.rb +19 -17
- data/lib/spontaneous/publishing/steps.rb +12 -3
- data/lib/spontaneous/rack.rb +2 -0
- data/lib/spontaneous/rack/asset_server.rb +5 -2
- data/lib/spontaneous/rack/back.rb +9 -1
- data/lib/spontaneous/rack/back/base.rb +1 -0
- data/lib/spontaneous/rack/back/changes.rb +5 -0
- data/lib/spontaneous/rack/back/preview.rb +4 -4
- data/lib/spontaneous/rack/back/private.rb +11 -0
- data/lib/spontaneous/rack/middleware/scope.rb +16 -4
- data/lib/spontaneous/rack/page_controller.rb +2 -2
- data/lib/spontaneous/rack/public.rb +52 -4
- data/lib/spontaneous/sequel.rb +10 -13
- data/lib/spontaneous/site.rb +28 -8
- data/lib/spontaneous/site/publishing.rb +1 -1
- data/lib/spontaneous/site/storage.rb +7 -4
- data/lib/spontaneous/tasks/environment.rake +3 -0
- data/lib/spontaneous/utils/database/postgres_dumper.rb +23 -2
- data/lib/spontaneous/version.rb +1 -1
- data/spontaneous.gemspec +7 -12
- data/test/fixtures/assets/public1/css/data.css.scss +1 -1
- data/test/functional/test_application.rb +15 -0
- data/test/functional/test_cli.rb +109 -3
- data/test/functional/test_front.rb +108 -10
- data/test/test_helper.rb +3 -3
- data/test/unit/fields/test_boolean_fields.rb +80 -0
- data/test/unit/fields/test_date_fields.rb +47 -0
- data/test/unit/fields/test_file_field.rb +210 -0
- data/test/unit/{test_images.rb → fields/test_image_fields.rb} +133 -15
- data/test/unit/fields/test_location_fields.rb +41 -0
- data/test/unit/fields/test_option_fields.rb +61 -0
- data/test/unit/fields/test_tag_list_fields.rb +45 -0
- data/test/unit/fields/test_text_fields.rb +124 -0
- data/test/unit/fields/test_web_video_fields.rb +198 -0
- data/test/unit/test_assets.rb +22 -22
- data/test/unit/test_boxes.rb +34 -13
- data/test/unit/test_changesets.rb +1 -0
- data/test/unit/test_extensions.rb +17 -0
- data/test/unit/test_fields.rb +20 -643
- data/test/unit/test_media.rb +9 -9
- data/test/unit/test_page.rb +47 -0
- data/test/unit/test_publishing_pipeline.rb +2 -2
- data/test/unit/test_serialisation.rb +37 -0
- data/test/unit/test_storage.rb +42 -3
- metadata +37 -17
@@ -8,14 +8,14 @@ module Spontaneous::Media
|
|
8
8
|
|
9
9
|
extend self
|
10
10
|
|
11
|
-
def create(config)
|
11
|
+
def create(name, config)
|
12
12
|
case config[:provider]
|
13
13
|
when "Local", "local"
|
14
|
-
Local.new(config[:local_root], config[:url], config[:accepts])
|
14
|
+
Local.new(name, config[:local_root], config[:url], config[:accepts])
|
15
15
|
else
|
16
16
|
bucket = config.delete(:bucket)
|
17
17
|
accepts = config.delete(:accepts)
|
18
|
-
Cloud.new(config, bucket, accepts)
|
18
|
+
Cloud.new(name, config, bucket, accepts)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -2,6 +2,22 @@
|
|
2
2
|
|
3
3
|
module Spontaneous::Media::Store
|
4
4
|
class Backend
|
5
|
+
attr_reader :name
|
6
|
+
attr_accessor :url_mapper
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
@url_mapper = default_url_mapper
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_url_mapper
|
14
|
+
Proc.new { |path| path }
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_url(path)
|
18
|
+
@url_mapper.call(path)
|
19
|
+
end
|
20
|
+
|
5
21
|
def accepts?(mimetype)
|
6
22
|
return true if @accepts.nil?
|
7
23
|
true
|
@@ -41,9 +41,15 @@ module Spontaneous::Media::Store
|
|
41
41
|
# Don't verify my ssl certs when uploading images
|
42
42
|
::Excon.defaults[:ssl_verify_peer] = false
|
43
43
|
|
44
|
-
def initialize(config, bucket_name, accepts = nil)
|
44
|
+
def initialize(name, config, bucket_name, accepts = nil)
|
45
|
+
super(name)
|
45
46
|
@config, @bucket_name, @accepts = config, bucket_name, accepts
|
46
|
-
|
47
|
+
if (host = @config.delete(:public_host))
|
48
|
+
self.public_host = host
|
49
|
+
end
|
50
|
+
if (mapper = @config.delete(:url_mapper))
|
51
|
+
self.url_mapper = mapper
|
52
|
+
end
|
47
53
|
end
|
48
54
|
|
49
55
|
def open(relative_path, headers, mode, &block)
|
@@ -100,30 +106,64 @@ module Spontaneous::Media::Store
|
|
100
106
|
@bucket ||= backend.directories.get(@bucket_name)
|
101
107
|
end
|
102
108
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
109
|
+
def url_path(path)
|
110
|
+
"/" << join_path(path)
|
111
|
+
end
|
112
|
+
|
113
|
+
def default_url_mapper
|
114
|
+
Proc.new { |path|
|
107
115
|
if @config[:provider] == "AWS"
|
108
|
-
|
116
|
+
aws_to_url(path)
|
109
117
|
else
|
110
|
-
bucket.files.new(:
|
118
|
+
bucket.files.new(key: path).public_url
|
111
119
|
end
|
112
|
-
|
120
|
+
}
|
113
121
|
end
|
114
122
|
|
115
123
|
# AWS Redirects to the bucketname.s3.amazonaws.com style of public URL
|
116
124
|
# if you use the s3.amazonaws.com/bucketname/ style so to avoid a lot of
|
117
125
|
# slow redirects when loading a page's media we use the fastest available
|
118
126
|
# version
|
119
|
-
def
|
127
|
+
def aws_to_url(path)
|
120
128
|
if bucket_name =~ AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX
|
121
|
-
"https://#{bucket_name}
|
129
|
+
"https://#{bucket_name}.#{aws_s3_endpoint}#{path}"
|
130
|
+
else
|
131
|
+
"https://#{aws_s3_endpoint}/#{bucket_name}#{path}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def aws_s3_endpoint
|
136
|
+
case (region = @config[:region])
|
137
|
+
when nil, "", "us-east-1"
|
138
|
+
"s3.amazonaws.com"
|
122
139
|
else
|
123
|
-
"
|
140
|
+
"s3-#{region}.amazonaws.com"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def public_host=(host)
|
145
|
+
@public_host = host
|
146
|
+
unless host.blank?
|
147
|
+
self.url_mapper = host_url_mapper(host)
|
124
148
|
end
|
125
149
|
end
|
126
150
|
|
151
|
+
def host_url_mapper(host)
|
152
|
+
uri = URI.parse(host)
|
153
|
+
lambda { |path|
|
154
|
+
return path if path.blank?
|
155
|
+
begin
|
156
|
+
path_uri = URI.parse(path)
|
157
|
+
return path if path_uri.absolute?
|
158
|
+
url = uri.clone
|
159
|
+
url.path = path
|
160
|
+
url.to_s
|
161
|
+
rescue => e
|
162
|
+
path
|
163
|
+
end
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
127
167
|
def root
|
128
168
|
"/"
|
129
169
|
end
|
@@ -4,8 +4,9 @@ module Spontaneous::Media::Store
|
|
4
4
|
class Local < Backend
|
5
5
|
attr_reader :root
|
6
6
|
|
7
|
-
def initialize(root_directory,
|
8
|
-
|
7
|
+
def initialize(name, root_directory, url_path_root, accepts = nil)
|
8
|
+
super(name)
|
9
|
+
@root, @url_path_root, @accepts = ::File.expand_path(root_directory), url_path_root, accepts
|
9
10
|
end
|
10
11
|
|
11
12
|
def copy(existing_file, media_path, headers = {})
|
@@ -54,9 +55,11 @@ module Spontaneous::Media::Store
|
|
54
55
|
end
|
55
56
|
|
56
57
|
def public_url(path)
|
57
|
-
File.join(@
|
58
|
+
File.join(@url_path_root, join_path(path))
|
58
59
|
end
|
59
60
|
|
61
|
+
alias_method :url_path, :public_url
|
62
|
+
|
60
63
|
def local?
|
61
64
|
true
|
62
65
|
end
|
data/lib/spontaneous/model.rb
CHANGED
@@ -57,7 +57,8 @@ module Spontaneous::Model::Core
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def entry_modified!(modified_entry)
|
60
|
-
self.entry_store = all_contents.serialize_db
|
60
|
+
self.entry_store = store = all_contents.serialize_db
|
61
|
+
all_contents.update(store)
|
61
62
|
end
|
62
63
|
|
63
64
|
def contents
|
@@ -108,25 +109,43 @@ module Spontaneous::Model::Core
|
|
108
109
|
end
|
109
110
|
|
110
111
|
|
112
|
+
# TODO: This insert process is too fragile & requires too many intermediate #saves
|
113
|
+
# Perhaps some unified insertion process would simplify the requirements
|
114
|
+
#
|
115
|
+
# e.g. page.box.create(OtherPage, slug: 'something')
|
116
|
+
# page.box.build(OtherPage, slug: 'something')
|
117
|
+
#
|
118
|
+
# A lot of the problems come from the child's potential lack of an id
|
119
|
+
# because it's a new record. If I wrap the process then I do two things:
|
120
|
+
#
|
121
|
+
# 1. prevent the creation of pages without a containing box
|
122
|
+
# 2. control the 'new' state of the added instance so I don't have to
|
123
|
+
# manage two pathways
|
124
|
+
#
|
125
|
+
# This would entail making Content::new private or something.
|
126
|
+
#
|
111
127
|
def insert_page(index, child_page, box)
|
112
|
-
child_page
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
128
|
+
insert_with_style(:page, index, child_page, box) do
|
129
|
+
child_page.owner = self
|
130
|
+
if page
|
131
|
+
child_page.depth = page.depth + 1
|
132
|
+
page.unordered_children << child_page
|
133
|
+
child_page.parent = page
|
134
|
+
child_page.update_path
|
135
|
+
end
|
118
136
|
end
|
119
|
-
insert_with_style(:page, index, child_page, box)
|
120
137
|
end
|
121
138
|
|
122
139
|
def insert_piece(index, piece, box)
|
123
|
-
piece
|
124
|
-
|
125
|
-
|
126
|
-
|
140
|
+
insert_with_style(:piece, index, piece, box) do
|
141
|
+
piece.owner = self
|
142
|
+
piece.page = page if page
|
143
|
+
piece.depth = (content_depth || 0) + 1
|
144
|
+
piece.save
|
145
|
+
end
|
127
146
|
end
|
128
147
|
|
129
|
-
def insert_with_style(type, index, content, box)
|
148
|
+
def insert_with_style(type, index, content, box, &block)
|
130
149
|
self.pieces << content
|
131
150
|
entry_style = style_for_content(content, box)
|
132
151
|
content.box_sid = box.schema_id if box
|
@@ -149,6 +168,8 @@ module Spontaneous::Model::Core
|
|
149
168
|
logger.error { "Attempting to modify visible only pieces" }
|
150
169
|
raise e
|
151
170
|
end
|
171
|
+
yield if block_given?
|
172
|
+
content.save
|
152
173
|
|
153
174
|
entry
|
154
175
|
end
|
@@ -12,6 +12,7 @@ module Spontaneous::Model::Page
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def default_controller_base_class
|
15
|
+
return ::PageController if defined?(::PageController)
|
15
16
|
Spontaneous::Rack::PageController
|
16
17
|
end
|
17
18
|
|
@@ -20,7 +21,6 @@ module Spontaneous::Model::Page
|
|
20
21
|
# namespace
|
21
22
|
def controller_superclass(namespace, base_class)
|
22
23
|
return base_class unless base_class.nil?
|
23
|
-
return ::PageController if defined?(::PageController)
|
24
24
|
search = ancestors.select { |klass| klass.respond_to?(:controllers) }
|
25
25
|
controller_superclass = [namespace, :__nil__].uniq.flat_map { |n|
|
26
26
|
search.map { |type| type.controllers[n] }
|
@@ -69,7 +69,6 @@ module Spontaneous::Model::Page
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def process_root_action(site, env, format)
|
72
|
-
env[S::Constants::PATH_INFO] = S::Constants::SLASH
|
73
72
|
run_controller(site, :__nil__, env, format)
|
74
73
|
end
|
75
74
|
|
@@ -5,12 +5,12 @@ module Spontaneous::Model::Page
|
|
5
5
|
extend Spontaneous::Concern
|
6
6
|
|
7
7
|
module ClassMethods
|
8
|
-
def generate_default_slug
|
9
|
-
"
|
8
|
+
def generate_default_slug(root = 'page')
|
9
|
+
"#{root}-#{Time.now.strftime('%Y%m%d-%H%M%S')}"
|
10
10
|
end
|
11
11
|
|
12
|
-
def is_default_slug?(slug)
|
13
|
-
|
12
|
+
def is_default_slug?(slug, root = 'page')
|
13
|
+
/^#{root}-\d{8}-\d{6}$/ === slug
|
14
14
|
end
|
15
15
|
|
16
16
|
def create_root(slug, values = {})
|
@@ -94,11 +94,15 @@ module Spontaneous::Model::Page
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def has_generated_slug?
|
97
|
-
self.class.is_default_slug?(slug)
|
97
|
+
self.class.is_default_slug?(slug, default_slug_root)
|
98
98
|
end
|
99
99
|
|
100
100
|
def generate_default_slug
|
101
|
-
self.class.generate_default_slug
|
101
|
+
self.class.generate_default_slug(default_slug_root)
|
102
|
+
end
|
103
|
+
|
104
|
+
def default_slug_root
|
105
|
+
'page'
|
102
106
|
end
|
103
107
|
|
104
108
|
def is_conflicting_slug?(slug)
|
@@ -165,6 +169,13 @@ module Spontaneous::Model::Page
|
|
165
169
|
tree_root.is_private_root?
|
166
170
|
end
|
167
171
|
|
172
|
+
# Loads the current calculated path of this page from the database.
|
173
|
+
# Used by Box#path! (which is itself used by #calculate_path_with_slug below)
|
174
|
+
# It's necessary to grab this from the db because there are too many cached
|
175
|
+
# values between the box & the up-to-date value
|
176
|
+
def path!
|
177
|
+
model.dataset.select(:path).get_unfiltered_raw(id).try(:[], :path)
|
178
|
+
end
|
168
179
|
|
169
180
|
def update_path
|
170
181
|
self.path = calculate_path
|
@@ -186,7 +197,7 @@ module Spontaneous::Model::Page
|
|
186
197
|
if parent.nil?
|
187
198
|
root? ? Spontaneous::SLASH : "##{slug}"
|
188
199
|
else
|
189
|
-
File.join(
|
200
|
+
File.join(container.path!, slug)
|
190
201
|
end
|
191
202
|
end
|
192
203
|
|
@@ -90,14 +90,6 @@ module Spontaneous::Output::Context
|
|
90
90
|
content.last
|
91
91
|
end
|
92
92
|
|
93
|
-
def first?
|
94
|
-
__target.owner.pieces.first == self
|
95
|
-
end
|
96
|
-
|
97
|
-
def last?
|
98
|
-
__target.owner.pieces.last == self
|
99
|
-
end
|
100
|
-
|
101
93
|
# template takes an existing first-pass template, converts it to a second pass template
|
102
94
|
# and then returns the result for inclusion.
|
103
95
|
# This lets you share templates between the publish step and the request step.
|
@@ -130,6 +130,10 @@ module Spontaneous::Prototypes
|
|
130
130
|
end
|
131
131
|
|
132
132
|
def default(instance = nil)
|
133
|
+
instance_class.make_default_value(instance, default_value_for_instance(instance))
|
134
|
+
end
|
135
|
+
|
136
|
+
def default_value_for_instance(instance)
|
133
137
|
case (default = @options[:default])
|
134
138
|
when Proc
|
135
139
|
default[instance]
|
@@ -136,8 +136,8 @@ module Spontaneous::Publishing
|
|
136
136
|
send_event
|
137
137
|
end
|
138
138
|
|
139
|
-
def send_event(stage = current_stage,
|
140
|
-
::Simultaneous.send_event('publish_progress', {:state => stage, :progress =>
|
139
|
+
def send_event(stage = current_stage, _percentage = percentage)
|
140
|
+
::Simultaneous.send_event('publish_progress', {:state => stage, :progress => _percentage}.to_json)
|
141
141
|
rescue Errno::ECONNREFUSED
|
142
142
|
rescue Errno::ENOENT
|
143
143
|
end
|
@@ -5,24 +5,26 @@ module Spontaneous
|
|
5
5
|
module Publishing
|
6
6
|
class Simultaneous
|
7
7
|
|
8
|
-
def self.
|
9
|
-
|
10
|
-
:publish
|
8
|
+
def self.publish_task
|
9
|
+
[:publish, "site publish"]
|
11
10
|
end
|
12
11
|
|
13
|
-
def self.
|
14
|
-
|
12
|
+
def self.rerender_task
|
13
|
+
[:rerender, "site render"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_tasks
|
15
17
|
niceness = Spontaneous::Site.config.publish_niceness || 15
|
16
|
-
logfile = "log/publish.log"
|
17
18
|
task_options = {
|
18
|
-
:
|
19
|
-
:
|
19
|
+
niceness: niceness,
|
20
|
+
logfile: "log/publish.log"
|
20
21
|
}
|
21
|
-
|
22
|
-
|
22
|
+
[publish_task, rerender_task].each do |task_name, task_cmd|
|
23
|
+
Spontaneous::Simultaneous.register(task_name, task_cmd, task_options, task_params = {})
|
24
|
+
end
|
23
25
|
end
|
24
26
|
|
25
|
-
|
27
|
+
register_tasks
|
26
28
|
|
27
29
|
attr_reader :revision
|
28
30
|
|
@@ -30,16 +32,16 @@ module Spontaneous
|
|
30
32
|
@revision, @content_model = revision, content_model
|
31
33
|
end
|
32
34
|
|
33
|
-
def task_name
|
34
|
-
self.class.task_name
|
35
|
-
end
|
36
|
-
|
37
35
|
def publish_pages(page_list)
|
38
|
-
Spontaneous::Simultaneous.fire(
|
36
|
+
Spontaneous::Simultaneous.fire(:publish, {"pages" => page_list})
|
39
37
|
end
|
40
38
|
|
41
39
|
def publish_all
|
42
|
-
Spontaneous::Simultaneous.fire(
|
40
|
+
Spontaneous::Simultaneous.fire(:publish)
|
41
|
+
end
|
42
|
+
|
43
|
+
def rerender
|
44
|
+
Spontaneous::Simultaneous.fire(:rerender)
|
43
45
|
end
|
44
46
|
end # Simultaneous
|
45
47
|
end # Publishing
|