card 1.16.15 → 1.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/config/initializers/recaptcha.rb +21 -4
- data/db/migrate_core_cards/20130823192433_add_style_cards.rb +1 -1
- data/db/migrate_core_cards/20140512155840_add_script_cards.rb +1 -1
- data/db/migrate_core_cards/20140629222005_add_email_cards.rb +2 -2
- data/db/migrate_core_cards/20150202143810_import_bootstrap_layout.rb +1 -1
- data/db/migrate_core_cards/20150528084659_add_session_cardtype.rb +1 -1
- data/db/migrate_core_cards/20150610180019_add_recaptcha_key_and_admin_info_cards.rb +63 -0
- data/db/migrate_core_cards/20150724123438_update_file_and_image_cards.rb +1 -1
- data/db/migrate_core_cards/20150903130006_attachment_upload_cards.rb +1 -1
- data/db/schema.rb +1 -1
- data/db/seed/new/card_actions.yml +602 -394
- data/db/seed/new/card_acts.yml +595 -1
- data/db/seed/new/card_changes.yml +26282 -10262
- data/db/seed/new/card_references.yml +1252 -1084
- data/db/seed/new/cards.yml +1899 -1423
- data/db/seed/test/fixtures/card_actions.yml +1653 -1413
- data/db/seed/test/fixtures/card_acts.yml +1063 -445
- data/db/seed/test/fixtures/card_changes.yml +29674 -13637
- data/db/seed/test/fixtures/card_references.yml +1976 -1815
- data/db/seed/test/fixtures/cards.yml +3194 -2719
- data/lib/card.rb +14 -13
- data/lib/card/auth.rb +10 -6
- data/lib/card/cache.rb +58 -120
- data/lib/card/cache/persistent.rb +50 -0
- data/lib/card/cache/temporary.rb +38 -0
- data/lib/card/chunk.rb +34 -25
- data/lib/card/content.rb +3 -3
- data/lib/card/env.rb +3 -0
- data/lib/card/format.rb +56 -53
- data/lib/card/migration.rb +6 -2
- data/lib/card/name.rb +9 -1
- data/lib/card/query.rb +1 -1
- data/lib/card/reference.rb +17 -11
- data/lib/card/set.rb +1 -1
- data/lib/card/subcards.rb +6 -6
- data/lib/card/view_cache.rb +45 -28
- data/lib/generators/card/migration/templates/card_migration.erb +1 -2
- data/mod/01_core/chunk/include.rb +71 -48
- data/mod/01_core/chunk/link.rb +6 -3
- data/mod/01_core/chunk/query_reference.rb +38 -29
- data/mod/01_core/chunk/reference.rb +23 -24
- data/mod/01_core/set/all/collection.rb +1 -1
- data/mod/01_core/set/all/fetch.rb +39 -12
- data/mod/01_core/set/all/permissions.rb +2 -4
- data/mod/01_core/set/all/references.rb +50 -75
- data/mod/01_core/set/all/rules.rb +19 -18
- data/mod/01_core/set/all/subcards.rb +1 -1
- data/mod/01_core/set/all/templating.rb +31 -88
- data/mod/01_core/set/all/tracked_attributes.rb +7 -14
- data/mod/01_core/set/all/utils.rb +77 -66
- data/mod/01_core/set_pattern/07_type_plus_right.rb +6 -3
- data/mod/01_core/spec/set/all/fetch_spec.rb +148 -96
- data/mod/01_core/spec/set/all/templating_spec.rb +49 -40
- data/mod/01_core/spec/set/all/trash_spec.rb +1 -1
- data/mod/01_history/set/all/actions.rb +1 -1
- data/mod/02_basic_types/set/all/base.rb +13 -7
- data/mod/02_basic_types/set/all/rss.rb +17 -22
- data/mod/02_basic_types/set/type/plain_text.rb +5 -2
- data/mod/02_basic_types/spec/set/all/base_spec.rb +1 -0
- data/mod/02_basic_types/spec/set/all/rss_spec.rb +7 -6
- data/mod/03_machines/lib/javascript/wagn.js.coffee +22 -9
- data/mod/03_machines/set/right/machine_output.rb +1 -1
- data/mod/04_settings/lib/card/setting.rb +45 -31
- data/mod/04_settings/set/right/structure.rb +47 -1
- data/mod/04_settings/set/self/default_html_view.rb +2 -0
- data/mod/04_settings/set/self/follow_fields.rb +2 -0
- data/mod/04_settings/set/self/recent_settings.rb +1 -1
- data/mod/05_standard/file/favicon/image-icon.png +0 -0
- data/mod/05_standard/file/favicon/image-large.png +0 -0
- data/mod/05_standard/file/favicon/image-medium.png +0 -0
- data/mod/05_standard/file/favicon/image-original.png +0 -0
- data/mod/05_standard/file/favicon/image-small.png +0 -0
- data/mod/05_standard/set/all/links.rb +27 -26
- data/mod/05_standard/set/all/rich_html/editing.rb +1 -1
- data/mod/05_standard/set/all/rich_html/toolbar.rb +1 -1
- data/mod/05_standard/set/rstar/rules.rb +20 -325
- data/mod/05_standard/set/rstar/rules_editor.rb +362 -0
- data/mod/05_standard/set/self/admin_info.rb +82 -0
- data/mod/05_standard/set/self/all.rb +16 -10
- data/mod/05_standard/set/self/head.rb +20 -19
- data/mod/05_standard/set/type/signup.rb +0 -1
- data/mod/05_standard/spec/set/all/account_spec.rb +44 -43
- data/mod/05_standard/spec/set/right/account_spec.rb +4 -2
- data/mod/05_standard/spec/set/type/search_type_spec.rb +8 -0
- data/mod/05_standard/spec/set/type/signup_spec.rb +24 -17
- data/mod/06_bootstrap/set/all/bootstrap/helper.rb +1 -1
- data/spec/lib/card/cache_spec.rb +64 -70
- data/spec/lib/card/content_spec.rb +236 -150
- data/spec/lib/card/reference_spec.rb +22 -38
- data/spec/lib/card/subcards_spec.rb +38 -0
- data/spec/lib/card/view_cache_spec.rb +8 -0
- data/spec/spec_helper.rb +1 -1
- data/tmpsets/set/mod001-01_core/all/collection.rb +77 -74
- data/tmpsets/set/mod001-01_core/all/content.rb +14 -16
- data/tmpsets/set/mod001-01_core/all/fetch.rb +137 -110
- data/tmpsets/set/mod001-01_core/all/name.rb +58 -40
- data/tmpsets/set/mod001-01_core/all/pattern.rb +12 -11
- data/tmpsets/set/mod001-01_core/all/permissions.rb +125 -117
- data/tmpsets/set/mod001-01_core/all/phases.rb +2 -1
- data/tmpsets/set/mod001-01_core/all/references.rb +52 -77
- data/tmpsets/set/mod001-01_core/all/rules.rb +47 -53
- data/tmpsets/set/mod001-01_core/all/templating.rb +31 -87
- data/tmpsets/set/mod001-01_core/all/tracked_attributes.rb +12 -21
- data/tmpsets/set/mod001-01_core/all/trash.rb +4 -1
- data/tmpsets/set/mod001-01_core/all/type.rb +23 -21
- data/tmpsets/set/mod001-01_core/all/utils.rb +80 -64
- data/tmpsets/set/mod002-01_history/all/actions.rb +20 -16
- data/tmpsets/set/mod002-01_history/all/history.rb +18 -13
- data/tmpsets/set/mod003-02_basic_types/all/base.rb +37 -10
- data/tmpsets/set/mod003-02_basic_types/all/rss.rb +17 -22
- data/tmpsets/set/mod003-02_basic_types/type/plain_text.rb +5 -2
- data/tmpsets/set/mod003-02_basic_types/type/pointer.rb +51 -39
- data/tmpsets/set/mod004-03_machines/right/machine_output.rb +10 -6
- data/tmpsets/set/mod005-04_settings/abstract/permission.rb +10 -5
- data/tmpsets/set/mod005-04_settings/right/structure.rb +47 -1
- data/tmpsets/set/mod005-04_settings/self/recent_settings.rb +1 -0
- data/tmpsets/set/mod005-04_settings/type/setting.rb +4 -1
- data/tmpsets/set/mod006-05_email/all/follow.rb +45 -54
- data/tmpsets/set/mod006-05_email/all/notify.rb +88 -73
- data/tmpsets/set/mod006-05_email/right/followers.rb +17 -14
- data/tmpsets/set/mod006-05_email/self/follow_defaults.rb +22 -18
- data/tmpsets/set/mod006-05_email/type/email_template.rb +1 -1
- data/tmpsets/set/mod007-05_standard/abstract/attachment.rb +94 -67
- data/tmpsets/set/mod007-05_standard/all/account.rb +18 -20
- data/tmpsets/set/mod007-05_standard/all/comment.rb +51 -29
- data/tmpsets/set/mod007-05_standard/all/error.rb +129 -99
- data/tmpsets/set/mod007-05_standard/all/links.rb +27 -26
- data/tmpsets/set/mod007-05_standard/all/rich_html/content.rb +115 -103
- data/tmpsets/set/mod007-05_standard/all/rich_html/editing.rb +112 -78
- data/tmpsets/set/mod007-05_standard/all/rich_html/form.rb +123 -81
- data/tmpsets/set/mod007-05_standard/all/rich_html/modal.rb +15 -58
- data/tmpsets/set/mod007-05_standard/all/rich_html/toolbar.rb +2 -2
- data/tmpsets/set/mod007-05_standard/right/account.rb +71 -75
- data/tmpsets/set/mod007-05_standard/right/email.rb +16 -13
- data/tmpsets/set/mod007-05_standard/right/password.rb +20 -12
- data/tmpsets/set/mod007-05_standard/right/status.rb +2 -2
- data/tmpsets/set/mod007-05_standard/right/token.rb +49 -2
- data/tmpsets/set/mod007-05_standard/rstar/rules.rb +20 -325
- data/tmpsets/set/mod007-05_standard/self/all.rb +16 -10
- data/tmpsets/set/mod007-05_standard/self/head.rb +76 -62
- data/tmpsets/set/mod007-05_standard/self/search.rb +45 -22
- data/tmpsets/set/mod007-05_standard/self/signin.rb +14 -12
- data/tmpsets/set/mod007-05_standard/type/cardtype.rb +13 -11
- data/tmpsets/set/mod007-05_standard/type/file.rb +1 -1
- data/tmpsets/set/mod007-05_standard/type/search_type.rb +3 -2
- data/tmpsets/set/mod007-05_standard/type/set.rb +20 -16
- data/tmpsets/set/mod007-05_standard/type/signup.rb +19 -25
- data/tmpsets/set/mod007-05_standard/type/user.rb +1 -1
- data/tmpsets/set/mod008-06_bootstrap/all/bootstrap/helper.rb +1 -1
- data/tmpsets/set_pattern/106-type_plus_right.rb +6 -3
- metadata +11 -2
@@ -1,6 +1,5 @@
|
|
1
1
|
|
2
2
|
module ClassMethods
|
3
|
-
|
4
3
|
def empty_trash
|
5
4
|
Card.delete_trashed_files
|
6
5
|
Card.where(trash: true).delete_all
|
@@ -10,25 +9,39 @@ module ClassMethods
|
|
10
9
|
Card.delete_tmp_files_of_cached_uploads
|
11
10
|
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
file_ids = Dir.entries( dir )[2..-1].map( &:to_i )
|
12
|
+
# deletes any file not associated with a real card.
|
13
|
+
def delete_trashed_files
|
14
|
+
trashed_card_ids = all_trashed_card_ids
|
15
|
+
file_ids = all_file_ids
|
18
16
|
file_ids.each do |file_id|
|
19
17
|
if trashed_card_ids.member?(file_id)
|
20
|
-
|
18
|
+
if Card.exists?(file_id) # double check!
|
19
|
+
fail Card::Error, 'Narrowly averted deleting current file'
|
20
|
+
end
|
21
21
|
FileUtils.rm_rf "#{dir}/#{file_id}", secure: true
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
def all_file_ids
|
27
|
+
dir = Card.paths['files'].existent.first
|
28
|
+
Dir.entries(dir)[2..-1].map(&:to_i)
|
29
|
+
end
|
30
|
+
|
31
|
+
def all_trashed_card_ids
|
32
|
+
trashed_card_sql = %{ select id from cards where trash is true }
|
33
|
+
sql_results = Card.connection.select_all(trashed_card_sql)
|
34
|
+
sql_results.map(&:values).flatten.map(&:to_i)
|
35
|
+
end
|
36
|
+
|
26
37
|
def delete_tmp_files_of_cached_uploads
|
27
38
|
actions = Card::Action.find_by_sql "SELECT * FROM card_actions
|
28
39
|
INNER JOIN cards ON card_actions.card_id = cards.id
|
29
|
-
WHERE cards.type_id IN (#{Card::FileID}, #{Card::ImageID})
|
40
|
+
WHERE cards.type_id IN (#{Card::FileID}, #{Card::ImageID})
|
41
|
+
AND card_actions.draft = true"
|
30
42
|
actions.each do |action|
|
31
|
-
if older_than_five_days?(action.created_at) && card = action.card
|
43
|
+
if older_than_five_days?(action.created_at) && (card = action.card)
|
44
|
+
# we don't want to delete uploads in progress
|
32
45
|
card.delete_files_for_action action
|
33
46
|
end
|
34
47
|
end
|
@@ -39,31 +52,31 @@ module ClassMethods
|
|
39
52
|
attribs.each do |row|
|
40
53
|
result = begin
|
41
54
|
merge row['name'], row, opts
|
42
|
-
# rescue => e
|
43
|
-
# Rails.logger.info "merge_list problem: #{ e.message }"
|
44
|
-
# false
|
45
55
|
end
|
46
56
|
unmerged.push row unless result == true
|
47
57
|
end
|
48
58
|
|
49
59
|
if unmerged.empty?
|
50
|
-
Rails.logger.info
|
60
|
+
Rails.logger.info 'successfully merged all!'
|
51
61
|
else
|
52
62
|
unmerged_json = JSON.pretty_generate unmerged
|
53
|
-
|
54
|
-
::File.open output_file, 'w' do |f|
|
55
|
-
f.write unmerged_json
|
56
|
-
end
|
57
|
-
else
|
58
|
-
Rails.logger.info "failed to merge:\n\n#{ unmerged_json }"
|
59
|
-
end
|
63
|
+
report_unmerged_json unmerged_json, opts[:output_file]
|
60
64
|
end
|
61
65
|
unmerged
|
62
66
|
end
|
63
67
|
|
68
|
+
def report_unmerged_json unmerged_json, output_file
|
69
|
+
if output_file
|
70
|
+
::File.open output_file, 'w' do |f|
|
71
|
+
f.write unmerged_json
|
72
|
+
end
|
73
|
+
else
|
74
|
+
Rails.logger.info "failed to merge:\n\n#{unmerged_json}"
|
75
|
+
end
|
76
|
+
end
|
64
77
|
|
65
78
|
def merge name, attribs={}, opts={}
|
66
|
-
puts "merging #{
|
79
|
+
puts "merging #{name}"
|
67
80
|
card = fetch name, new: {}
|
68
81
|
|
69
82
|
if opts[:pristine] && !card.pristine?
|
@@ -74,64 +87,60 @@ module ClassMethods
|
|
74
87
|
end
|
75
88
|
end
|
76
89
|
|
77
|
-
|
78
90
|
def older_than_five_days? time
|
79
91
|
Time.now - time > 432000
|
80
92
|
end
|
81
|
-
|
82
93
|
end
|
83
94
|
|
84
95
|
def debug_type
|
85
|
-
"#{type_code||'no code'}:#{type_id}"
|
96
|
+
"#{type_code || 'no code'}:#{type_id}"
|
86
97
|
end
|
87
98
|
|
88
99
|
def to_s
|
89
|
-
"#<#{self.class.name}[#{debug_type}]#{
|
100
|
+
"#<#{self.class.name}[#{debug_type}]#{attributes['name']}>"
|
90
101
|
end
|
91
102
|
|
92
103
|
def inspect
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
'
|
104
|
+
tags = []
|
105
|
+
tags << 'trash' if trash
|
106
|
+
tags << 'new' if new_card?
|
107
|
+
tags << 'frozen' if frozen?
|
108
|
+
tags << 'readonly' if readonly?
|
109
|
+
tags << 'virtual' if @virtual
|
110
|
+
tags << 'set_mods_loaded' if @set_mods_loaded
|
111
|
+
|
112
|
+
error_messages = errors.any? ? "<E*#{errors.full_messages * ', '}*>" : ''
|
113
|
+
|
114
|
+
"#<Card##{id}[#{debug_type}](#{name})#{error_messages}{#{tags * ','}}"
|
102
115
|
end
|
103
116
|
|
104
117
|
format :html do
|
105
|
-
view :views_by_format do
|
106
|
-
format_views =
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
118
|
+
view :views_by_format do
|
119
|
+
format_views =
|
120
|
+
self.class.ancestors.each_with_object({}) do |format_class, hash|
|
121
|
+
views =
|
122
|
+
format_class.instance_methods.map do |method|
|
123
|
+
if method.to_s.match(/^_view_(.+)$/)
|
124
|
+
"<li>#{$1}</li>"
|
125
|
+
end
|
126
|
+
end.compact.join "\n"
|
127
|
+
if views.present?
|
128
|
+
format_class.name.match(/^Card(::Set)?::(.+?)$/) #::(\w+Format)
|
129
|
+
hash[$2] = views
|
130
|
+
end
|
116
131
|
end
|
117
|
-
end
|
118
132
|
accordion_group format_views
|
119
133
|
end
|
120
134
|
|
121
|
-
view :views_by_name do
|
135
|
+
view :views_by_name do
|
122
136
|
views = methods.map do |method|
|
123
|
-
if method.to_s.match
|
137
|
+
if method.to_s.match(/^_view_(.+)$/)
|
124
138
|
$1
|
125
139
|
end
|
126
140
|
end.compact.sort
|
127
|
-
"<ul>
|
128
|
-
#{ wrap_each_with :li, views }
|
129
|
-
</ul>"
|
141
|
+
"<ul>#{wrap_each_with :li, views}</ul>"
|
130
142
|
end
|
131
143
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
144
|
def accordion_group list, collapse_id=card.cardname.safe_key
|
136
145
|
accordions = ''
|
137
146
|
index = 1
|
@@ -139,35 +148,37 @@ format :html do
|
|
139
148
|
accordions << accordion(title, content, "#{collapse_id}-#{index}")
|
140
149
|
index += 1
|
141
150
|
end
|
142
|
-
content_tag :div, accordions.html_safe, class:
|
151
|
+
content_tag :div, accordions.html_safe, class: 'panel-group',
|
152
|
+
id: "accordion-#{collapse_id}",
|
153
|
+
role: 'tablist',
|
154
|
+
'aria-multiselectable' => 'true'
|
143
155
|
end
|
144
156
|
|
145
157
|
def accordion title, content, collapse_id=card.cardname.safe_key
|
146
158
|
panel_body =
|
147
159
|
case content
|
148
|
-
when Hash
|
149
|
-
|
150
|
-
|
151
|
-
content.join "\n"
|
152
|
-
else
|
153
|
-
content
|
160
|
+
when Hash then accordion_group accordion(content, collapse_id)
|
161
|
+
when Array then content.join "\n"
|
162
|
+
else content
|
154
163
|
end
|
155
164
|
%{
|
156
165
|
<div class="panel panel-default">
|
157
166
|
<div class="panel-heading" role="tab" id="heading-#{collapse_id}">
|
158
167
|
<h4 class="panel-title">
|
159
|
-
<a data-toggle="collapse" data-parent="#accordion-#{collapse_id}"
|
160
|
-
|
168
|
+
<a data-toggle="collapse" data-parent="#accordion-#{collapse_id}" \
|
169
|
+
href="##{collapse_id}" aria-expanded="true" \
|
170
|
+
aria-controls="#{collapse_id}">
|
171
|
+
#{title}
|
161
172
|
</a>
|
162
173
|
</h4>
|
163
174
|
</div>
|
164
|
-
<div id="#{collapse_id}" class="panel-collapse collapse"
|
175
|
+
<div id="#{collapse_id}" class="panel-collapse collapse" \
|
176
|
+
role="tabpanel" aria-labelledby="heading-#{collapse_id}">
|
165
177
|
<div class="panel-body">
|
166
|
-
#{
|
178
|
+
#{panel_body}
|
167
179
|
</div>
|
168
180
|
</div>
|
169
181
|
</div>
|
170
182
|
}.html_safe
|
171
183
|
end
|
172
184
|
end
|
173
|
-
|
@@ -9,15 +9,18 @@ def label name
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def prototype_args anchor
|
12
|
-
{
|
12
|
+
{
|
13
|
+
name: "+#{anchor.tag}",
|
14
|
+
supercard: Card.new(name: '*dummy', type: anchor.trunk_name)
|
15
|
+
}
|
13
16
|
end
|
14
17
|
|
15
18
|
def anchor_name card
|
16
19
|
left = card.left
|
17
|
-
type_name = (left && left.type_name) || Card[
|
20
|
+
type_name = (left && left.type_name) || Card[Card.default_type_id].name
|
18
21
|
"#{type_name}+#{card.cardname.tag}"
|
19
22
|
end
|
20
23
|
|
21
24
|
def follow_label name
|
22
|
-
%{all
|
25
|
+
%{all "+#{name.to_name.tag}" on "#{name.to_name.left_name}s"}
|
23
26
|
end
|
@@ -10,196 +10,248 @@ describe Card::Set::All::Fetch do
|
|
10
10
|
expect(Card.fetch('A')).to be_instance_of(Card)
|
11
11
|
end
|
12
12
|
|
13
|
-
it
|
14
|
-
expect(Card.fetch(
|
15
|
-
expect(Card.cache.read(
|
16
|
-
expect(Card.fetch(
|
13
|
+
it 'returns nil and caches missing cards' do
|
14
|
+
expect(Card.fetch('Zork')).to be_nil
|
15
|
+
expect(Card.cache.read('zork').new_card?).to be_truthy
|
16
|
+
expect(Card.fetch('Zork')).to be_nil
|
17
17
|
end
|
18
18
|
|
19
|
-
it
|
19
|
+
it 'returns nil and caches trash cards' do
|
20
20
|
Card::Auth.as_bot do
|
21
|
-
card_double = class_double(
|
22
|
-
Card.fetch(
|
23
|
-
expect(Card.fetch(
|
21
|
+
card_double = class_double('Card')
|
22
|
+
Card.fetch('A').delete!
|
23
|
+
expect(Card.fetch('A')).to be_nil
|
24
24
|
expect(card_double).not_to receive(:find_by_key_and_trash)
|
25
|
-
expect(Card.fetch(
|
25
|
+
expect(Card.fetch('A')).to be_nil
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
it
|
30
|
-
expect(Card.fetch(
|
31
|
-
expect(Card.cache.read(
|
29
|
+
it 'returns and caches builtin cards' do
|
30
|
+
expect(Card.fetch('*head')).to be_instance_of(Card)
|
31
|
+
expect(Card.cache.read('*head')).not_to be_nil
|
32
32
|
end
|
33
33
|
|
34
|
-
it
|
34
|
+
it 'returns virtual cards and caches them as missing' do
|
35
35
|
Card::Auth.as_bot do
|
36
|
-
card = Card.fetch(
|
36
|
+
card = Card.fetch('Joe User+*email')
|
37
37
|
expect(card).to be_instance_of(Card)
|
38
|
-
expect(card.name).to eq(
|
38
|
+
expect(card.name).to eq('Joe User+*email')
|
39
39
|
expect(card.format.render_raw).to eq('joe@user.com')
|
40
40
|
end
|
41
|
-
#card.raw_content.should == 'joe@user.com'
|
42
|
-
#cached_card = Card.cache.read(
|
43
|
-
#cached_card.missing?.should be_true
|
44
|
-
#cached_card.virtual?.should be_true
|
41
|
+
# card.raw_content.should == 'joe@user.com'
|
42
|
+
# cached_card = Card.cache.read('joe_user+*email')
|
43
|
+
# cached_card.missing?.should be_true
|
44
|
+
# cached_card.virtual?.should be_true
|
45
45
|
end
|
46
46
|
|
47
|
-
it
|
47
|
+
it 'fetches virtual cards after skipping them' do
|
48
48
|
expect(Card['A+*self']).to be_nil
|
49
|
-
expect(Card.fetch(
|
49
|
+
expect(Card.fetch('A+*self')).not_to be_nil
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
expect(Card.fetch( 'A+virtual')).to be_nil
|
52
|
+
it 'fetches newly virtual cards' do
|
53
|
+
expect(Card.fetch('A+virtual')).to be_nil
|
55
54
|
Card::Auth.as_bot { Card.create name: 'virtual+*right+*structure' }
|
56
|
-
expect(Card.fetch(
|
55
|
+
expect(Card.fetch('A+virtual')).not_to be_nil
|
57
56
|
end
|
58
57
|
|
59
|
-
it
|
58
|
+
it 'handles name variants of cached cards' do
|
60
59
|
expect(Card.fetch('yomama+*self').name).to eq('yomama+*self')
|
61
60
|
expect(Card.fetch('YOMAMA+*self').name).to eq('YOMAMA+*self')
|
62
61
|
expect(Card.fetch('yomama', new: {}).name).to eq('yomama')
|
63
62
|
expect(Card.fetch('YOMAMA', new: {}).name).to eq('YOMAMA')
|
64
|
-
expect(Card.fetch('yomama!', new: { name: 'Yomama'}
|
63
|
+
expect(Card.fetch('yomama!', new: { name: 'Yomama' }).name)
|
64
|
+
.to eq('Yomama')
|
65
65
|
end
|
66
66
|
|
67
|
-
it
|
68
|
-
expect(Card.fetch(
|
67
|
+
it 'does not recurse infinitely on template templates' do
|
68
|
+
expect(Card.fetch('*structure+*right+*structure')).to be_nil
|
69
69
|
end
|
70
70
|
|
71
|
-
it
|
72
|
-
#Card.cache.dump # should be empty
|
73
|
-
Card.cache.
|
74
|
-
expect(Card.cache.
|
71
|
+
it 'expires card and dependencies on save' do
|
72
|
+
# Card.cache.dump # should be empty
|
73
|
+
Card.cache.soft.reset
|
74
|
+
expect(Card.cache.soft.store.keys).to eq([])
|
75
75
|
|
76
76
|
Card::Auth.as_bot do
|
77
|
-
a = Card.fetch(
|
77
|
+
a = Card.fetch('A')
|
78
78
|
expect(a).to be_instance_of(Card)
|
79
79
|
|
80
80
|
# expires the saved card
|
81
|
-
expect(
|
82
|
-
expect(
|
81
|
+
expect(a).to receive(:expire).and_call_original
|
82
|
+
# expect().to receive(:delete).with('a#SUBCARDS#').and_call_original
|
83
83
|
# expires plus cards
|
84
|
-
#expect(Card.cache).to receive(:delete).with('c+a')
|
85
|
-
#expect(Card.cache).to receive(:delete).with('d+a')
|
86
|
-
#expect(Card.cache).to receive(:delete).with('f+a')
|
87
|
-
#expect(Card.cache).to receive(:delete).with('a+b')
|
88
|
-
#expect(Card.cache).to receive(:delete).with('a+c')
|
89
|
-
#expect(Card.cache).to receive(:delete).with('a+d')
|
90
|
-
#expect(Card.cache).to receive(:delete).with('a+e')
|
91
|
-
#expect(Card.cache).to receive(:delete).with('a+b+c')
|
84
|
+
# expect(Card.cache).to receive(:delete).with('c+a')
|
85
|
+
# expect(Card.cache).to receive(:delete).with('d+a')
|
86
|
+
# expect(Card.cache).to receive(:delete).with('f+a')
|
87
|
+
# expect(Card.cache).to receive(:delete).with('a+b')
|
88
|
+
# expect(Card.cache).to receive(:delete).with('a+c')
|
89
|
+
# expect(Card.cache).to receive(:delete).with('a+d')
|
90
|
+
# expect(Card.cache).to receive(:delete).with('a+e')
|
91
|
+
# expect(Card.cache).to receive(:delete).with('a+b+c')
|
92
92
|
|
93
93
|
# expired including? cards
|
94
|
-
#expect(Card.cache).to receive(:delete).with('x').exactly(2).times
|
95
|
-
#expect(Card.cache).to receive(:delete).with('y').exactly(2).times
|
96
|
-
|
94
|
+
# expect(Card.cache).to receive(:delete).with('x').exactly(2).times
|
95
|
+
# expect(Card.cache).to receive(:delete).with('y').exactly(2).times
|
97
96
|
a.save!
|
98
97
|
end
|
99
98
|
end
|
99
|
+
describe 'default option' do
|
100
|
+
context "when card doesn't exist" do
|
101
|
+
it 'initializes new cards' do
|
102
|
+
card = Card.fetch 'non-existent',
|
103
|
+
new: { default_content: 'default content' }
|
104
|
+
expect(card.content).to eq 'default content'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
context 'when new card exist' do
|
108
|
+
it "doesn't change anything" do
|
109
|
+
Card.new name: 'new card',
|
110
|
+
'+sub' => { content: 'some content' }
|
111
|
+
card = Card.fetch 'new card+sub',
|
112
|
+
new: { default_content: 'new content' }
|
113
|
+
expect(card.content).to eq 'some content'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
100
117
|
|
101
|
-
describe
|
118
|
+
describe 'preferences' do
|
102
119
|
before do
|
103
120
|
Card::Auth.current_id = Card::WagnBotID
|
104
121
|
end
|
105
122
|
|
106
|
-
it
|
107
|
-
c1=Card.create!(name:
|
108
|
-
c2=Card.create!(name:
|
109
|
-
card = Card.fetch(
|
123
|
+
it 'prefers db cards to pattern virtual cards' do
|
124
|
+
c1=Card.create!(name: 'y+*right+*structure', content: 'Formatted Content')
|
125
|
+
c2=Card.create!(name: 'a+y', content: 'DB Content')
|
126
|
+
card = Card.fetch('a+y')
|
110
127
|
expect(card.virtual?).to be_falsey
|
111
|
-
expect(card.rule(:structure)).to eq(
|
112
|
-
expect(card.content).to eq(
|
128
|
+
expect(card.rule(:structure)).to eq('Formatted Content')
|
129
|
+
expect(card.content).to eq('DB Content')
|
113
130
|
end
|
114
131
|
|
115
|
-
it
|
116
|
-
Card.create!(name:
|
117
|
-
Card.create!(name:
|
118
|
-
Card.fetch(
|
132
|
+
it 'prefers a pattern virtual card to trash cards' do
|
133
|
+
Card.create!(name: 'y+*right+*structure', content: 'Formatted Content')
|
134
|
+
Card.create!(name: 'a+y', content: 'DB Content')
|
135
|
+
Card.fetch('a+y').delete!
|
119
136
|
|
120
|
-
card = Card.fetch(
|
137
|
+
card = Card.fetch('a+y')
|
121
138
|
expect(card.virtual?).to be
|
122
|
-
expect(card.raw_content).to eq(
|
139
|
+
expect(card.raw_content).to eq('Formatted Content')
|
123
140
|
end
|
124
141
|
|
125
|
-
it
|
126
|
-
|
127
|
-
tc=Card.create!(name:
|
128
|
-
card = Card.fetch(
|
142
|
+
it 'should recognize pattern overrides' do
|
143
|
+
# ~~~ create right rule
|
144
|
+
tc=Card.create!(name: 'y+*right+*structure', content: 'Right Content')
|
145
|
+
card = Card.fetch('a+y')
|
129
146
|
expect(card.virtual?).to be
|
130
|
-
expect(card.raw_content).to eq(
|
147
|
+
expect(card.raw_content).to eq('Right Content')
|
131
148
|
|
132
|
-
# warn
|
133
|
-
tpr = Card.create!
|
134
|
-
|
149
|
+
# warn 'creating template'
|
150
|
+
tpr = Card.create! name: 'Basic+y+*type plus right+*structure',
|
151
|
+
content: 'Type Plus Right Content'
|
152
|
+
card = Card.fetch('a+y')
|
135
153
|
expect(card.virtual?).to be
|
136
|
-
expect(card.raw_content).to eq(
|
154
|
+
expect(card.raw_content).to eq('Type Plus Right Content')
|
137
155
|
|
138
|
-
|
156
|
+
# ~~~ delete type plus right rule
|
139
157
|
tpr.delete!
|
140
|
-
card = Card.fetch(
|
158
|
+
card = Card.fetch('a+y')
|
141
159
|
expect(card.virtual?).to be
|
142
|
-
expect(card.raw_content).to eq(
|
143
|
-
|
160
|
+
expect(card.raw_content).to eq('Right Content')
|
144
161
|
end
|
145
162
|
|
146
|
-
it
|
147
|
-
card_double = class_double(
|
148
|
-
Card.create!(name:
|
149
|
-
Card.fetch(
|
163
|
+
it 'should not hit the database for every fetch_virtual lookup' do
|
164
|
+
card_double = class_double('Card')
|
165
|
+
Card.create!(name: 'y+*right+*structure', content: 'Formatted Content')
|
166
|
+
Card.fetch('a+y')
|
150
167
|
expect(card_double).not_to receive(:find_by_key)
|
151
|
-
Card.fetch(
|
168
|
+
Card.fetch('a+y')
|
152
169
|
end
|
153
170
|
|
154
|
-
it
|
171
|
+
it 'should not be a new_record after being saved' do
|
155
172
|
Card.create!(name: 'growing up')
|
156
173
|
card = Card.fetch('growing up')
|
157
174
|
expect(card.new_record?).to be_falsey
|
158
175
|
end
|
159
176
|
end
|
177
|
+
|
178
|
+
describe 'default_content option' do
|
179
|
+
context "when card doesn't exist" do
|
180
|
+
it 'initializes card with default content' do
|
181
|
+
card = Card.fetch 'non-existent',
|
182
|
+
new: { default_content: 'default content' }
|
183
|
+
expect(card.content).to eq 'default content'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
context 'when new card exist' do
|
187
|
+
it "doesn't change content" do
|
188
|
+
Card.new name: 'new card',
|
189
|
+
'+sub' => { content: 'some content' }
|
190
|
+
card = Card.fetch 'new card+sub',
|
191
|
+
new: { default_content: 'new content' }
|
192
|
+
expect(card.content).to eq 'some content'
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
160
196
|
end
|
161
197
|
|
162
|
-
describe
|
198
|
+
describe '#fetch new: { ... }' do
|
163
199
|
it "returns a new card if it doesn't find one" do
|
164
|
-
new_card = Card.fetch
|
200
|
+
new_card = Card.fetch 'Never Seen Me Before', new: {}
|
165
201
|
expect(new_card).to be_instance_of(Card)
|
166
202
|
expect(new_card.new_record?).to be_truthy
|
167
203
|
end
|
168
204
|
|
169
|
-
it
|
170
|
-
new_card = Card.fetch
|
205
|
+
it 'returns a card if it finds one' do
|
206
|
+
new_card = Card.fetch 'A+B', new: {}
|
171
207
|
expect(new_card).to be_instance_of(Card)
|
172
208
|
expect(new_card.new_record?).to be_falsey
|
173
209
|
end
|
174
210
|
|
175
|
-
it
|
176
|
-
new_card = Card.fetch
|
211
|
+
it 'takes a second hash of options as new card options' do
|
212
|
+
new_card = Card.fetch 'Never Before', new: { type: 'Image' }
|
177
213
|
expect(new_card).to be_instance_of(Card)
|
178
214
|
expect(new_card.type_code).to eq(:image)
|
179
215
|
expect(new_card.new_record?).to be_truthy
|
180
|
-
expect(Card.fetch(
|
216
|
+
expect(Card.fetch('Never Before', new: {}).type_id).to eq(Card::BasicID)
|
181
217
|
end
|
182
218
|
end
|
183
219
|
|
184
|
-
describe
|
185
|
-
|
220
|
+
describe '#fetch_virtual' do
|
221
|
+
before do
|
186
222
|
Card::Auth.as_bot do
|
187
|
-
Card.create! name:
|
223
|
+
Card.create! name: 'testsearch+*right+*structure',
|
224
|
+
content: '{"plus":"_self"}', type: 'Search'
|
188
225
|
end
|
189
|
-
|
226
|
+
end
|
227
|
+
it 'should find cards with *right+*structure specified' do
|
228
|
+
c = Card.fetch('A+testsearch'.to_name)
|
190
229
|
assert c.virtual?
|
191
230
|
expect(c.type_code).to eq(:search_type)
|
192
|
-
expect(c.raw_content).to eq(
|
231
|
+
expect(c.raw_content).to eq('{"plus":"_self"}')
|
232
|
+
end
|
233
|
+
context 'fetched virtual card with new args' do
|
234
|
+
it 'should fetch the virtual card with type set in patterns' do
|
235
|
+
Card.fetch '+testsearch', new: { name: '+testsearch',
|
236
|
+
supercard: Card['home'] }
|
237
|
+
|
238
|
+
c = Card.fetch('Home+testsearch'.to_name)
|
239
|
+
assert c.virtual?
|
240
|
+
expect(c.type_code).to eq(:search_type)
|
241
|
+
expect(c.raw_content).to eq('{"plus":"_self"}')
|
242
|
+
patterns = c.instance_variable_get('@patterns').map(&:to_s)
|
243
|
+
expect(patterns).to include('Search+*type')
|
244
|
+
end
|
193
245
|
end
|
194
246
|
end
|
195
247
|
|
196
|
-
describe
|
197
|
-
it
|
198
|
-
expect(Card.exists?(
|
248
|
+
describe '#exists?' do
|
249
|
+
it 'is true for cards that are there' do
|
250
|
+
expect(Card.exists?('A')).to eq(true)
|
199
251
|
end
|
200
252
|
|
201
|
-
it "is false for cards that
|
202
|
-
expect(Card.exists?(
|
253
|
+
it "is false for cards that aren't" do
|
254
|
+
expect(Card.exists?('Mumblefunk is gone')).to eq(false)
|
203
255
|
end
|
204
256
|
end
|
205
257
|
end
|