yodel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +9 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +63 -0
- data/LICENSE +1 -0
- data/README.rdoc +20 -0
- data/Rakefile +1 -0
- data/bin/yodel +4 -0
- data/lib/yodel.rb +22 -0
- data/lib/yodel/application/application.rb +44 -0
- data/lib/yodel/application/extension.rb +59 -0
- data/lib/yodel/application/request_handler.rb +48 -0
- data/lib/yodel/application/yodel.rb +25 -0
- data/lib/yodel/command/command.rb +94 -0
- data/lib/yodel/command/deploy.rb +67 -0
- data/lib/yodel/command/dns_server.rb +16 -0
- data/lib/yodel/command/installer.rb +229 -0
- data/lib/yodel/config/config.rb +30 -0
- data/lib/yodel/config/environment.rb +16 -0
- data/lib/yodel/config/yodel.rb +21 -0
- data/lib/yodel/exceptions/destroyed_record.rb +2 -0
- data/lib/yodel/exceptions/domain_not_found.rb +16 -0
- data/lib/yodel/exceptions/duplicate_layout.rb +2 -0
- data/lib/yodel/exceptions/exceptions.rb +3 -0
- data/lib/yodel/exceptions/inconsistent_lock_state.rb +2 -0
- data/lib/yodel/exceptions/invalid_field.rb +2 -0
- data/lib/yodel/exceptions/invalid_index.rb +2 -0
- data/lib/yodel/exceptions/invalid_mixin.rb +2 -0
- data/lib/yodel/exceptions/invalid_model_field.rb +2 -0
- data/lib/yodel/exceptions/layout_not_found.rb +2 -0
- data/lib/yodel/exceptions/mass_assignment.rb +2 -0
- data/lib/yodel/exceptions/missing_migration.rb +2 -0
- data/lib/yodel/exceptions/missing_root_directory.rb +15 -0
- data/lib/yodel/exceptions/unable_to_acquire_lock.rb +2 -0
- data/lib/yodel/exceptions/unauthorised.rb +2 -0
- data/lib/yodel/exceptions/unknown_field.rb +2 -0
- data/lib/yodel/middleware/development_server.rb +180 -0
- data/lib/yodel/middleware/error_pages.rb +72 -0
- data/lib/yodel/middleware/public_assets.rb +78 -0
- data/lib/yodel/middleware/request.rb +16 -0
- data/lib/yodel/middleware/site_detector.rb +22 -0
- data/lib/yodel/mime_types/default_mime_set.rb +28 -0
- data/lib/yodel/mime_types/mime_type.rb +68 -0
- data/lib/yodel/mime_types/mime_type_set.rb +41 -0
- data/lib/yodel/mime_types/mime_types.rb +6 -0
- data/lib/yodel/mime_types/yodel.rb +15 -0
- data/lib/yodel/models/api/api.rb +1 -0
- data/lib/yodel/models/api/api_call.rb +87 -0
- data/lib/yodel/models/core/associations/association.rb +37 -0
- data/lib/yodel/models/core/associations/associations.rb +22 -0
- data/lib/yodel/models/core/associations/counts/many_association.rb +18 -0
- data/lib/yodel/models/core/associations/counts/one_association.rb +22 -0
- data/lib/yodel/models/core/associations/embedded/embedded_association.rb +47 -0
- data/lib/yodel/models/core/associations/embedded/embedded_record_array.rb +12 -0
- data/lib/yodel/models/core/associations/embedded/many_embedded_association.rb +62 -0
- data/lib/yodel/models/core/associations/embedded/one_embedded_association.rb +49 -0
- data/lib/yodel/models/core/associations/query/many_query_association.rb +10 -0
- data/lib/yodel/models/core/associations/query/one_query_association.rb +10 -0
- data/lib/yodel/models/core/associations/query/query_association.rb +64 -0
- data/lib/yodel/models/core/associations/record_association.rb +38 -0
- data/lib/yodel/models/core/associations/store/many_store_association.rb +32 -0
- data/lib/yodel/models/core/associations/store/one_store_association.rb +14 -0
- data/lib/yodel/models/core/associations/store/store_association.rb +51 -0
- data/lib/yodel/models/core/attachments/attachment.rb +73 -0
- data/lib/yodel/models/core/attachments/image.rb +38 -0
- data/lib/yodel/models/core/core.rb +15 -0
- data/lib/yodel/models/core/fields/alias_field.rb +32 -0
- data/lib/yodel/models/core/fields/array_field.rb +64 -0
- data/lib/yodel/models/core/fields/attachment_field.rb +42 -0
- data/lib/yodel/models/core/fields/boolean_field.rb +28 -0
- data/lib/yodel/models/core/fields/change_sensitive_array.rb +96 -0
- data/lib/yodel/models/core/fields/change_sensitive_hash.rb +53 -0
- data/lib/yodel/models/core/fields/color_field.rb +4 -0
- data/lib/yodel/models/core/fields/date_field.rb +35 -0
- data/lib/yodel/models/core/fields/decimal_field.rb +19 -0
- data/lib/yodel/models/core/fields/email_field.rb +10 -0
- data/lib/yodel/models/core/fields/enum_field.rb +33 -0
- data/lib/yodel/models/core/fields/field.rb +154 -0
- data/lib/yodel/models/core/fields/fields.rb +29 -0
- data/lib/yodel/models/core/fields/fields_field.rb +31 -0
- data/lib/yodel/models/core/fields/filter_mixin.rb +9 -0
- data/lib/yodel/models/core/fields/filtered_string_field.rb +5 -0
- data/lib/yodel/models/core/fields/filtered_text_field.rb +5 -0
- data/lib/yodel/models/core/fields/function_field.rb +28 -0
- data/lib/yodel/models/core/fields/hash_field.rb +54 -0
- data/lib/yodel/models/core/fields/html_field.rb +15 -0
- data/lib/yodel/models/core/fields/image_field.rb +11 -0
- data/lib/yodel/models/core/fields/integer_field.rb +25 -0
- data/lib/yodel/models/core/fields/password_field.rb +21 -0
- data/lib/yodel/models/core/fields/self_field.rb +27 -0
- data/lib/yodel/models/core/fields/string_field.rb +15 -0
- data/lib/yodel/models/core/fields/tags_field.rb +7 -0
- data/lib/yodel/models/core/fields/text_field.rb +7 -0
- data/lib/yodel/models/core/fields/time_field.rb +36 -0
- data/lib/yodel/models/core/functions/function.rb +471 -0
- data/lib/yodel/models/core/functions/functions.rb +2 -0
- data/lib/yodel/models/core/functions/trigger.rb +14 -0
- data/lib/yodel/models/core/log/log.rb +33 -0
- data/lib/yodel/models/core/log/log_entry.rb +12 -0
- data/lib/yodel/models/core/model/abstract_model.rb +59 -0
- data/lib/yodel/models/core/model/model.rb +460 -0
- data/lib/yodel/models/core/model/mongo_model.rb +25 -0
- data/lib/yodel/models/core/model/site_model.rb +17 -0
- data/lib/yodel/models/core/mongo/mongo.rb +3 -0
- data/lib/yodel/models/core/mongo/primary_key_factory.rb +12 -0
- data/lib/yodel/models/core/mongo/query.rb +68 -0
- data/lib/yodel/models/core/mongo/record_index.rb +89 -0
- data/lib/yodel/models/core/record/abstract_record.rb +411 -0
- data/lib/yodel/models/core/record/embedded_record.rb +47 -0
- data/lib/yodel/models/core/record/mongo_record.rb +83 -0
- data/lib/yodel/models/core/record/record.rb +386 -0
- data/lib/yodel/models/core/record/section.rb +21 -0
- data/lib/yodel/models/core/record/site_record.rb +31 -0
- data/lib/yodel/models/core/site/migration.rb +52 -0
- data/lib/yodel/models/core/site/remote.rb +61 -0
- data/lib/yodel/models/core/site/site.rb +202 -0
- data/lib/yodel/models/core/validations/email_address_validation.rb +24 -0
- data/lib/yodel/models/core/validations/embedded_records_validation.rb +31 -0
- data/lib/yodel/models/core/validations/errors.rb +51 -0
- data/lib/yodel/models/core/validations/excluded_from_validation.rb +10 -0
- data/lib/yodel/models/core/validations/excludes_combinations_validation.rb +18 -0
- data/lib/yodel/models/core/validations/format_validation.rb +10 -0
- data/lib/yodel/models/core/validations/included_in_validation.rb +10 -0
- data/lib/yodel/models/core/validations/includes_combinations_validation.rb +14 -0
- data/lib/yodel/models/core/validations/length_validation.rb +28 -0
- data/lib/yodel/models/core/validations/password_confirmation_validation.rb +11 -0
- data/lib/yodel/models/core/validations/required_validation.rb +9 -0
- data/lib/yodel/models/core/validations/unique_validation.rb +9 -0
- data/lib/yodel/models/core/validations/validation.rb +39 -0
- data/lib/yodel/models/core/validations/validations.rb +15 -0
- data/lib/yodel/models/email/email.rb +79 -0
- data/lib/yodel/models/migrations/01_record_model.rb +29 -0
- data/lib/yodel/models/migrations/02_page_model.rb +45 -0
- data/lib/yodel/models/migrations/03_layout_model.rb +38 -0
- data/lib/yodel/models/migrations/04_group_model.rb +61 -0
- data/lib/yodel/models/migrations/05_user_model.rb +24 -0
- data/lib/yodel/models/migrations/06_snippet_model.rb +13 -0
- data/lib/yodel/models/migrations/07_search_page_model.rb +32 -0
- data/lib/yodel/models/migrations/08_default_site_options.rb +21 -0
- data/lib/yodel/models/migrations/09_security_page_models.rb +36 -0
- data/lib/yodel/models/migrations/10_record_proxy_page_model.rb +17 -0
- data/lib/yodel/models/migrations/11_email_model.rb +28 -0
- data/lib/yodel/models/migrations/12_api_call_model.rb +23 -0
- data/lib/yodel/models/migrations/13_redirect_page_model.rb +13 -0
- data/lib/yodel/models/migrations/14_menu_model.rb +20 -0
- data/lib/yodel/models/models.rb +8 -0
- data/lib/yodel/models/pages/form_builder.rb +379 -0
- data/lib/yodel/models/pages/html_decorator.rb +132 -0
- data/lib/yodel/models/pages/layout.rb +120 -0
- data/lib/yodel/models/pages/menu.rb +32 -0
- data/lib/yodel/models/pages/page.rb +378 -0
- data/lib/yodel/models/pages/pages.rb +7 -0
- data/lib/yodel/models/pages/record_proxy_page.rb +188 -0
- data/lib/yodel/models/pages/redirect_page.rb +11 -0
- data/lib/yodel/models/search/search.rb +1 -0
- data/lib/yodel/models/search/search_page.rb +58 -0
- data/lib/yodel/models/security/facebook_login_page.rb +55 -0
- data/lib/yodel/models/security/group.rb +10 -0
- data/lib/yodel/models/security/guests_group.rb +5 -0
- data/lib/yodel/models/security/login_page.rb +20 -0
- data/lib/yodel/models/security/logout_page.rb +13 -0
- data/lib/yodel/models/security/noone_group.rb +5 -0
- data/lib/yodel/models/security/owner_group.rb +8 -0
- data/lib/yodel/models/security/password.rb +5 -0
- data/lib/yodel/models/security/password_reset_page.rb +47 -0
- data/lib/yodel/models/security/security.rb +10 -0
- data/lib/yodel/models/security/user.rb +33 -0
- data/lib/yodel/public/core/css/core.css +257 -0
- data/lib/yodel/public/core/css/reset.css +48 -0
- data/lib/yodel/public/core/images/cross.png +0 -0
- data/lib/yodel/public/core/images/spinner.gif +0 -0
- data/lib/yodel/public/core/images/tick.png +0 -0
- data/lib/yodel/public/core/images/yodel.png +0 -0
- data/lib/yodel/public/core/js/jquery.min.js +18 -0
- data/lib/yodel/public/core/js/json2.js +480 -0
- data/lib/yodel/public/core/js/yodel_jquery.js +238 -0
- data/lib/yodel/request/authentication.rb +76 -0
- data/lib/yodel/request/flash.rb +28 -0
- data/lib/yodel/request/request.rb +4 -0
- data/lib/yodel/requires.rb +47 -0
- data/lib/yodel/task_queue/queue_daemon.rb +33 -0
- data/lib/yodel/task_queue/queue_worker.rb +32 -0
- data/lib/yodel/task_queue/stats_thread.rb +27 -0
- data/lib/yodel/task_queue/task.rb +62 -0
- data/lib/yodel/task_queue/task_queue.rb +40 -0
- data/lib/yodel/types/date.rb +5 -0
- data/lib/yodel/types/object_id.rb +11 -0
- data/lib/yodel/types/time.rb +5 -0
- data/lib/yodel/version.rb +3 -0
- data/system/Library/LaunchDaemons/com.yodelcms.dns.plist +26 -0
- data/system/Library/LaunchDaemons/com.yodelcms.server.plist +26 -0
- data/system/etc/resolver/yodel +2 -0
- data/system/usr/local/bin/yodel_command_runner +2 -0
- data/system/usr/local/etc/yodel/development_settings.rb +28 -0
- data/system/usr/local/etc/yodel/production_settings.rb +27 -0
- data/system/var/log/yodel.log +0 -0
- data/test/helper.rb +18 -0
- data/test/test_yodel.rb +4 -0
- data/yodel.gemspec +47 -0
- metadata +501 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
class Association < Field
|
2
|
+
def json_action(action, value, record)
|
3
|
+
store = record.get_raw(name)
|
4
|
+
|
5
|
+
case action
|
6
|
+
when 'set'
|
7
|
+
clear(store, record)
|
8
|
+
process_json_items(value, record, store, :associate)
|
9
|
+
when 'add'
|
10
|
+
process_json_items(value, record, store, :associate)
|
11
|
+
when 'remove'
|
12
|
+
process_json_items(value, record, store, :unassociate)
|
13
|
+
when 'clear'
|
14
|
+
clear(store, record)
|
15
|
+
end
|
16
|
+
|
17
|
+
record.changed!(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def from_json(value, record)
|
21
|
+
store = record.get_raw(name)
|
22
|
+
clear(store, record)
|
23
|
+
process_json_items(value, record, store, :associate)
|
24
|
+
record.get(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
private
|
29
|
+
def process_json_items(items, record, store, method)
|
30
|
+
items = [items] unless items.is_a?(Array)
|
31
|
+
items.each do |raw_item|
|
32
|
+
item = process_json_item(raw_item, store, record)
|
33
|
+
next if item.nil?
|
34
|
+
send(method, item, store, record)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require './model/abstract_model'
|
2
|
+
|
3
|
+
Dir.chdir(File.dirname(__FILE__)) do
|
4
|
+
require './association'
|
5
|
+
require './record_association'
|
6
|
+
|
7
|
+
require './counts/many_association'
|
8
|
+
require './counts/one_association'
|
9
|
+
|
10
|
+
require './query/query_association'
|
11
|
+
require './query/many_query_association'
|
12
|
+
require './query/one_query_association'
|
13
|
+
|
14
|
+
require './store/store_association'
|
15
|
+
require './store/many_store_association'
|
16
|
+
require './store/one_store_association'
|
17
|
+
|
18
|
+
require './embedded/embedded_record_array'
|
19
|
+
require './embedded/embedded_association'
|
20
|
+
require './embedded/many_embedded_association'
|
21
|
+
require './embedded/one_embedded_association'
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ManyAssociation
|
2
|
+
def search_terms_set(record)
|
3
|
+
return [] unless include_in_search_keywords?
|
4
|
+
record.get(name).collect do |embedded_record|
|
5
|
+
embedded_record.search_terms
|
6
|
+
end.flatten
|
7
|
+
end
|
8
|
+
|
9
|
+
def before_destroy(record)
|
10
|
+
if @options['destroy'] == true
|
11
|
+
record.get(name).each(&:destroy)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def typecast(value, record)
|
16
|
+
ChangeSensitiveArray.new(record, name, all(value, record))
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module OneAssociation
|
2
|
+
def search_terms_set(record)
|
3
|
+
return [] unless include_in_search_keywords?
|
4
|
+
record.get(name).try(:search_terms) || []
|
5
|
+
end
|
6
|
+
|
7
|
+
def before_destroy(record)
|
8
|
+
if @options['destroy'] == true
|
9
|
+
record.get(name).try(:destroy)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def typecast(value, record)
|
14
|
+
return default if value.blank?
|
15
|
+
associated(value, record)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def clear(store, record)
|
20
|
+
unassociate(associated(store, record), store, record)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module EmbeddedAssociation
|
2
|
+
include AbstractModel
|
3
|
+
|
4
|
+
def fields_field
|
5
|
+
@fields_field ||= FieldsField.new(name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def fields
|
9
|
+
@fields ||= fields_field.typecast(@options['fields'], nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
def options
|
13
|
+
@options.merge({'fields' => fields_field.untypecast(fields, nil)})
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate(record, errors)
|
17
|
+
EmbeddedRecordsValidation.validate(self, record.get(name), record, errors)
|
18
|
+
super(record, errors)
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_input_type
|
22
|
+
:embedded
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def process_json_item(embedded_record, store, record)
|
27
|
+
return unless embedded_record.respond_to?(:to_hash)
|
28
|
+
EmbeddedRecord.new(self, record, {}).tap {|new_record| new_record.from_json(embedded_record)}
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear(store, record)
|
32
|
+
if store.is_a?(Array)
|
33
|
+
store.clear
|
34
|
+
else
|
35
|
+
record.set_raw(name, {})
|
36
|
+
return {}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def all(store, record)
|
41
|
+
return [] if store.blank?
|
42
|
+
store = [store] unless store.respond_to?(:collect)
|
43
|
+
store.collect do |values|
|
44
|
+
EmbeddedRecord.new(self, record, values, false)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class EmbeddedRecordArray < ChangeSensitiveArray
|
2
|
+
def new(values={})
|
3
|
+
EmbeddedRecord.new(@field, @record).tap {|record| record.update(values, false)}
|
4
|
+
end
|
5
|
+
|
6
|
+
private
|
7
|
+
def notify!
|
8
|
+
return if @notified
|
9
|
+
@record.try(:changed!, @field.name)
|
10
|
+
@notified = true
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class ManyEmbeddedAssociation < Association
|
2
|
+
include EmbeddedAssociation
|
3
|
+
include ManyAssociation
|
4
|
+
|
5
|
+
# remove ManyAssociation's destroy behaviour since embedded
|
6
|
+
# records are destroyed as part of the parent anyway
|
7
|
+
undef :before_destroy
|
8
|
+
|
9
|
+
def search_terms_set(record)
|
10
|
+
record.get(name).collect do |embedded_record|
|
11
|
+
embedded_record.search_terms
|
12
|
+
end.flatten
|
13
|
+
end
|
14
|
+
|
15
|
+
def options
|
16
|
+
super.merge({'type' => 'many_embedded'})
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(embedded_record, parent_record)
|
20
|
+
if embedded_record.new?
|
21
|
+
parent_record.get(name) << embedded_record
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def destroy(embedded_record, parent_record)
|
26
|
+
parent_record.get(name).delete(embedded_record)
|
27
|
+
end
|
28
|
+
|
29
|
+
def associate(embedded_record, store, record)
|
30
|
+
raise "Associated record must be an Embedded Record" unless embedded_record.is_a?(EmbeddedRecord)
|
31
|
+
embedded_record.save
|
32
|
+
store << embedded_record.values
|
33
|
+
end
|
34
|
+
|
35
|
+
def unassociate(embedded_record, store, record)
|
36
|
+
store.delete(embedded_record)
|
37
|
+
end
|
38
|
+
|
39
|
+
def untypecast(value, record)
|
40
|
+
return nil if value.blank?
|
41
|
+
raise "ManyEmbeddedAssociation values must be enumerable (#{name})" unless value.respond_to?(:each)
|
42
|
+
|
43
|
+
store = record.get_raw(name)
|
44
|
+
clear(store, record)
|
45
|
+
value.each do |associated_record|
|
46
|
+
associate(associated_record, store, record)
|
47
|
+
end
|
48
|
+
store
|
49
|
+
end
|
50
|
+
|
51
|
+
def default
|
52
|
+
@options['default'] || []
|
53
|
+
end
|
54
|
+
|
55
|
+
def typecast(value, record)
|
56
|
+
return EmbeddedRecordArray.new(record, self, []) if value.blank?
|
57
|
+
raise "ManyEmbedded values must be enumerable (#{name})" unless value.respond_to?(:each)
|
58
|
+
EmbeddedRecordArray.new(record, self, all(value, record))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Field::TYPES['many_embedded'] = ManyEmbeddedAssociation
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class OneEmbeddedAssociation < Association
|
2
|
+
include EmbeddedAssociation
|
3
|
+
include OneAssociation
|
4
|
+
|
5
|
+
# remove OneAssociation's destroy behaviour since embedded
|
6
|
+
# records are destroyed as part of the parent anyway
|
7
|
+
undef :before_destroy
|
8
|
+
|
9
|
+
def search_terms_set(record)
|
10
|
+
record.get(name).search_terms
|
11
|
+
end
|
12
|
+
|
13
|
+
def default
|
14
|
+
@options['default'] || EmbeddedRecord.new(self, nil).default_values
|
15
|
+
end
|
16
|
+
|
17
|
+
def options
|
18
|
+
super.merge({'type' => 'one_embedded'})
|
19
|
+
end
|
20
|
+
|
21
|
+
def save(embedded_record, parent_record)
|
22
|
+
# noop
|
23
|
+
end
|
24
|
+
|
25
|
+
def destroy(embedded_record, parent_record)
|
26
|
+
embedded_record.initialize(self, parent_record)
|
27
|
+
parent_record.changed!(name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def untypecast(value, record)
|
31
|
+
return {} unless value.is_a?(EmbeddedRecord)
|
32
|
+
value.save
|
33
|
+
value.values
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def associated(store, record)
|
38
|
+
store = {} unless store.is_a?(Hash)
|
39
|
+
EmbeddedRecord.new(self, record, store, false)
|
40
|
+
end
|
41
|
+
|
42
|
+
def associate(embedded_record, store, record)
|
43
|
+
raise "Associated record must be an Embedded Record" unless embedded_record.is_a?(EmbeddedRecord)
|
44
|
+
embedded_record.save
|
45
|
+
record.set(name, embedded_record)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Field::TYPES['one_embedded'] = OneEmbeddedAssociation
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module QueryAssociation
|
2
|
+
include RecordAssociation
|
3
|
+
|
4
|
+
def initialize(name, options={})
|
5
|
+
super
|
6
|
+
if @options['where']
|
7
|
+
@options['where'] = Plucky::CriteriaHash.new(@options['where']).to_hash
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate(record, errors)
|
12
|
+
# noop
|
13
|
+
end
|
14
|
+
|
15
|
+
def default
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def strip_nil?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def associate(associated_record, store, record)
|
24
|
+
associated_record.set_meta(foreign_key(record), record.id)
|
25
|
+
associated_record.save_without_validation
|
26
|
+
end
|
27
|
+
|
28
|
+
def unassociate(associated_record, store, record)
|
29
|
+
return unless associated_record.get_meta(foreign_key) == record.id
|
30
|
+
associated_record.set_meta(foreign_key(record), nil)
|
31
|
+
associated_record.save_without_validation
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
def clear(store, record)
|
36
|
+
all(store, record).each {|associated_record| unassociate(associated_record, store, record)}
|
37
|
+
end
|
38
|
+
|
39
|
+
def all(store, record)
|
40
|
+
scope(record).all
|
41
|
+
end
|
42
|
+
|
43
|
+
def scope(record)
|
44
|
+
if @options['through']
|
45
|
+
through_query = record.field(@options['through'].to_s).scope(record).only('_id')
|
46
|
+
cursor = Record.collection.find(through_query.criteria.to_hash, through_query.options.to_hash)
|
47
|
+
ids = cursor.to_a.collect {|doc| doc['_id']}
|
48
|
+
scope = record.site.model(model_name).where(foreign_key(record) => ids)
|
49
|
+
elsif @options['extends']
|
50
|
+
scope = record.field(@options['extends'].to_s).scope(record)
|
51
|
+
else
|
52
|
+
scope = record.site.model(model_name).where(foreign_key(record) => record.id)
|
53
|
+
end
|
54
|
+
scope = scope.where(@options['where'].to_hash) if @options['where']
|
55
|
+
scope = scope.sort(@options['order'].to_s) if @options['order']
|
56
|
+
scope = scope.limit(@options['limit'].to_i) if @options['limit']
|
57
|
+
scope = scope.skip(@options['skip'].to_i) if @options['skip']
|
58
|
+
scope
|
59
|
+
end
|
60
|
+
|
61
|
+
def associated(store, record)
|
62
|
+
record.site.model(model_name).first(foreign_key(record) => record.id)
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RecordAssociation
|
2
|
+
private
|
3
|
+
def process_json_item(raw_id, store, record)
|
4
|
+
return nil if raw_id.blank?
|
5
|
+
associated_record = model(record).find(BSON::ObjectId.from_string(raw_id))
|
6
|
+
if !associated_record.nil?
|
7
|
+
if model_name == 'Model'
|
8
|
+
return associated_record if associated_record.is_a?(Model)
|
9
|
+
elsif associated_record.model.ancestors.include?(model(record))
|
10
|
+
return associated_record
|
11
|
+
end
|
12
|
+
end
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def foreign_key(record)
|
17
|
+
@foreign_key ||= (options['foreign_key'] || model_name.to_s.underscore) # FIXME: should be foreign_key || record.model.name.underscore
|
18
|
+
end
|
19
|
+
|
20
|
+
def model_name
|
21
|
+
@model_name ||= (options['model'] || name).to_s.classify
|
22
|
+
end
|
23
|
+
|
24
|
+
def model(record)
|
25
|
+
# caching was causing a bug where running migrations on a new site would fail because
|
26
|
+
# calls to NewModel.parent were generating the query:
|
27
|
+
# {id: parent_id, _site_id: yodel_dev_site_id NOT new_site.id}
|
28
|
+
#@model ||=
|
29
|
+
case model_name
|
30
|
+
when 'Site'
|
31
|
+
Site
|
32
|
+
when 'Remote'
|
33
|
+
Remote
|
34
|
+
else
|
35
|
+
record.site.model(model_name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class ManyStoreAssociation < Association
|
2
|
+
include StoreAssociation
|
3
|
+
include ManyAssociation
|
4
|
+
|
5
|
+
def default_input_type
|
6
|
+
:store_many
|
7
|
+
end
|
8
|
+
|
9
|
+
def typecast(value, record)
|
10
|
+
return ChangeSensitiveArray.new(record, name, []) if value.blank?
|
11
|
+
raise "ManyStoreAssociation values must be enumerable (#{name})" unless value.respond_to?(:each)
|
12
|
+
ChangeSensitiveArray.new(record, name, all(value, record))
|
13
|
+
end
|
14
|
+
|
15
|
+
def untypecast(value, record)
|
16
|
+
return nil if value.blank?
|
17
|
+
raise "ManyStoreAssociation values must be enumerable (#{name})" unless value.respond_to?(:each)
|
18
|
+
|
19
|
+
store = record.get_raw(name) || []
|
20
|
+
store.clear
|
21
|
+
value.each do |associated_record|
|
22
|
+
store << associated_record.id
|
23
|
+
end
|
24
|
+
store
|
25
|
+
end
|
26
|
+
|
27
|
+
def default
|
28
|
+
@options['default'] || []
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Field::TYPES['many_store'] = ManyStoreAssociation
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class OneStoreAssociation < Association
|
2
|
+
include StoreAssociation
|
3
|
+
include OneAssociation
|
4
|
+
|
5
|
+
def default_input_type
|
6
|
+
:store_one
|
7
|
+
end
|
8
|
+
|
9
|
+
def untypecast(value, record)
|
10
|
+
value.respond_to?(:id) ? value.id : nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Field::TYPES['one_store'] = OneStoreAssociation
|