spontaneous 0.2.0.beta9 → 0.2.0.beta10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|