spontaneous 0.2.0.alpha7 → 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (247) hide show
  1. data/Gemfile +10 -4
  2. data/Readme.markdown +1 -1
  3. data/application/css/definitions.css.scss +5 -0
  4. data/application/css/dialogue.css.scss +62 -0
  5. data/application/js/content.js +1 -1
  6. data/application/js/dom.js +1 -1
  7. data/application/js/event_source.js +3 -0
  8. data/application/js/{field_types/date_field.js → field/date.js} +2 -2
  9. data/application/js/{field_types/file_field.js → field/file.js} +2 -2
  10. data/application/js/{field_types/image_field.js → field/image.js} +54 -20
  11. data/application/js/{field_types/long_string_field.js → field/long_string.js} +2 -2
  12. data/application/js/{field_types/markdown_field.js → field/markdown.js} +2 -2
  13. data/application/js/{field_types/select_field.js → field/select.js} +2 -2
  14. data/application/js/{field_types/string_field.js → field/string.js} +21 -7
  15. data/application/js/{field_types/webvideo_field.js → field/webvideo.js} +2 -2
  16. data/application/js/field.js +2 -2
  17. data/application/js/publish.js +99 -19
  18. data/application/js/spontaneous.js +8 -8
  19. data/application/js/top_bar.js +6 -4
  20. data/db/migrations/20130109125023_add_page_publish_lock.rb +17 -0
  21. data/db/migrations/20130111161934_convert_bcrypt_passwords.rb +22 -0
  22. data/db/migrations/20130114120000_create_revision_tables.rb +106 -0
  23. data/db/migrations/20130116220423_add_index_to_archive.rb +9 -0
  24. data/lib/spontaneous/box.rb +53 -18
  25. data/lib/spontaneous/box_style.rb +2 -3
  26. data/lib/spontaneous/change.rb +39 -13
  27. data/lib/spontaneous/cli/fields.rb +29 -0
  28. data/lib/spontaneous/cli/init.rb +2 -2
  29. data/lib/spontaneous/cli/migrate.rb +0 -1
  30. data/lib/spontaneous/cli/server.rb +14 -10
  31. data/lib/spontaneous/cli/site.rb +20 -9
  32. data/lib/spontaneous/cli.rb +8 -6
  33. data/lib/spontaneous/collections/box_set.rb +11 -0
  34. data/lib/spontaneous/collections/field_set.rb +24 -1
  35. data/lib/spontaneous/concern.rb +37 -0
  36. data/lib/spontaneous/config.rb +3 -4
  37. data/lib/spontaneous/crypt/version.rb +130 -0
  38. data/lib/spontaneous/crypt.rb +84 -0
  39. data/lib/spontaneous/data_mapper/content_model/associations.rb +199 -0
  40. data/lib/spontaneous/data_mapper/content_model/column_accessors.rb +52 -0
  41. data/lib/spontaneous/data_mapper/content_model/instance_hooks.rb +34 -0
  42. data/lib/spontaneous/data_mapper/content_model/serialization.rb +54 -0
  43. data/lib/spontaneous/data_mapper/content_model/timestamps.rb +39 -0
  44. data/lib/spontaneous/data_mapper/content_model.rb +343 -0
  45. data/lib/spontaneous/data_mapper/content_table.rb +103 -0
  46. data/lib/spontaneous/data_mapper/dataset.rb +194 -0
  47. data/lib/spontaneous/data_mapper/scope.rb +195 -0
  48. data/lib/spontaneous/data_mapper.rb +161 -0
  49. data/lib/spontaneous/facet.rb +2 -2
  50. data/lib/spontaneous/field/base.rb +418 -0
  51. data/lib/spontaneous/field/date.rb +54 -0
  52. data/lib/spontaneous/{field_version.rb → field/field_version.rb} +1 -1
  53. data/lib/spontaneous/field/file.rb +100 -0
  54. data/lib/spontaneous/{field_types/image_field.rb → field/image.rb} +33 -33
  55. data/lib/spontaneous/{field_types/location_field.rb → field/location.rb} +2 -2
  56. data/lib/spontaneous/{field_types/long_string_field.rb → field/long_string.rb} +3 -3
  57. data/lib/spontaneous/field/markdown.rb +36 -0
  58. data/lib/spontaneous/{field_types/select_field.rb → field/select.rb} +4 -5
  59. data/lib/spontaneous/field/string.rb +17 -0
  60. data/lib/spontaneous/field/update.rb +156 -0
  61. data/lib/spontaneous/field/webvideo.rb +310 -0
  62. data/lib/spontaneous/field.rb +80 -0
  63. data/lib/spontaneous/generators/site/Gemfile.tt +2 -2
  64. data/lib/spontaneous/generators/site/config/environments/development.rb.tt +1 -1
  65. data/lib/spontaneous/generators/site/config/environments/production.rb.tt +1 -1
  66. data/lib/spontaneous/generators/site/lib/content.rb.tt +6 -0
  67. data/lib/spontaneous/generators/site/schema/box.rb.tt +3 -2
  68. data/lib/spontaneous/generators/site/schema/page.rb.tt +3 -1
  69. data/lib/spontaneous/generators/site/schema/piece.rb.tt +3 -1
  70. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +3 -1
  71. data/lib/spontaneous/generators/site.rb +4 -3
  72. data/lib/spontaneous/image_size.rb +8 -1
  73. data/lib/spontaneous/layout.rb +5 -1
  74. data/lib/spontaneous/loader.rb +2 -5
  75. data/lib/spontaneous/media/file.rb +11 -2
  76. data/lib/spontaneous/media/temp_file.rb +23 -0
  77. data/lib/spontaneous/media.rb +20 -39
  78. data/lib/spontaneous/{plugins → model/box}/allowed_types.rb +38 -17
  79. data/lib/spontaneous/model/box.rb +18 -0
  80. data/lib/spontaneous/{plugins → model/core}/aliases.rb +10 -14
  81. data/lib/spontaneous/{plugins → model/core}/boxes.rb +2 -2
  82. data/lib/spontaneous/{plugins → model/core}/content_groups.rb +2 -2
  83. data/lib/spontaneous/model/core/editor_class.rb +4 -0
  84. data/lib/spontaneous/{plugins → model/core}/entries.rb +19 -7
  85. data/lib/spontaneous/{plugins → model/core}/entry.rb +3 -3
  86. data/lib/spontaneous/{plugins → model/core}/fields.rb +38 -5
  87. data/lib/spontaneous/{plugins → model/core}/instance_code.rb +2 -2
  88. data/lib/spontaneous/{plugins → model/core}/media.rb +2 -12
  89. data/lib/spontaneous/{plugins → model/core}/modifications.rb +7 -6
  90. data/lib/spontaneous/model/core/page_search.rb +36 -0
  91. data/lib/spontaneous/{plugins → model/core}/permissions.rb +4 -4
  92. data/lib/spontaneous/{plugins → model/core}/prototypes.rb +4 -4
  93. data/lib/spontaneous/{plugins → model/core}/publishing.rb +93 -115
  94. data/lib/spontaneous/{plugins → model/core}/render.rb +2 -2
  95. data/lib/spontaneous/{plugins → model/core}/schema_hierarchy.rb +7 -11
  96. data/lib/spontaneous/model/core/schema_id.rb +65 -0
  97. data/lib/spontaneous/{plugins → model/core}/schema_title.rb +2 -2
  98. data/lib/spontaneous/{plugins → model/core}/serialisation.rb +2 -2
  99. data/lib/spontaneous/{plugins → model/core}/styles.rb +2 -2
  100. data/lib/spontaneous/{plugins → model/core}/supertype.rb +2 -2
  101. data/lib/spontaneous/{plugins → model/core}/visibility.rb +7 -48
  102. data/lib/spontaneous/model/core.rb +143 -0
  103. data/lib/spontaneous/{plugins → model/page}/controllers.rb +3 -3
  104. data/lib/spontaneous/{plugins → model}/page/formats.rb +2 -2
  105. data/lib/spontaneous/{plugins → model/page}/layouts.rb +2 -2
  106. data/lib/spontaneous/model/page/locks.rb +14 -0
  107. data/lib/spontaneous/{plugins → model/page}/page_tree.rb +3 -3
  108. data/lib/spontaneous/{plugins → model/page}/paths.rb +30 -12
  109. data/lib/spontaneous/{plugins → model}/page/request.rb +2 -2
  110. data/lib/spontaneous/{plugins → model/page}/site_map.rb +2 -2
  111. data/lib/spontaneous/model/page/site_timestamps.rb +44 -0
  112. data/lib/spontaneous/{page.rb → model/page.rb} +49 -28
  113. data/lib/spontaneous/{piece.rb → model/piece.rb} +7 -6
  114. data/lib/spontaneous/model.rb +97 -0
  115. data/lib/spontaneous/output/context.rb +1 -1
  116. data/lib/spontaneous/output/format.rb +4 -0
  117. data/lib/spontaneous/output/template/renderer.rb +2 -2
  118. data/lib/spontaneous/output.rb +2 -2
  119. data/lib/spontaneous/page_lock.rb +62 -0
  120. data/lib/spontaneous/page_piece.rb +1 -1
  121. data/lib/spontaneous/permissions/access_key.rb +9 -4
  122. data/lib/spontaneous/permissions/user.rb +19 -9
  123. data/lib/spontaneous/permissions.rb +2 -5
  124. data/lib/spontaneous/plugins/application/facets.rb +1 -2
  125. data/lib/spontaneous/plugins/application/features.rb +1 -1
  126. data/lib/spontaneous/plugins/application/paths.rb +1 -1
  127. data/lib/spontaneous/plugins/application/render.rb +1 -1
  128. data/lib/spontaneous/plugins/application/serialisation.rb +1 -1
  129. data/lib/spontaneous/plugins/application/state.rb +30 -1
  130. data/lib/spontaneous/plugins/application/system.rb +12 -12
  131. data/lib/spontaneous/prototypes/box_prototype.rb +1 -1
  132. data/lib/spontaneous/prototypes/field_prototype.rb +3 -6
  133. data/lib/spontaneous/prototypes/style_prototype.rb +1 -1
  134. data/lib/spontaneous/publishing/immediate.rb +77 -49
  135. data/lib/spontaneous/publishing/revision.rb +355 -0
  136. data/lib/spontaneous/publishing/simultaneous.rb +10 -49
  137. data/lib/spontaneous/publishing.rb +1 -0
  138. data/lib/spontaneous/rack/around_back.rb +1 -1
  139. data/lib/spontaneous/rack/around_front.rb +2 -4
  140. data/lib/spontaneous/rack/around_preview.rb +1 -1
  141. data/lib/spontaneous/rack/back.rb +80 -63
  142. data/lib/spontaneous/rack/cacheable_file.rb +2 -2
  143. data/lib/spontaneous/rack/cookie_authentication.rb +1 -1
  144. data/lib/spontaneous/rack/front.rb +1 -1
  145. data/lib/spontaneous/rack/helpers.rb +8 -9
  146. data/lib/spontaneous/{page_controller.rb → rack/page_controller.rb} +1 -1
  147. data/lib/spontaneous/rack/public.rb +3 -3
  148. data/lib/spontaneous/rack.rb +15 -15
  149. data/lib/spontaneous/schema/uid.rb +4 -1
  150. data/lib/spontaneous/schema.rb +57 -24
  151. data/lib/spontaneous/search/database.rb +12 -1
  152. data/lib/spontaneous/search/index.rb +34 -6
  153. data/lib/spontaneous/search/results.rb +1 -1
  154. data/lib/spontaneous/server.rb +3 -3
  155. data/lib/spontaneous/simultaneous.rb +53 -0
  156. data/lib/spontaneous/{plugins/site → site}/features.rb +2 -2
  157. data/lib/spontaneous/{plugins/site → site}/helpers.rb +2 -3
  158. data/lib/spontaneous/{plugins/site → site}/hooks.rb +2 -2
  159. data/lib/spontaneous/{plugins/site → site}/instance.rb +4 -6
  160. data/lib/spontaneous/{plugins/site → site}/level.rb +2 -2
  161. data/lib/spontaneous/{plugins/site → site}/map.rb +4 -4
  162. data/lib/spontaneous/{plugins/site → site}/paths.rb +2 -2
  163. data/lib/spontaneous/site/publishing.rb +89 -0
  164. data/lib/spontaneous/{plugins/site → site}/schema.rb +4 -4
  165. data/lib/spontaneous/{plugins/site → site}/search.rb +2 -2
  166. data/lib/spontaneous/{plugins/site → site}/selectors.rb +15 -7
  167. data/lib/spontaneous/{plugins/site → site}/state.rb +2 -2
  168. data/lib/spontaneous/{plugins/site → site}/storage.rb +2 -2
  169. data/lib/spontaneous/{plugins/site → site}/url.rb +2 -2
  170. data/lib/spontaneous/site.rb +31 -14
  171. data/lib/spontaneous/state.rb +5 -6
  172. data/lib/spontaneous/style.rb +3 -2
  173. data/lib/spontaneous/utils/database/mysql_dumper.rb +13 -0
  174. data/lib/spontaneous/utils/database/postgres_dumper.rb +5 -0
  175. data/lib/spontaneous/version.rb +1 -1
  176. data/lib/spontaneous.rb +34 -89
  177. data/spontaneous.gemspec +112 -114
  178. data/test/experimental/test_crypt.rb +158 -0
  179. data/test/experimental/test_features.rb +3 -3
  180. data/test/fixtures/example_application/config/environments/development.rb +1 -1
  181. data/test/fixtures/example_application/lib/content.rb +5 -0
  182. data/test/fixtures/example_application/schema/page.rb +2 -1
  183. data/test/fixtures/example_application/schema/piece.rb +3 -2
  184. data/test/fixtures/serialisation/class_hash.yaml.erb +5 -5
  185. data/test/fixtures/serialisation/root_hash.yaml.erb +8 -0
  186. data/test/functional/test_application.rb +12 -1
  187. data/test/functional/test_back.rb +80 -48
  188. data/test/functional/test_front.rb +39 -46
  189. data/test/functional/test_user_manager.rb +3 -9
  190. data/test/javascript/test_markdown.rb +2 -2
  191. data/test/test_helper.rb +78 -23
  192. data/test/unit/test_alias.rb +21 -15
  193. data/test/unit/test_asset_bundler.rb +3 -3
  194. data/test/unit/test_assets.rb +2 -2
  195. data/test/unit/test_async.rb +7 -6
  196. data/test/unit/test_authentication.rb +43 -37
  197. data/test/unit/test_boxes.rb +46 -21
  198. data/test/unit/test_changesets.rb +65 -20
  199. data/test/unit/test_config.rb +9 -9
  200. data/test/unit/test_content.rb +50 -51
  201. data/test/unit/test_content_inheritance.rb +6 -20
  202. data/test/unit/test_datamapper.rb +1330 -0
  203. data/test/unit/test_datamapper_content.rb +214 -0
  204. data/test/unit/test_fields.rb +543 -54
  205. data/test/unit/test_formats.rb +2 -3
  206. data/test/unit/test_generators.rb +6 -6
  207. data/test/unit/test_helpers.rb +1 -1
  208. data/test/unit/test_image_size.rb +10 -5
  209. data/test/unit/test_images.rb +17 -18
  210. data/test/unit/test_layouts.rb +18 -3
  211. data/test/unit/test_media.rb +74 -49
  212. data/test/unit/test_modifications.rb +43 -43
  213. data/test/unit/test_page.rb +7 -10
  214. data/test/unit/test_permissions.rb +3 -10
  215. data/test/unit/test_piece.rb +5 -6
  216. data/test/unit/test_plugins.rb +7 -14
  217. data/test/unit/test_prototypes.rb +3 -3
  218. data/test/unit/test_publishing.rb +49 -27
  219. data/test/unit/test_render.rb +46 -15
  220. data/test/unit/test_revisions.rb +124 -127
  221. data/test/unit/test_schema.rb +53 -58
  222. data/test/unit/test_search.rb +64 -16
  223. data/test/unit/test_serialisation.rb +4 -11
  224. data/test/unit/test_site.rb +11 -12
  225. data/test/unit/test_structure.rb +2 -5
  226. data/test/unit/test_styles.rb +22 -24
  227. data/test/unit/test_type_hierarchy.rb +7 -5
  228. data/test/unit/test_visibility.rb +79 -55
  229. metadata +128 -102
  230. data/lib/sequel/plugins/content_table_inheritance.rb +0 -203
  231. data/lib/sequel/plugins/scoped_table_name.rb +0 -54
  232. data/lib/spontaneous/content.rb +0 -129
  233. data/lib/spontaneous/field_types/date_field.rb +0 -56
  234. data/lib/spontaneous/field_types/field.rb +0 -302
  235. data/lib/spontaneous/field_types/file_field.rb +0 -68
  236. data/lib/spontaneous/field_types/markdown_field.rb +0 -38
  237. data/lib/spontaneous/field_types/string_field.rb +0 -19
  238. data/lib/spontaneous/field_types/webvideo_field.rb +0 -313
  239. data/lib/spontaneous/field_types.rb +0 -38
  240. data/lib/spontaneous/generators/site/lib/site.rb.tt +0 -4
  241. data/lib/spontaneous/plugins/field/editor_class.rb +0 -13
  242. data/lib/spontaneous/plugins/page/site_timestamps.rb +0 -28
  243. data/lib/spontaneous/plugins/page_search.rb +0 -63
  244. data/lib/spontaneous/plugins/schema_id.rb +0 -68
  245. data/lib/spontaneous/plugins/site/publishing.rb +0 -75
  246. data/lib/spontaneous/rack/fiber_pool.rb +0 -26
  247. data/test/unit/test_table_scoping.rb +0 -80
