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.
- data/Gemfile +10 -4
- data/Readme.markdown +1 -1
- data/application/css/definitions.css.scss +5 -0
- data/application/css/dialogue.css.scss +62 -0
- data/application/js/content.js +1 -1
- data/application/js/dom.js +1 -1
- data/application/js/event_source.js +3 -0
- data/application/js/{field_types/date_field.js → field/date.js} +2 -2
- data/application/js/{field_types/file_field.js → field/file.js} +2 -2
- data/application/js/{field_types/image_field.js → field/image.js} +54 -20
- data/application/js/{field_types/long_string_field.js → field/long_string.js} +2 -2
- data/application/js/{field_types/markdown_field.js → field/markdown.js} +2 -2
- data/application/js/{field_types/select_field.js → field/select.js} +2 -2
- data/application/js/{field_types/string_field.js → field/string.js} +21 -7
- data/application/js/{field_types/webvideo_field.js → field/webvideo.js} +2 -2
- data/application/js/field.js +2 -2
- data/application/js/publish.js +99 -19
- data/application/js/spontaneous.js +8 -8
- data/application/js/top_bar.js +6 -4
- data/db/migrations/20130109125023_add_page_publish_lock.rb +17 -0
- data/db/migrations/20130111161934_convert_bcrypt_passwords.rb +22 -0
- data/db/migrations/20130114120000_create_revision_tables.rb +106 -0
- data/db/migrations/20130116220423_add_index_to_archive.rb +9 -0
- data/lib/spontaneous/box.rb +53 -18
- data/lib/spontaneous/box_style.rb +2 -3
- data/lib/spontaneous/change.rb +39 -13
- data/lib/spontaneous/cli/fields.rb +29 -0
- data/lib/spontaneous/cli/init.rb +2 -2
- data/lib/spontaneous/cli/migrate.rb +0 -1
- data/lib/spontaneous/cli/server.rb +14 -10
- data/lib/spontaneous/cli/site.rb +20 -9
- data/lib/spontaneous/cli.rb +8 -6
- data/lib/spontaneous/collections/box_set.rb +11 -0
- data/lib/spontaneous/collections/field_set.rb +24 -1
- data/lib/spontaneous/concern.rb +37 -0
- data/lib/spontaneous/config.rb +3 -4
- data/lib/spontaneous/crypt/version.rb +130 -0
- data/lib/spontaneous/crypt.rb +84 -0
- data/lib/spontaneous/data_mapper/content_model/associations.rb +199 -0
- data/lib/spontaneous/data_mapper/content_model/column_accessors.rb +52 -0
- data/lib/spontaneous/data_mapper/content_model/instance_hooks.rb +34 -0
- data/lib/spontaneous/data_mapper/content_model/serialization.rb +54 -0
- data/lib/spontaneous/data_mapper/content_model/timestamps.rb +39 -0
- data/lib/spontaneous/data_mapper/content_model.rb +343 -0
- data/lib/spontaneous/data_mapper/content_table.rb +103 -0
- data/lib/spontaneous/data_mapper/dataset.rb +194 -0
- data/lib/spontaneous/data_mapper/scope.rb +195 -0
- data/lib/spontaneous/data_mapper.rb +161 -0
- data/lib/spontaneous/facet.rb +2 -2
- data/lib/spontaneous/field/base.rb +418 -0
- data/lib/spontaneous/field/date.rb +54 -0
- data/lib/spontaneous/{field_version.rb → field/field_version.rb} +1 -1
- data/lib/spontaneous/field/file.rb +100 -0
- data/lib/spontaneous/{field_types/image_field.rb → field/image.rb} +33 -33
- data/lib/spontaneous/{field_types/location_field.rb → field/location.rb} +2 -2
- data/lib/spontaneous/{field_types/long_string_field.rb → field/long_string.rb} +3 -3
- data/lib/spontaneous/field/markdown.rb +36 -0
- data/lib/spontaneous/{field_types/select_field.rb → field/select.rb} +4 -5
- data/lib/spontaneous/field/string.rb +17 -0
- data/lib/spontaneous/field/update.rb +156 -0
- data/lib/spontaneous/field/webvideo.rb +310 -0
- data/lib/spontaneous/field.rb +80 -0
- data/lib/spontaneous/generators/site/Gemfile.tt +2 -2
- data/lib/spontaneous/generators/site/config/environments/development.rb.tt +1 -1
- data/lib/spontaneous/generators/site/config/environments/production.rb.tt +1 -1
- data/lib/spontaneous/generators/site/lib/content.rb.tt +6 -0
- data/lib/spontaneous/generators/site/schema/box.rb.tt +3 -2
- data/lib/spontaneous/generators/site/schema/page.rb.tt +3 -1
- data/lib/spontaneous/generators/site/schema/piece.rb.tt +3 -1
- data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +3 -1
- data/lib/spontaneous/generators/site.rb +4 -3
- data/lib/spontaneous/image_size.rb +8 -1
- data/lib/spontaneous/layout.rb +5 -1
- data/lib/spontaneous/loader.rb +2 -5
- data/lib/spontaneous/media/file.rb +11 -2
- data/lib/spontaneous/media/temp_file.rb +23 -0
- data/lib/spontaneous/media.rb +20 -39
- data/lib/spontaneous/{plugins → model/box}/allowed_types.rb +38 -17
- data/lib/spontaneous/model/box.rb +18 -0
- data/lib/spontaneous/{plugins → model/core}/aliases.rb +10 -14
- data/lib/spontaneous/{plugins → model/core}/boxes.rb +2 -2
- data/lib/spontaneous/{plugins → model/core}/content_groups.rb +2 -2
- data/lib/spontaneous/model/core/editor_class.rb +4 -0
- data/lib/spontaneous/{plugins → model/core}/entries.rb +19 -7
- data/lib/spontaneous/{plugins → model/core}/entry.rb +3 -3
- data/lib/spontaneous/{plugins → model/core}/fields.rb +38 -5
- data/lib/spontaneous/{plugins → model/core}/instance_code.rb +2 -2
- data/lib/spontaneous/{plugins → model/core}/media.rb +2 -12
- data/lib/spontaneous/{plugins → model/core}/modifications.rb +7 -6
- data/lib/spontaneous/model/core/page_search.rb +36 -0
- data/lib/spontaneous/{plugins → model/core}/permissions.rb +4 -4
- data/lib/spontaneous/{plugins → model/core}/prototypes.rb +4 -4
- data/lib/spontaneous/{plugins → model/core}/publishing.rb +93 -115
- data/lib/spontaneous/{plugins → model/core}/render.rb +2 -2
- data/lib/spontaneous/{plugins → model/core}/schema_hierarchy.rb +7 -11
- data/lib/spontaneous/model/core/schema_id.rb +65 -0
- data/lib/spontaneous/{plugins → model/core}/schema_title.rb +2 -2
- data/lib/spontaneous/{plugins → model/core}/serialisation.rb +2 -2
- data/lib/spontaneous/{plugins → model/core}/styles.rb +2 -2
- data/lib/spontaneous/{plugins → model/core}/supertype.rb +2 -2
- data/lib/spontaneous/{plugins → model/core}/visibility.rb +7 -48
- data/lib/spontaneous/model/core.rb +143 -0
- data/lib/spontaneous/{plugins → model/page}/controllers.rb +3 -3
- data/lib/spontaneous/{plugins → model}/page/formats.rb +2 -2
- data/lib/spontaneous/{plugins → model/page}/layouts.rb +2 -2
- data/lib/spontaneous/model/page/locks.rb +14 -0
- data/lib/spontaneous/{plugins → model/page}/page_tree.rb +3 -3
- data/lib/spontaneous/{plugins → model/page}/paths.rb +30 -12
- data/lib/spontaneous/{plugins → model}/page/request.rb +2 -2
- data/lib/spontaneous/{plugins → model/page}/site_map.rb +2 -2
- data/lib/spontaneous/model/page/site_timestamps.rb +44 -0
- data/lib/spontaneous/{page.rb → model/page.rb} +49 -28
- data/lib/spontaneous/{piece.rb → model/piece.rb} +7 -6
- data/lib/spontaneous/model.rb +97 -0
- data/lib/spontaneous/output/context.rb +1 -1
- data/lib/spontaneous/output/format.rb +4 -0
- data/lib/spontaneous/output/template/renderer.rb +2 -2
- data/lib/spontaneous/output.rb +2 -2
- data/lib/spontaneous/page_lock.rb +62 -0
- data/lib/spontaneous/page_piece.rb +1 -1
- data/lib/spontaneous/permissions/access_key.rb +9 -4
- data/lib/spontaneous/permissions/user.rb +19 -9
- data/lib/spontaneous/permissions.rb +2 -5
- data/lib/spontaneous/plugins/application/facets.rb +1 -2
- data/lib/spontaneous/plugins/application/features.rb +1 -1
- data/lib/spontaneous/plugins/application/paths.rb +1 -1
- data/lib/spontaneous/plugins/application/render.rb +1 -1
- data/lib/spontaneous/plugins/application/serialisation.rb +1 -1
- data/lib/spontaneous/plugins/application/state.rb +30 -1
- data/lib/spontaneous/plugins/application/system.rb +12 -12
- data/lib/spontaneous/prototypes/box_prototype.rb +1 -1
- data/lib/spontaneous/prototypes/field_prototype.rb +3 -6
- data/lib/spontaneous/prototypes/style_prototype.rb +1 -1
- data/lib/spontaneous/publishing/immediate.rb +77 -49
- data/lib/spontaneous/publishing/revision.rb +355 -0
- data/lib/spontaneous/publishing/simultaneous.rb +10 -49
- data/lib/spontaneous/publishing.rb +1 -0
- data/lib/spontaneous/rack/around_back.rb +1 -1
- data/lib/spontaneous/rack/around_front.rb +2 -4
- data/lib/spontaneous/rack/around_preview.rb +1 -1
- data/lib/spontaneous/rack/back.rb +80 -63
- data/lib/spontaneous/rack/cacheable_file.rb +2 -2
- data/lib/spontaneous/rack/cookie_authentication.rb +1 -1
- data/lib/spontaneous/rack/front.rb +1 -1
- data/lib/spontaneous/rack/helpers.rb +8 -9
- data/lib/spontaneous/{page_controller.rb → rack/page_controller.rb} +1 -1
- data/lib/spontaneous/rack/public.rb +3 -3
- data/lib/spontaneous/rack.rb +15 -15
- data/lib/spontaneous/schema/uid.rb +4 -1
- data/lib/spontaneous/schema.rb +57 -24
- data/lib/spontaneous/search/database.rb +12 -1
- data/lib/spontaneous/search/index.rb +34 -6
- data/lib/spontaneous/search/results.rb +1 -1
- data/lib/spontaneous/server.rb +3 -3
- data/lib/spontaneous/simultaneous.rb +53 -0
- data/lib/spontaneous/{plugins/site → site}/features.rb +2 -2
- data/lib/spontaneous/{plugins/site → site}/helpers.rb +2 -3
- data/lib/spontaneous/{plugins/site → site}/hooks.rb +2 -2
- data/lib/spontaneous/{plugins/site → site}/instance.rb +4 -6
- data/lib/spontaneous/{plugins/site → site}/level.rb +2 -2
- data/lib/spontaneous/{plugins/site → site}/map.rb +4 -4
- data/lib/spontaneous/{plugins/site → site}/paths.rb +2 -2
- data/lib/spontaneous/site/publishing.rb +89 -0
- data/lib/spontaneous/{plugins/site → site}/schema.rb +4 -4
- data/lib/spontaneous/{plugins/site → site}/search.rb +2 -2
- data/lib/spontaneous/{plugins/site → site}/selectors.rb +15 -7
- data/lib/spontaneous/{plugins/site → site}/state.rb +2 -2
- data/lib/spontaneous/{plugins/site → site}/storage.rb +2 -2
- data/lib/spontaneous/{plugins/site → site}/url.rb +2 -2
- data/lib/spontaneous/site.rb +31 -14
- data/lib/spontaneous/state.rb +5 -6
- data/lib/spontaneous/style.rb +3 -2
- data/lib/spontaneous/utils/database/mysql_dumper.rb +13 -0
- data/lib/spontaneous/utils/database/postgres_dumper.rb +5 -0
- data/lib/spontaneous/version.rb +1 -1
- data/lib/spontaneous.rb +34 -89
- data/spontaneous.gemspec +112 -114
- data/test/experimental/test_crypt.rb +158 -0
- data/test/experimental/test_features.rb +3 -3
- data/test/fixtures/example_application/config/environments/development.rb +1 -1
- data/test/fixtures/example_application/lib/content.rb +5 -0
- data/test/fixtures/example_application/schema/page.rb +2 -1
- data/test/fixtures/example_application/schema/piece.rb +3 -2
- data/test/fixtures/serialisation/class_hash.yaml.erb +5 -5
- data/test/fixtures/serialisation/root_hash.yaml.erb +8 -0
- data/test/functional/test_application.rb +12 -1
- data/test/functional/test_back.rb +80 -48
- data/test/functional/test_front.rb +39 -46
- data/test/functional/test_user_manager.rb +3 -9
- data/test/javascript/test_markdown.rb +2 -2
- data/test/test_helper.rb +78 -23
- data/test/unit/test_alias.rb +21 -15
- data/test/unit/test_asset_bundler.rb +3 -3
- data/test/unit/test_assets.rb +2 -2
- data/test/unit/test_async.rb +7 -6
- data/test/unit/test_authentication.rb +43 -37
- data/test/unit/test_boxes.rb +46 -21
- data/test/unit/test_changesets.rb +65 -20
- data/test/unit/test_config.rb +9 -9
- data/test/unit/test_content.rb +50 -51
- data/test/unit/test_content_inheritance.rb +6 -20
- data/test/unit/test_datamapper.rb +1330 -0
- data/test/unit/test_datamapper_content.rb +214 -0
- data/test/unit/test_fields.rb +543 -54
- data/test/unit/test_formats.rb +2 -3
- data/test/unit/test_generators.rb +6 -6
- data/test/unit/test_helpers.rb +1 -1
- data/test/unit/test_image_size.rb +10 -5
- data/test/unit/test_images.rb +17 -18
- data/test/unit/test_layouts.rb +18 -3
- data/test/unit/test_media.rb +74 -49
- data/test/unit/test_modifications.rb +43 -43
- data/test/unit/test_page.rb +7 -10
- data/test/unit/test_permissions.rb +3 -10
- data/test/unit/test_piece.rb +5 -6
- data/test/unit/test_plugins.rb +7 -14
- data/test/unit/test_prototypes.rb +3 -3
- data/test/unit/test_publishing.rb +49 -27
- data/test/unit/test_render.rb +46 -15
- data/test/unit/test_revisions.rb +124 -127
- data/test/unit/test_schema.rb +53 -58
- data/test/unit/test_search.rb +64 -16
- data/test/unit/test_serialisation.rb +4 -11
- data/test/unit/test_site.rb +11 -12
- data/test/unit/test_structure.rb +2 -5
- data/test/unit/test_styles.rb +22 -24
- data/test/unit/test_type_hierarchy.rb +7 -5
- data/test/unit/test_visibility.rb +79 -55
- metadata +128 -102
- data/lib/sequel/plugins/content_table_inheritance.rb +0 -203
- data/lib/sequel/plugins/scoped_table_name.rb +0 -54
- data/lib/spontaneous/content.rb +0 -129
- data/lib/spontaneous/field_types/date_field.rb +0 -56
- data/lib/spontaneous/field_types/field.rb +0 -302
- data/lib/spontaneous/field_types/file_field.rb +0 -68
- data/lib/spontaneous/field_types/markdown_field.rb +0 -38
- data/lib/spontaneous/field_types/string_field.rb +0 -19
- data/lib/spontaneous/field_types/webvideo_field.rb +0 -313
- data/lib/spontaneous/field_types.rb +0 -38
- data/lib/spontaneous/generators/site/lib/site.rb.tt +0 -4
- data/lib/spontaneous/plugins/field/editor_class.rb +0 -13
- data/lib/spontaneous/plugins/page/site_timestamps.rb +0 -28
- data/lib/spontaneous/plugins/page_search.rb +0 -63
- data/lib/spontaneous/plugins/schema_id.rb +0 -68
- data/lib/spontaneous/plugins/site/publishing.rb +0 -75
- data/lib/spontaneous/rack/fiber_pool.rb +0 -26
- data/test/unit/test_table_scoping.rb +0 -80
@@ -0,0 +1,37 @@
|
|
1
|
+
module Spontaneous
|
2
|
+
# Based on Concern but extended to allow running of
|
3
|
+
# a 'before_include' block before the module is included and before the
|
4
|
+
# 'included' block is run
|
5
|
+
module Concern
|
6
|
+
def self.extended(base)
|
7
|
+
base.instance_variable_set("@_dependencies", [])
|
8
|
+
end
|
9
|
+
|
10
|
+
def append_features(base)
|
11
|
+
if base.instance_variable_defined?("@_dependencies")
|
12
|
+
base.instance_variable_get("@_dependencies") << self
|
13
|
+
return false
|
14
|
+
else
|
15
|
+
return false if base < self
|
16
|
+
base.class_eval(&@_before_include_block) if instance_variable_defined?("@_before_include_block")
|
17
|
+
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
|
18
|
+
super
|
19
|
+
@_dependencies.each { |dep| base.send(:include, dep) }
|
20
|
+
base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def included(base = nil, &block)
|
25
|
+
if base.nil?
|
26
|
+
@_included_block = block
|
27
|
+
else
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def before_include(&block)
|
33
|
+
@_before_include_block = block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
data/lib/spontaneous/config.rb
CHANGED
@@ -105,9 +105,8 @@ module Spontaneous
|
|
105
105
|
def_delegators :@settings, :key?
|
106
106
|
end
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
}
|
108
|
+
DEFAULTS = {
|
109
|
+
}.freeze unless defined?(DEFAULTS)
|
111
110
|
|
112
111
|
attr_reader :environment, :mode, :env, :base, :local, :defaults
|
113
112
|
|
@@ -117,7 +116,7 @@ module Spontaneous
|
|
117
116
|
@local = Configuration.new
|
118
117
|
@env = Configuration.new
|
119
118
|
@base = Configuration.new
|
120
|
-
@defaults = Configuration.new(
|
119
|
+
@defaults = Configuration.new(DEFAULTS)
|
121
120
|
end
|
122
121
|
|
123
122
|
def load(config_root)
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'bcrypt'
|
2
|
+
|
3
|
+
module Spontaneous::Crypt
|
4
|
+
class Version
|
5
|
+
FIELD_SEP = ":".freeze
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def subclasses
|
9
|
+
@subclasses ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
def inherited(subclass)
|
13
|
+
subclasses << subclass
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def version(*args)
|
18
|
+
return @version if args.empty?
|
19
|
+
@version = args.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def serialize(hash)
|
23
|
+
Spontaneous::Crypt.serialize(version, hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid?(password, hash)
|
27
|
+
self.new(hash).valid?(password)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
extend ClassMethods
|
32
|
+
|
33
|
+
def initialize(hash)
|
34
|
+
@hash = hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def valid?(password)
|
38
|
+
# Depends on implementation
|
39
|
+
end
|
40
|
+
|
41
|
+
def outdated?
|
42
|
+
Spontaneous::Crypt.outdated?(self.class)
|
43
|
+
end
|
44
|
+
|
45
|
+
def salt
|
46
|
+
# Depends on implementation
|
47
|
+
end
|
48
|
+
|
49
|
+
# A fast version for use during testing
|
50
|
+
class Fake < Version
|
51
|
+
version 0
|
52
|
+
|
53
|
+
def self.create(password)
|
54
|
+
serialize generate(password)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.generate(password)
|
58
|
+
Digest::SHA1.hexdigest(password)
|
59
|
+
end
|
60
|
+
|
61
|
+
def valid?(password)
|
62
|
+
@hash == self.class.generate(password)
|
63
|
+
end
|
64
|
+
|
65
|
+
def outdated?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
69
|
+
def salt
|
70
|
+
""
|
71
|
+
end
|
72
|
+
end
|
73
|
+
class SHALegacy < Version
|
74
|
+
version 201102
|
75
|
+
|
76
|
+
def self.create(hash, salt)
|
77
|
+
serialize [salt, BCrypt201301.generate(hash)].join(FIELD_SEP)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.sha(salt, password)
|
81
|
+
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
82
|
+
end
|
83
|
+
|
84
|
+
def initialize(blob)
|
85
|
+
@salt, hash = blob.split(FIELD_SEP)
|
86
|
+
@bcrypt = BCrypt201301.new(hash)
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid?(password)
|
90
|
+
@bcrypt.valid?(sha(password))
|
91
|
+
end
|
92
|
+
|
93
|
+
def sha(password)
|
94
|
+
self.class.sha(@salt, password)
|
95
|
+
end
|
96
|
+
|
97
|
+
def salt
|
98
|
+
@salt
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class BCrypt201301 < Version
|
103
|
+
version 201301
|
104
|
+
|
105
|
+
def self.cost
|
106
|
+
13
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.create(password)
|
110
|
+
serialize generate(password)
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.generate(password)
|
114
|
+
::BCrypt::Password.create(password, :cost => cost)
|
115
|
+
end
|
116
|
+
|
117
|
+
def valid?(password)
|
118
|
+
self.password == password
|
119
|
+
end
|
120
|
+
|
121
|
+
def password
|
122
|
+
@password ||= ::BCrypt::Password.new(@hash)
|
123
|
+
end
|
124
|
+
|
125
|
+
def salt
|
126
|
+
password.salt
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Spontaneous
|
4
|
+
module Crypt
|
5
|
+
VERSION_SEP = "%".freeze
|
6
|
+
|
7
|
+
module ModuleMethods
|
8
|
+
def current
|
9
|
+
return version(@forced) if @forced
|
10
|
+
versions.last
|
11
|
+
end
|
12
|
+
|
13
|
+
def force_version(n)
|
14
|
+
@forced = n
|
15
|
+
end
|
16
|
+
|
17
|
+
def default_version
|
18
|
+
@forced = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def versions
|
22
|
+
Version.subclasses.sort { |v1, v2| v1.version <=> v2.version }
|
23
|
+
end
|
24
|
+
|
25
|
+
def hash(password)
|
26
|
+
current.create(password)
|
27
|
+
end
|
28
|
+
|
29
|
+
def version(version)
|
30
|
+
versions.detect { |v| v.version == version }
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid?(password, blob)
|
34
|
+
new(password, blob).valid?
|
35
|
+
end
|
36
|
+
|
37
|
+
def serialize(version, hash)
|
38
|
+
[version, hash].join VERSION_SEP
|
39
|
+
end
|
40
|
+
|
41
|
+
def version_split(blob)
|
42
|
+
version, hash = blob.split(VERSION_SEP)
|
43
|
+
[version.to_i, hash]
|
44
|
+
end
|
45
|
+
|
46
|
+
def outdated?(version)
|
47
|
+
version.version < current.version
|
48
|
+
end
|
49
|
+
|
50
|
+
def new(password, blob)
|
51
|
+
version, hash = version_split(blob)
|
52
|
+
Validator.new(password, Crypt.version(version).new(hash))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
extend ModuleMethods
|
57
|
+
|
58
|
+
class Validator
|
59
|
+
def initialize(password, version)
|
60
|
+
@password, @version = password, version
|
61
|
+
end
|
62
|
+
|
63
|
+
def valid?
|
64
|
+
@version.valid?(@password)
|
65
|
+
end
|
66
|
+
|
67
|
+
def outdated?
|
68
|
+
@version.outdated?
|
69
|
+
end
|
70
|
+
|
71
|
+
alias_method :needs_upgrade?, :outdated?
|
72
|
+
|
73
|
+
def upgrade
|
74
|
+
Spontaneous::Crypt.hash(@password)
|
75
|
+
end
|
76
|
+
|
77
|
+
def salt
|
78
|
+
@version.salt
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
require 'spontaneous/crypt/version'
|
@@ -0,0 +1,199 @@
|
|
1
|
+
module Spontaneous
|
2
|
+
module DataMapper
|
3
|
+
module ContentModel
|
4
|
+
module Associations
|
5
|
+
def inherited(subclass)
|
6
|
+
subclass.instance_variable_set(:@associations, associations.dup)
|
7
|
+
subclass.instance_variable_set(:@association_dependencies, association_dependencies.dup)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
# Provided for compatibility with Sequel models.
|
12
|
+
#
|
13
|
+
# In particular when constructing associations between
|
14
|
+
# a content model and a Sequel model, the Sequel
|
15
|
+
# association tries to automatically set the reciprocal
|
16
|
+
# value and uses Model.all_association_reflections to
|
17
|
+
# search for appropriate candidates.
|
18
|
+
#
|
19
|
+
# Returning an empty array here stops this crashing.
|
20
|
+
def all_association_reflections
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
def associations
|
25
|
+
@associations ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def association_method_module
|
29
|
+
@association_method_module ||= define_association_method_module
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_association_method_module
|
33
|
+
m = Module.new
|
34
|
+
self.const_set(:AssociationMethods, m)
|
35
|
+
self.send :include, m
|
36
|
+
m
|
37
|
+
end
|
38
|
+
|
39
|
+
ASSOCIATION_MAPPING = {
|
40
|
+
:one_to_many=>:before,
|
41
|
+
:many_to_one=>:after,
|
42
|
+
:many_to_many=>:before,
|
43
|
+
:one_to_one=>:before
|
44
|
+
}
|
45
|
+
|
46
|
+
def association_dependencies
|
47
|
+
@association_dependencies ||= {
|
48
|
+
:before_delete=>[], :before_destroy=>[],
|
49
|
+
:after_delete=>[], :after_destroy=>[]
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_association_dependency(opts)
|
54
|
+
if (action = opts[:dependent])
|
55
|
+
time = ASSOCIATION_MAPPING[opts[:association]]
|
56
|
+
hook = :"#{time}_#{action}"
|
57
|
+
association_dependencies[hook] << opts[:dataset_method]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def has_many(name, opts = {})
|
62
|
+
opts[:association] = :one_to_many
|
63
|
+
opts[:model] ||= self
|
64
|
+
opts[:mapper_method] = "#{name}_mapper"
|
65
|
+
opts[:dataset_method] = "#{name}_dataset"
|
66
|
+
opts[:add_method] = "add_#{name.to_s.singularize}"
|
67
|
+
opts[:module] ||= association_method_module
|
68
|
+
|
69
|
+
where = opts[:model].respond_to?(:where!) ? :where! : :where
|
70
|
+
|
71
|
+
define_association_method(opts, opts[:mapper_method]) {
|
72
|
+
opts[:model] == model ? self.mapper : opts[:model]
|
73
|
+
}
|
74
|
+
define_association_method(opts, opts[:dataset_method]) {
|
75
|
+
m = send(opts[:mapper_method])
|
76
|
+
c = m.respond_to?(:qualify_column) ? m.send(:qualify_column, opts[:key]) : opts[:key]
|
77
|
+
m.send(where, [[c, id]])
|
78
|
+
}
|
79
|
+
define_association_method(opts, name) { |options = {}|
|
80
|
+
cached_has_many_association(name, options)
|
81
|
+
}
|
82
|
+
define_association_method(opts, opts[:add_method]) { |member|
|
83
|
+
member.send("#{opts[:key]}=", self.id)
|
84
|
+
member.save
|
85
|
+
}
|
86
|
+
add_association_dependency(opts)
|
87
|
+
associations[name] = opts
|
88
|
+
end
|
89
|
+
|
90
|
+
def define_association_method(opts, name, &block)
|
91
|
+
opts[:module].module_eval{ define_method(name, &block) }
|
92
|
+
end
|
93
|
+
|
94
|
+
alias_method :one_to_many, :has_many
|
95
|
+
|
96
|
+
def belongs_to(name, opts = {})
|
97
|
+
opts[:association] = :many_to_one
|
98
|
+
opts[:model] ||= self
|
99
|
+
opts[:mapper_method] = "#{name}_mapper"
|
100
|
+
opts[:dataset_method] = "#{name}_dataset"
|
101
|
+
opts[:add_method] = "#{name}="
|
102
|
+
opts[:module] ||= association_method_module
|
103
|
+
|
104
|
+
where = opts[:model].respond_to?(:where!) ? :where! : :where
|
105
|
+
|
106
|
+
define_association_method(opts, opts[:mapper_method]) {
|
107
|
+
opts[:model] == model ? self.mapper : opts[:model]
|
108
|
+
}
|
109
|
+
define_association_method(opts, opts[:dataset_method]) {
|
110
|
+
m = send(opts[:mapper_method])
|
111
|
+
id = send(opts[:key])
|
112
|
+
c = m.respond_to?(:qualify_column) ? m.send(:qualify_column, :id) : :id
|
113
|
+
m.send(where, [[c, id]])
|
114
|
+
}
|
115
|
+
define_association_method(opts, name) { |options = {}|
|
116
|
+
cached_belongs_to_association(name, options)
|
117
|
+
}
|
118
|
+
define_association_method(opts, opts[:add_method]) { |owner|
|
119
|
+
set_association_cache(name, owner)
|
120
|
+
send("#{opts[:key]}=", owner.id) if owner
|
121
|
+
}
|
122
|
+
add_association_dependency(opts)
|
123
|
+
associations[name] = opts
|
124
|
+
end
|
125
|
+
|
126
|
+
alias_method :many_to_one, :belongs_to
|
127
|
+
|
128
|
+
module InstanceMethods
|
129
|
+
def after_destroy
|
130
|
+
super
|
131
|
+
model.association_dependencies[:after_delete].each{|m| send(m).delete}
|
132
|
+
model.association_dependencies[:after_destroy].each{|m| send(m).destroy}
|
133
|
+
end
|
134
|
+
|
135
|
+
def before_destroy
|
136
|
+
model.association_dependencies[:before_delete].each{|m| send(m).delete}
|
137
|
+
model.association_dependencies[:before_destroy].each{|m| send(m).destroy}
|
138
|
+
super
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def cached_belongs_to_association(name, options)
|
144
|
+
clear_association_cache(name) if options[:reload]
|
145
|
+
ac = associations_cache
|
146
|
+
unless ac.key?(name)
|
147
|
+
ac[name] = load_belongs_to_association(name)
|
148
|
+
end
|
149
|
+
ac[name]
|
150
|
+
end
|
151
|
+
|
152
|
+
def load_belongs_to_association(name)
|
153
|
+
assoc = model.associations[name]
|
154
|
+
id = send(assoc[:key])
|
155
|
+
return nil if id.nil?
|
156
|
+
self.send(assoc[:mapper_method])[id]
|
157
|
+
end
|
158
|
+
|
159
|
+
def cached_has_many_association(name, options)
|
160
|
+
clear_association_cache(name) if options[:reload]
|
161
|
+
ac = associations_cache
|
162
|
+
unless ac.key?(name)
|
163
|
+
ac[name] = load_has_many_association(name)
|
164
|
+
end
|
165
|
+
ac[name]
|
166
|
+
end
|
167
|
+
|
168
|
+
def load_has_many_association(name)
|
169
|
+
assoc = model.associations[name]
|
170
|
+
members = self.send(assoc[:dataset_method]).all
|
171
|
+
if (reciprocal = assoc[:reciprocal])
|
172
|
+
members.each do |member|
|
173
|
+
member.send :set_association_cache, reciprocal, self
|
174
|
+
end
|
175
|
+
end
|
176
|
+
members
|
177
|
+
end
|
178
|
+
|
179
|
+
def associations_cache
|
180
|
+
@associations_cache ||= {}
|
181
|
+
end
|
182
|
+
|
183
|
+
def set_association_cache(name, value)
|
184
|
+
associations_cache[name] = value
|
185
|
+
end
|
186
|
+
|
187
|
+
def clear_association_cache(name)
|
188
|
+
associations_cache.delete(name)
|
189
|
+
end
|
190
|
+
|
191
|
+
def refresh
|
192
|
+
associations_cache.clear
|
193
|
+
super
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Spontaneous
|
2
|
+
module DataMapper
|
3
|
+
module ContentModel
|
4
|
+
module ColumnAccessors
|
5
|
+
def self.included(model)
|
6
|
+
m = Module.new
|
7
|
+
model.columns.each do |column|
|
8
|
+
m.module_eval "def #{column}; _get_column_value(:#{column}) ; end", __FILE__, __LINE__
|
9
|
+
m.module_eval "def #{column}=(v); _set_column_value(:#{column}, v) ; end", __FILE__, __LINE__
|
10
|
+
end
|
11
|
+
model.send :include, m
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def changed_columns
|
16
|
+
@changed_columns ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def _set_column_value(column, value)
|
20
|
+
attrs = @attributes
|
21
|
+
if new? || !attrs.key?(column) || value != attrs[column]
|
22
|
+
_change_column_value(column, value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :[]=, :_set_column_value
|
27
|
+
|
28
|
+
def _change_column_value(column, value)
|
29
|
+
_mark_columns_as_modified(column)
|
30
|
+
@attributes[column] = value
|
31
|
+
end
|
32
|
+
|
33
|
+
def _mark_columns_as_modified(*columns)
|
34
|
+
cc = changed_columns
|
35
|
+
columns.each { |c| cc << c unless cc.include?(c) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def _get_column_value(column)
|
39
|
+
@attributes[column]
|
40
|
+
end
|
41
|
+
|
42
|
+
alias_method :[], :_get_column_value
|
43
|
+
|
44
|
+
def modified_attributes
|
45
|
+
cc = changed_columns
|
46
|
+
return @attributes.reject { |col, val| col == :id } if cc.empty? && @modified
|
47
|
+
@attributes.reject { |col, val| !cc.include?(col) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Spontaneous
|
2
|
+
module DataMapper
|
3
|
+
module ContentModel
|
4
|
+
module InstanceHooks
|
5
|
+
# This only currently supports the after_save hooks as that's the
|
6
|
+
# only one that's used (by PagePieces to save their owner after
|
7
|
+
# a style change)
|
8
|
+
def after_save_hook(&block)
|
9
|
+
add_instance_hook(:after_save, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def after_save
|
13
|
+
run_after_instance_hooks(:after_save)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def add_instance_hook(hook, &block)
|
20
|
+
instance_hooks(hook).push(block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def instance_hooks(hook)
|
24
|
+
@instance_hooks ||= {}
|
25
|
+
@instance_hooks[hook] ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_after_instance_hooks(hook)
|
29
|
+
instance_hooks(hook).each{|b| b.call}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Spontaneous::DataMapper
|
2
|
+
module ContentModel
|
3
|
+
module Serialization
|
4
|
+
def self.included(model)
|
5
|
+
# Don't want to pollute this shared module with the accessors
|
6
|
+
# (in case we are using this in multiple models)
|
7
|
+
m = Module.new
|
8
|
+
model.serialized_columns.each do |column|
|
9
|
+
m.module_eval (<<-RB), __FILE__, __LINE__
|
10
|
+
def #{column}
|
11
|
+
_deserialize_column(:#{column}) { super }
|
12
|
+
end
|
13
|
+
def #{column}=(value)
|
14
|
+
super(_serialize_column(:#{column}, value))
|
15
|
+
end
|
16
|
+
RB
|
17
|
+
end
|
18
|
+
model.send :include, m
|
19
|
+
end
|
20
|
+
|
21
|
+
def refresh
|
22
|
+
_deserialized_values.clear
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def _deserialized_values
|
29
|
+
@_deserialized_values ||= {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def _deserialize_column(column)
|
33
|
+
cache = _deserialized_values
|
34
|
+
unless cache.key?(column)
|
35
|
+
cache[column] = _deserialize_value(yield)
|
36
|
+
end
|
37
|
+
cache[column]
|
38
|
+
end
|
39
|
+
|
40
|
+
def _deserialize_value(value)
|
41
|
+
Yajl::Parser.new(:symbolize_keys => true).parse(value || "null")
|
42
|
+
end
|
43
|
+
|
44
|
+
def _serialize_column(column, value)
|
45
|
+
_deserialized_values[column] = value
|
46
|
+
_serialize_value(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def _serialize_value(value)
|
50
|
+
Yajl::Encoder.new.encode(value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Spontaneous
|
2
|
+
module DataMapper
|
3
|
+
|
4
|
+
def self.timestamp
|
5
|
+
::Sequel.datetime_class.now
|
6
|
+
end
|
7
|
+
|
8
|
+
module ContentModel
|
9
|
+
module Timestamps
|
10
|
+
def before_create
|
11
|
+
set_create_timestamp
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
# I have disabled update timestamps because the logic is more complex
|
16
|
+
# than this. Modification timestamps are controlled by the modification
|
17
|
+
# logic, e.g. preventing parent pages from appearing as modified on the
|
18
|
+
# addition of a new child page.
|
19
|
+
#
|
20
|
+
# def before_update
|
21
|
+
# # set_update_timestamp
|
22
|
+
# super
|
23
|
+
# end
|
24
|
+
|
25
|
+
def set_create_timestamp(time=nil)
|
26
|
+
return unless self.respond_to?(:created_at)
|
27
|
+
time ||= Spontaneous::DataMapper.timestamp
|
28
|
+
self.send(:"created_at=", time) if send(:created_at).nil?
|
29
|
+
# set_update_timestamp(time)
|
30
|
+
end
|
31
|
+
|
32
|
+
# def set_update_timestamp(time=nil)
|
33
|
+
# return unless self.respond_to?(:modified_at)
|
34
|
+
# self.send(:modified_at=, time || Spontaneous::DataMapper.timestamp)
|
35
|
+
# end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|