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.
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