@@ -0,0 +1,62 @@
1
+ # encoding: UTF-8
2
+
3
+ module Spontaneous
4
+ class PageLock < Sequel::Model(:spontaneous_page_lock)
5
+ class InvalidPage < Spontaneous::Error; end
6
+
7
+ plugin :timestamps
8
+
9
+ many_to_one :page, :class => "Spontaneous::Content", :key => :page_id
10
+ many_to_one :content, :class => "Spontaneous::Content", :key => :content_id
11
+
12
+ def self.lock_field(field)
13
+ create(field_attributes(field).merge(
14
+ :description => field.page_lock_description
15
+ ))
16
+ rescue InvalidPage
17
+ nil
18
+ end
19
+
20
+ def self.unlock_field(field)
21
+ field_dataset(field).delete
22
+ rescue InvalidPage
23
+ nil
24
+ end
25
+
26
+ def self.field_dataset(field)
27
+ filter(field_attributes(field))
28
+ end
29
+
30
+ def self.field_attributes(field)
31
+ owner = field.owner
32
+ raise InvalidPage.new(field.id) unless owner.page
33
+ { :content_id => owner.content_instance.id,
34
+ :page_id => owner.page.id,
35
+ :field_id => field.id }
36
+ end
37
+
38
+ def field=(field)
39
+ self.field_id = field.id
40
+ end
41
+
42
+ def field
43
+ @field ||= Spontaneous::Field.find(field_id)
44
+ end
45
+
46
+ def field_name
47
+ field.name
48
+ end
49
+
50
+ def location
51
+ field, owner = self.field, self.field.owner
52
+ case owner
53
+ when Spontaneous::Content::Box
54
+ "Field ‘#{field.name}’ of box ‘#{owner.box_name}’"
55
+ when Spontaneous::Content::Page
56
+ "Field ‘#{field.name}’"
57
+ when Spontaneous::Content::Piece
58
+ "Field ‘#{field.name}’ of entry #{owner.position + 1} in box ‘#{owner.container.box_name}’"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -4,7 +4,7 @@ require 'delegate'
4
4
 
