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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -0
  3. data/LICENSE +18 -17
  4. data/Rakefile +1 -1
  5. data/application/css/core.css.scss +1 -1
  6. data/application/css/dialogue.css.scss +8 -20
  7. data/application/js/preview.js +28 -7
  8. data/application/js/publish.js +15 -4
  9. data/application/js/top_bar.js +0 -16
  10. data/application/js/views/piece_view.js +1 -1
  11. data/lib/spontaneous/asset/environment.rb +16 -1
  12. data/lib/spontaneous/box.rb +68 -0
  13. data/lib/spontaneous/capistrano/deploy.rb +7 -4
  14. data/lib/spontaneous/capistrano/sync.rb +2 -2
  15. data/lib/spontaneous/cli/init.rb +70 -19
  16. data/lib/spontaneous/cli/init/db.rb +34 -55
  17. data/lib/spontaneous/cli/init/mysql.rb +5 -5
  18. data/lib/spontaneous/cli/init/postgresql.rb +8 -9
  19. data/lib/spontaneous/cli/init/sqlite.rb +1 -2
  20. data/lib/spontaneous/cli/migrate.rb +0 -1
  21. data/lib/spontaneous/cli/site.rb +4 -0
  22. data/lib/spontaneous/collections/entry_set.rb +11 -0
  23. data/lib/spontaneous/data_mapper/content_model.rb +2 -0
  24. data/lib/spontaneous/data_mapper/content_model/serialization.rb +2 -2
  25. data/lib/spontaneous/extensions/array.rb +12 -2
  26. data/lib/spontaneous/field/base.rb +10 -0
  27. data/lib/spontaneous/field/file.rb +32 -2
  28. data/lib/spontaneous/field/image.rb +24 -2
  29. data/lib/spontaneous/field/select.rb +8 -0
  30. data/lib/spontaneous/field/webvideo.rb +8 -0
  31. data/lib/spontaneous/generators/site/config/initializers/fields.rb +55 -0
  32. data/lib/spontaneous/json.rb +3 -2
  33. data/lib/spontaneous/logger.rb +2 -2
  34. data/lib/spontaneous/media/file.rb +3 -3
  35. data/lib/spontaneous/media/image/attributes.rb +72 -6
  36. data/lib/spontaneous/media/image/renderable.rb +53 -20
  37. data/lib/spontaneous/media/store.rb +3 -3
  38. data/lib/spontaneous/media/store/backend.rb +16 -0
  39. data/lib/spontaneous/media/store/cloud.rb +52 -12
  40. data/lib/spontaneous/media/store/local.rb +6 -3
  41. data/lib/spontaneous/model.rb +3 -0
  42. data/lib/spontaneous/model/core/entries.rb +34 -13
  43. data/lib/spontaneous/model/core/entry.rb +3 -1
  44. data/lib/spontaneous/model/page/controllers.rb +1 -2
  45. data/lib/spontaneous/model/page/paths.rb +18 -7
  46. data/lib/spontaneous/output/context.rb +0 -8
  47. data/lib/spontaneous/output/template/renderer.rb +2 -0
  48. data/lib/spontaneous/plugins/application/state.rb +0 -4
  49. data/lib/spontaneous/prototypes/field_prototype.rb +4 -0
  50. data/lib/spontaneous/publishing/immediate.rb +0 -5
  51. data/lib/spontaneous/publishing/progress.rb +2 -2
  52. data/lib/spontaneous/publishing/rerender.rb +1 -4
  53. data/lib/spontaneous/publishing/simultaneous.rb +19 -17
  54. data/lib/spontaneous/publishing/steps.rb +12 -3
  55. data/lib/spontaneous/rack.rb +2 -0
  56. data/lib/spontaneous/rack/asset_server.rb +5 -2
  57. data/lib/spontaneous/rack/back.rb +9 -1
  58. data/lib/spontaneous/rack/back/base.rb +1 -0
  59. data/lib/spontaneous/rack/back/changes.rb +5 -0
  60. data/lib/spontaneous/rack/back/preview.rb +4 -4
  61. data/lib/spontaneous/rack/back/private.rb +11 -0
  62. data/lib/spontaneous/rack/middleware/scope.rb +16 -4
  63. data/lib/spontaneous/rack/page_controller.rb +2 -2
  64. data/lib/spontaneous/rack/public.rb +52 -4
  65. data/lib/spontaneous/sequel.rb +10 -13
  66. data/lib/spontaneous/site.rb +28 -8
  67. data/lib/spontaneous/site/publishing.rb +1 -1
  68. data/lib/spontaneous/site/storage.rb +7 -4
  69. data/lib/spontaneous/tasks/environment.rake +3 -0
  70. data/lib/spontaneous/utils/database/postgres_dumper.rb +23 -2
  71. data/lib/spontaneous/version.rb +1 -1
  72. data/spontaneous.gemspec +7 -12
  73. data/test/fixtures/assets/public1/css/data.css.scss +1 -1
  74. data/test/functional/test_application.rb +15 -0
  75. data/test/functional/test_cli.rb +109 -3
  76. data/test/functional/test_front.rb +108 -10
  77. data/test/test_helper.rb +3 -3
  78. data/test/unit/fields/test_boolean_fields.rb +80 -0
  79. data/test/unit/fields/test_date_fields.rb +47 -0
  80. data/test/unit/fields/test_file_field.rb +210 -0
  81. data/test/unit/{test_images.rb → fields/test_image_fields.rb} +133 -15
  82. data/test/unit/fields/test_location_fields.rb +41 -0
  83. data/test/unit/fields/test_option_fields.rb +61 -0
  84. data/test/unit/fields/test_tag_list_fields.rb +45 -0
  85. data/test/unit/fields/test_text_fields.rb +124 -0
  86. data/test/unit/fields/test_web_video_fields.rb +198 -0
  87. data/test/unit/test_assets.rb +22 -22
  88. data/test/unit/test_boxes.rb +34 -13
  89. data/test/unit/test_changesets.rb +1 -0
  90. data/test/unit/test_extensions.rb +17 -0
  91. data/test/unit/test_fields.rb +20 -643
  92. data/test/unit/test_media.rb +9 -9
  93. data/test/unit/test_page.rb +47 -0
  94. data/test/unit/test_publishing_pipeline.rb +2 -2
  95. data/test/unit/test_serialisation.rb +37 -0
  96. data/test/unit/test_storage.rb +42 -3
  97. 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