5
5
  module Spontaneous
6
6
  class PagePiece < DelegateClass(Page)
7
- include Plugins::Render
7
+ include Spontaneous::Model::Core::Render
8
8
 
9
9
  attr_accessor :owner
10
10
  attr_reader :style_id
@@ -3,7 +3,7 @@
3
3
  module Spontaneous::Permissions
4
4
  class AccessKey < Sequel::Model(:spontaneous_access_keys)
5
5
  plugin :timestamps
6
- many_to_one :user, :class => :'Spontaneous::Permissions::User'
6
+ many_to_one :user, :class => :'Spontaneous::Permissions::User', :reciprocal => :access_keys
7
7
 
8
8
  def self.authenticate(key_id, ip_address = nil)
9
9
  if key = self.for_id(key_id)
@@ -14,12 +14,17 @@ module Spontaneous::Permissions
14
14
  end
15
15
 
16
16
  def self.valid?(key_id, user)
17
- return true if (key = self.for_id(key_id)) && (key.user == user) && (key.user.enabled?)
18
- false
17
+ (key = self.for_id(key_id)) && (key.user == user) && (key.user.enabled?)
19
18
  end
20
19
 
21
20
  def self.for_id(key_id)
22
- self[:key_id => key_id]
21
+ key_dataset.call(:key_id => key_id).first
22
+ end
23
+
24
+ def self.key_dataset
25
+ @key_dataset ||= self.where(:key_id => :$key_id).
26
+ eager_graph(:user).
27
+ prepare(:select, :select_access_key_by_key)
23
28
  end
24
29
 
25
30
  def before_create
@@ -8,20 +8,27 @@ module Spontaneous::Permissions
8
8
 
9
9
  one_to_one :group, :class => :'Spontaneous::Permissions::AccessGroup'
10
10
  many_to_many :groups, :class => :'Spontaneous::Permissions::AccessGroup', :join_table => :spontaneous_groups_users
11
- one_to_many :access_keys, :class => :'Spontaneous::Permissions::AccessKey'
11
+ one_to_many :access_keys, :class => :'Spontaneous::Permissions::AccessKey', :reciprocal => :user
12
12
 
13
- set_restricted_columns(:crypted_password, :salt)
13
+ set_restricted_columns(:crypted_password)
14
14
 
15
15
  def_delegators :group, :level, :access_selector
16
16
 
17
- def self.encrypt_password(clear_password, salt)
18
- Digest::SHA1.hexdigest("--#{salt}--#{clear_password}--")
17
+ def self.login(login)
18
+ login_dataset.call(:login => login).first
19
+ end
20
+
21
+ def self.login_dataset
22
+ @login_dataset ||= self.where(:login => :$login).
23
+ eager_graph(:access_keys).
24
+ prepare(:select, :select_user_by_login)
19
25
  end
20
26
 
21
27
  def self.authenticate(login, clear_password, ip_address = nil)