- @public_host = @config.delete(:public_host)
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 public_url(path)
104
- if @public_host
105
- "#{@public_host}/#{join_path(path)}"
106
- else
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
- public_url_aws(path)
116
+ aws_to_url(path)
109
117
  else
110
- bucket.files.new(:key => join_path(path)).public_url
118
+ bucket.files.new(key: path).public_url
111
119
  end
112
- end
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 public_url_aws(path)
127
+ def aws_to_url(path)
120
128
  if bucket_name =~ AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX
121
- "https://#{bucket_name}.s3.amazonaws.com/#{join_path(path)}"
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
- "https://s3.amazonaws.com/#{bucket_name}/#{join_path(path)}"
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, url_path, accepts = nil)
8
- @root, @url_path, @accepts = ::File.expand_path(root_directory), url_path, accepts
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(@url_path, join_path(path))
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
@@ -99,5 +99,8 @@ module Spontaneous
99
99
  def content_model
100
100
  model.content_model
101
101
  end
102
+ def site
103
+ content_model.schema.site
104
+ end
102
105
  end
103
106
  end
@@ -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.owner = self
113
- if page
114
- child_page.depth = page.depth + 1
115
- page.unordered_children << child_page
116
- child_page.parent = page
117
- child_page.update_path
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.owner = self
124
- piece.page = page if page
125
- piece.depth = (content_depth || 0) + 1
126
- insert_with_style(:piece, index, piece, box)
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
@@ -24,7 +24,9 @@ module Spontaneous::Model::Core
24
24
  owner.boxes.sid(box_sid) if owner
25
25
  end
26
26
 
27
- alias_method :container, :box
27
+ def container
28
+ box
29
+ end
28
30
 
29
31
  def first?
30
32
  container.contents.first == self
@@ -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
- "page-#{Time.now.strftime('%Y%m%d-%H%M%S')}"
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
- /^page-\d{8}-\d{6}$/ === slug
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(parent.path, slug)
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.
@@ -127,6 +127,8 @@ module Spontaneous::Output::Template
127
127
  end
128
128
 
129
129
  class PublishedRenderer < Renderer
130
+ attr_reader :revision
131
+
130
132
  def initialize(site, revision, cache = Spontaneous::Output.cache_templates?)
131
133
  super(site, cache)
132
134
  @revision = revision
@@ -69,10 +69,6 @@ module Spontaneous::Plugins::Application
69
69
  instance.config
70
70
  end
71
71
 
72
- def db_settings
73
- YAML.load_file(root / "config/database.yml")[environment]
74
- end
75
-
76
72
  def database
77
73
  Spontaneous::Site.instance.database
78
74
  end
@@ -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]
@@ -28,10 +28,5 @@ module Spontaneous::Publishing
28
28
  def rerender
29
29
  Rerender.new(@site, @revision, @steps).rerender
30
30
  end
31
-
32
- def rerender_revision
33
- logger.info { "Re-rendering revision #{@revision}"}
34
- render_revision
35
- end
36
31
  end
37
32
  end
@@ -136,8 +136,8 @@ module Spontaneous::Publishing
136
136
  send_event
137
137
  end
138
138
 
139
- def send_event(stage = current_stage, percentage = percentage)
140
- ::Simultaneous.send_event('publish_progress', {:state => stage, :progress => percentage}.to_json)
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
@@ -8,10 +8,7 @@ module Spontaneous::Publishing
8
8
  model.scope(revision, true) do
9
9
  pipeline.run(site, revision, [], progress)
10
10
  end
11
- end
12
-
13
- def progress
14
- @progress ||= Progress::Stdout.new
11
+ progress.done
15
12
  end
16
13
  end
17
14
  end
@@ -5,24 +5,26 @@ module Spontaneous
5
5
  module Publishing
6
6
  class Simultaneous
7
7
 
8
- def self.task_name
9
- # TODO: add site name to this to make it unique on a server
10
- :publish
8
+ def self.publish_task
9
+ [:publish, "site publish"]
11
10
  end
12
11
 
13
- def self.register_task
14
- task = "site publish"
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
- :niceness => niceness,
19
- :logfile => logfile
19
+ niceness: niceness,
20
+ logfile: "log/publish.log"
20
21
  }
21
- task_params = {}
22
- Spontaneous::Simultaneous.register(task_name, task, task_options, task_params)
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
- register_task
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(task_name, {"pages" => page_list})
36
+ Spontaneous::Simultaneous.fire(:publish, {"pages" => page_list})
39
37
  end
40
38
 
41
39
  def publish_all
42
- Spontaneous::Simultaneous.fire(task_name)
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