22
- if user = self[:login => login, :disabled => false]
23
- crypted_password = user.encrypt_password(clear_password)
24
- if crypted_password == user.crypted_password
28
+ if (user = self[:login => login, :disabled => false])
29
+ authenticator = Spontaneous::Crypt.new(clear_password, user.crypted_password)
30
+ if authenticator.valid?
31
+ user.upgrade_authentication(authenticator) if authenticator.outdated?
25
32
  access_key = user.logged_in!(ip_address)
26
33
  return access_key
27
34
  end
@@ -91,11 +98,14 @@ module Spontaneous::Permissions
91
98
  end
92
99
 
93
100
  def encrypt_password(clear_password)
94
- self.class.encrypt_password(clear_password, salt)
101
+ Spontaneous::Crypt.hash(clear_password)
102
+ end
103
+
104
+ def upgrade_authentication(auth)
105
+ update_all :crypted_password => auth.upgrade
95
106
  end
96
107
 
97
108
  def before_save
98
- self.salt = Spontaneous::Permissions.random_string(32) if salt.blank?
99
109
  self.crypted_password = encrypt_password(password) unless password.blank?
100
110
  clear_access_keys! if disabled?
101
111
  super
@@ -1,6 +1,7 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  require 'base58'
4
+ require 'securerandom'
4
5
 
5
6
  module Spontaneous
6
7
  module Permissions
@@ -10,8 +11,6 @@ module Spontaneous
10
11
  autoload :AccessGroup, "spontaneous/permissions/access_group"
11
12
  autoload :AccessKey, "spontaneous/permissions/access_key"
12
13
 
13
- @@active_user = nil
14
-
15
14
  class << self
16
15
  # Convenience shortcut so we can do Permissions[:root]
17
16
  def [](level_name)
@@ -28,9 +27,7 @@ module Spontaneous
28
27
  end
29
28
 
30
29
  def random_string(length)
31
- bytes = ((length * Math.log10(58))/(8 * Math.log10(2))).ceil + 2
32
- string = Base58.encode(OpenSSL::Random.random_bytes(bytes).unpack("h*").first.to_i(16))
33
- string[0...(length)]
30
+ SecureRandom.urlsafe_base64(length)[0...(length)]
34
31
  end
35
32
  end
36
33
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spontaneous::Plugins::Application
4
4
  module Facets
5
- extend ActiveSupport::Concern
5
+ extend Spontaneous::Concern
6
6
 
7
7
  module ClassMethods
8
8
  def instance
@@ -16,7 +16,6 @@ module Spontaneous::Plugins::Application
16
16
  def schema
17
17
  instance.schema
18
18
  end
19
-
20
19
  end
21
20
  end
22
21
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spontaneous::Plugins::Application
4
4
  module Features
5
- extend ActiveSupport::Concern
5
+ extend Spontaneous::Concern
6
6
 
7
7
  module ClassMethods
8
8
  def register_back_controller(namespace, controller_class)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spontaneous::Plugins::Application
4
4
  module Paths
5
- extend ActiveSupport::Concern
5
+ extend Spontaneous::Concern
6
6
 
7
7
  module ClassMethods
8
8
  def load_paths
@@ -4,7 +4,7 @@ require 'cutaneous'
4
4
 
5
5
  module Spontaneous::Plugins::Application
6
6
  module Render
7
- extend ActiveSupport::Concern
7
+ extend Spontaneous::Concern
8
8
 
9
9
  module ClassMethods
10
10
  attr_accessor :render_engine
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spontaneous::Plugins::Application
4
4
  module Serialisation
5
- extend ActiveSupport::Concern
5
+ extend Spontaneous::Concern
6
6
 
7
7
  module ClassMethods
8
8
  include Spontaneous::JSON
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spontaneous::Plugins::Application
4
4
  module State
5
- extend ActiveSupport::Concern
5
+ extend Spontaneous::Concern
6
6
 
7
7
  module ClassMethods
8
8
  def init(options={})
@@ -11,6 +11,8 @@ module Spontaneous::Plugins::Application
11
11
  self.mode = options.delete(:mode) || ENV["SPOT_MODE"] || :back
12
12
  root = options.delete(:root) || ENV["SPOT_ROOT"] || Dir.pwd
13
13
  site = Spontaneous::Site.instantiate(root, environment, mode)
14
+ lib = File.expand_path(File.join(root, "lib"))
15
+ $:.push(lib) unless $:.include?(lib)
14
16
  Spontaneous::Logger.setup(:log_level => options[:log_level], :logfile => options[:logfile], :cli => options[:cli])
15
17
  site.initialize!
16
18
  site.schema.validate! if self.mode == :console
@@ -18,6 +20,33 @@ module Spontaneous::Plugins::Application
18
20
  Thread.current[:spontaneous_loaded] = true
19
21
  end
20
22
 
23
+ # This is called after definition of the Content model.
24
+ #
25
+ # Site = Spontaneous.site(Content)
26
+ #
27
+ # It is a safe way to define the content model that should be used
28
+ # globally as it checks for its existance before overwriting.
29
+ #
30
+ # I could do the assignment of Spontaneous::Content automatically
31
+ # after creation of the first content model, but this method provides
32
+ # a nice way to create the ::Site constant in the user/site code
33
+ def site(content_model)
34
+ site!(content_model) unless defined?(Spontaneous::Content)
35
+ Spontaneous::Site
36
+ end
37
+
38
+ # This forces the assignment of Spontaneous::Content, overwriting any
39
+ # previous value.
40
+ #
41
+ # Used in tests.
42
+ #
43
+ def site!(content_model)
44
+ spot = ::Spontaneous
45
+ Spontaneous.send :remove_const, :Content if defined?(Spontaneous::Content)
46
+ Spontaneous.const_set(:Content, content_model)
47
+ Spontaneous::Site
48
+ end
49
+
21
50
  def loaded?
22
51
  Thread.current[:spontaneous_loaded]
23
52
  end
@@ -4,31 +4,31 @@ require 'fiber'
4
4
 
5
5
  module Spontaneous::Plugins::Application
6
6
  module System
7
- extend ActiveSupport::Concern
7
+ extend Spontaneous::Concern
8
8
 
9
9
  module ClassMethods
10
10
  def async?
11
11
  defined?(::EM) && ::EM.reactor_running?
12
12
  end
13
13
 
14
- def system(cmd, timeout = 10)
15
- return async_system(cmd) if async?
16
- super(cmd)
14
+ def system(cmd, timeout = 10, &callback)
15
+ if async?
16
+ async_system(cmd, &callback)
17
+ else
18
+ result = super(cmd)
19
+ callback.call(result) if callback
20
+ end
17
21
  end
18
22
 
19
- # Invokes an asynchronous system call. Must be wrapped in a Fiber
20
- # such as that provided by rack-fiber_pool
21
- def async_system(cmd, timeout = 10)
23
+ # Invokes an asynchronous system call
24
+ def async_system(cmd, timeout = 10, &callback)
22
25
  deferrable = EM::DefaultDeferrable.new
23
- fiber = Fiber.current
24
26
  EM.system cmd do |output, status|
25
- deferrable.succeed
26
- fiber.resume [output, status]
27
+ deferrable.succeed(output, status)
27
28
  end
28
29
  deferrable.timeout timeout
29
30
  deferrable.errback { }
30
- output, status = Fiber.yield
31
- status.success?
31
+ deferrable.callback(&callback)
32
32
  end
33
33
  end
34
34
  end
@@ -114,7 +114,7 @@ module Spontaneous::Prototypes
114
114
  end
115
115
 
116
116
  def default_box_class
117
- defined?(::Box) ? ::Box : Spontaneous::Box
117
+ defined?(::Box) ? ::Box : owner.content_model::Box
118
118
  end
119
119
 
120
120
  ## failed attempt to exclude anonymous boxes from the list of schema classes
@@ -14,7 +14,7 @@ module Spontaneous::Prototypes
14
14
 
15
15
  # if the type is nil then try the name, this will assign sensible defaults
16
16
  # to fields like 'image' or 'date'
17
- @base_class = Spontaneous::FieldTypes[type || name]
17
+ @base_class = Spontaneous::Field[type || name]
18
18
 
19
19
  owner.const_set("#{name.to_s.camelize}Field", instance_class)
20
20
 
@@ -25,11 +25,8 @@ module Spontaneous::Prototypes
25
25
  "field/#{owner.schema_id}/#{name}"
26
26
  end
27
27
 
28
- # def schema_id
29
- # Spontaneous.schema.schema_id(self)
30
- # end
31
28
  def schema_id
32
- Spontaneous.schema.uids[@_inherited_schema_id] || Spontaneous.schema.schema_id(self)
29
+ Spontaneous.schema.uids[@_inherited_schema_id] || Spontaneous.schema.to_id(self)
33
30
  end
34
31
 
35
32
  def schema_owner
@@ -145,7 +142,7 @@ module Spontaneous::Prototypes
145
142
  values = { :name => self.name }
146
143
  values[:unprocessed_value] = default(instance) if using_default_values
147
144
  values.update(database_values || {})
148
- self.instance_class.new(values, !using_default_values).tap do |field|
145
+ self.instance_class.new(values, using_default_values).tap do |field|
149
146
  field.prototype = self
150
147
  end
151
148
  end
@@ -29,7 +29,7 @@ module Spontaneous::Prototypes
29
29
  end
30
30
 
31
31
  def schema_id
32
- Spontaneous.schema.schema_id(self)
32
+ Spontaneous.schema.to_id(self)
33
33
  end
34
34
 
35
35
  def export(user)
@@ -8,11 +8,14 @@ module Spontaneous
8
8
  class Immediate
9
9
  include ::Simultaneous::Task
10
10
 
11
- attr_reader :revision
11
+ KEEP_REVISIONS = 8
12
12
 
13
- def initialize(revision)
14
- @revision = revision
15
- @previous_revision = Site.published_revision
13
+ attr_reader :revision, :now
14
+
15
+ def initialize(revision, content_model)
16
+ @revision, @content_model = revision, content_model
17
+ @previous_revision = Spontaneous::Site.published_revision
18
+ @now = Time.now
16
19
  end
17
20
 
18
21
  def renderer
@@ -21,9 +24,9 @@ module Spontaneous
21
24
 
22
25
  def publish_pages(page_list)
23
26
  pages = page_list.flatten.map { |c|
24
- c.is_a?(S::Page) ? c.reload : S::Page[c]
27
+ c.is_a?(@content_model::Page) ? c.reload : @content_model::Page[c]
25
28
  }
26
- all_pages = S::Page.all
29
+ all_pages = @content_model::Page.all
27
30
 
28
31
  if (all_pages - pages).empty?
29
32
  # publish_all is quicker
@@ -55,9 +58,7 @@ module Spontaneous
55
58
 
56
59
  def rerender_revision
57
60
  logger.info { "Re-rendering revision #{@revision}"}
58
- S::Content.with_revision(@revision) do
59
- render_revision
60
- end
61
+ render_revision
61
62
  end
62
63
 
63
64
  protected
@@ -70,7 +71,7 @@ module Spontaneous
70
71
 
71
72
 
72
73
  def pages
73
- @pages ||= S::Page.order(:depth)
74
+ @pages ||= Spontaneous::Site.pages(@content_model)
74
75
  end
75
76
 
76
77
  # The number of times the publisher has to run through the site's pages
@@ -87,7 +88,7 @@ module Spontaneous
87
88
  }
88
89
  before_publish
89
90
  begin
90
- S::Content.publish(revision, modified_page_list) do
91
+ @content_model.publish(revision, modified_page_list) do
91
92
  render_revision
92
93
  end
93
94
  after_publish
@@ -101,11 +102,9 @@ module Spontaneous
101
102
  S::Output.renderer = renderer
102
103
  update_progress("rendering", 0)
103
104
  @pages_rendered = 0
104
- S::Content.with_identity_map do
105
- S::Content.with_visible do
106
- render_pages
107
- index_pages unless index_stages == 0
108
- end
105
+ @content_model.scope(@revision, true) do
106
+ render_pages
107
+ index_pages unless index_stages == 0
109
108
  end
110
109
  copy_static_files
111
110
  generate_rackup_file
@@ -138,12 +137,15 @@ module Spontaneous
138
137
  end
139
138
 
140
139
  # Used to calculate the progress percentage
141
- # Calculated by (pages * indexes) * (pages * formats)
140
+ # Calculated by (pages * indexes) + (pages * formats)
142
141
  # where indexes = Site.indexes.length > 0 ? 1 : 0
143
142
  # although not all pages are included by a format
144
143
  def total_pages_to_render
145
- @total_pages ||= (index_stages * pages.count) + pages.inject(0) do |total, page|
146
- total += page.outputs.length
144
+ @total_pages ||= \
145
+ begin
146
+ index_steps = (index_stages * pages.count)
147
+ output_steps = pages.map { |page| page.outputs.length }.inject(0, :+)
148
+ index_steps + output_steps
147
149
  end
148
150
  end
149
151
 
@@ -188,6 +190,15 @@ module Spontaneous
188
190
  File.open(rack_file, 'w') { |f| f.write(template) }
189
191
  end
190
192
 
193
+ # Creates a revisions/REVISION file that contains the directory name of the current
194
+ # revision. This is useful in deployment because it's a real (non-symlinked) file
195
+ # that changes with each publish and can hence be used as the target for an
196
+ # `inotifywait` script that does something with every publish.
197
+ def generate_revision_file(r)
198
+ rev_file = Spontaneous.revision_root / 'REVISION'
199
+ File.open(rev_file, 'w') { |f| f.write(Spontaneous::Media.pad_revision(r)) }
200
+ end
201
+
191
202
  def copy_static_files
192
203
  update_progress("copying_files")
193
204
  public_dest = Pathname.new(Spontaneous.revision_dir(revision) / 'public')
@@ -202,29 +213,34 @@ module Spontaneous
202
213
  def copy_facet_public_files(facet, public_dest)
203
214
  public_dirs = facet.paths.expanded(:public).map { |dir| Pathname.new(dir) }
204
215
  public_dirs.each do |public_src|
205
- if public_src.exist?
206
- public_src = public_src.realpath
207
- Dir[public_src.to_s / "**/*"].each do |src|
208
- src = Pathname.new(src)
209
- # insert facet namespace in front of path to keep URLs consistent across
210
- # the back & front servers
211
- dest = [facet.file_namespace, src.relative_path_from(public_src).to_s].compact
212
- dest = (public_dest + File.join(dest))
213
- if src.directory?
214
- dest.mkpath
215
- else
216
- case src.extname
217
- when ".scss"
218
- render_sass_template(src, dest)
219
- else
220
- link_file(src, dest)
221
- end
222
- end
216
+ next unless public_src.exist?
217
+ public_src = public_src.realpath
218
+ Dir[public_src.to_s / "**/*"].each do |src|
219
+ src = Pathname.new(src)
220
+ # insert facet namespace in front of path to keep URLs consistent across
221
+ # the back & front servers
222
+ dest = [facet.file_namespace, src.relative_path_from(public_src).to_s].compact
223
+ dest = (public_dest + File.join(dest))
224
+ if src.directory?
225
+ dest.mkpath
226
+ else
227
+ copy_public_file(src, dest)
223
228
  end
224
229
  end
225
230
  end
226
231
  end
227
232
 
233
+ def copy_public_file(src, dest)
234
+ # TODO: Add coffeescript compilation.
235
+ # Should be implemented using sprockets
236
+ case src.extname
237
+ when ".scss"
238
+ render_sass_template(src, dest)
239
+ else
240
+ link_file(src, dest)
241
+ end
242
+ end
243
+
228
244
  def link_file(src, dest)
229
245
  src_dev = File::stat(src).dev
230
246
  dst_dev = File::stat(File.dirname(dest)).dev
@@ -264,32 +280,41 @@ module Spontaneous
264
280
  update_progress("initialising")
265
281
  # when working with multiple instances it's possible to rollback the revision number
266
282
  # leaving behind old revisions > the current published_revision.
267
- S::Content.delete_revision(revision)
268
- S::Site.send(:pending_revision=, revision)
283
+ @content_model.delete_revision(revision)
284
+ Spontaneous::Site.send(:pending_revision=, revision)
269
285
  end
270
286
 
271
287
  def after_publish
272
288
  update_progress("finalising")
273
- S::Revision.create(:revision => revision, :published_at => Time.now)
274
- S::Site.send(:set_published_revision, revision)
289
+ S::Revision.create(:revision => revision, :published_at => now)
290
+ Spontaneous::Site.send(:set_published_revision, revision)
275
291
  tmp = Spontaneous.revision_dir(revision) / "tmp"
276
292
  FileUtils.mkdir_p(tmp) unless ::File.exists?(tmp)
277
- symlink_revision(revision)
293
+ write_revision(revision)
278
294
 
279
295
  begin
280
- S::Site.trigger(:after_publish, revision)
281
- S::Site.send(:pending_revision=, nil)
296
+ Spontaneous::Site.trigger(:after_publish, revision)
297
+ Spontaneous::Site.send(:pending_revision=, nil)
298
+ Spontaneous::Content.cleanup_revisions(revision, keep_revisions)
282
299
  update_progress("complete")
283
300
  rescue Exception => e
284
301
  # if a post publish hook raises an exception then we want to roll everything back
285
302
  S::Revision.filter(:revision => revision).delete
286
- S::Site.send(:set_published_revision, @previous_revision)
287
- symlink_revision(@previous_revision)
303
+ Spontaneous::Site.send(:set_published_revision, @previous_revision)
304
+ write_revision(@previous_revision)
288
305
  abort_publish(e)
289
306
  raise e
290
307
  end
291
308
  end
292
309
 
310
+ # Makes the revision live on the filesystem by symlinking the revisions/current
311
+ # directory to the revision directory and writing the current revision to the
312
+ # revisions/REVISION file.
313
+ def write_revision(revision)
314
+ symlink_revision(revision)
315
+ generate_revision_file(revision)
316
+ end
317
+
293
318
  def symlink_revision(rev)
294
319
  system("ln -nsf #{Spontaneous.revision_dir(rev)} #{Spontaneous.revision_dir}")
295
320
  end
@@ -302,12 +327,15 @@ module Spontaneous
302
327
  if r = S::Site.pending_revision
303
328
  update_progress("aborting")
304
329
  FileUtils.rm_r(Spontaneous.revision_dir(revision)) if File.exists?(Spontaneous.revision_dir(revision))
305
- S::Site.send(:pending_revision=, nil)
306
- S::Content.delete_revision(revision)
330
+ Spontaneous::Site.send(:pending_revision=, nil)
331
+ @content_model.delete_revision(revision)
307
332
  puts exception.backtrace.join("\n") if exception
308
- update_progress("error", exception)
309
333
  end
310
334
  end
335
+
336
+ def keep_revisions
337
+ Spontaneous::Site.config.keep_revisions || KEEP_REVISIONS
338
+ end
311
339
  end # Immediate
312
340
  end # Publishing
313
341
  end # Spontaneous