card 1.16.14 → 1.16.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/db/migrate_core_cards/20150903130006_attachment_upload_cards.rb +4 -2
  4. data/lib/card/auth.rb +15 -10
  5. data/lib/card/codename.rb +25 -21
  6. data/lib/card/content.rb +100 -68
  7. data/lib/card/format.rb +158 -129
  8. data/lib/card/query.rb +15 -9
  9. data/lib/card/query/attributes.rb +41 -49
  10. data/lib/card/set.rb +15 -12
  11. data/lib/card/set_pattern.rb +4 -5
  12. data/lib/card/spec_helper.rb +54 -16
  13. data/lib/cardio.rb +43 -25
  14. data/mod/01_core/chunk/include.rb +1 -1
  15. data/mod/01_core/set/all/collection.rb +76 -73
  16. data/mod/01_core/set/all/content.rb +0 -4
  17. data/mod/01_core/set/all/fetch.rb +35 -42
  18. data/mod/01_core/set/all/name.rb +17 -7
  19. data/mod/01_core/set/all/pattern.rb +12 -11
  20. data/mod/01_core/set/all/permissions.rb +51 -42
  21. data/mod/01_core/set/all/phases.rb +2 -1
  22. data/mod/01_core/set/all/references.rb +2 -2
  23. data/mod/01_core/set/all/rules.rb +28 -35
  24. data/mod/01_core/set/all/subcards.rb +12 -12
  25. data/mod/01_core/set/all/tracked_attributes.rb +1 -1
  26. data/mod/01_core/set/all/type.rb +11 -11
  27. data/mod/01_core/set/all/utils.rb +6 -1
  28. data/mod/01_core/spec/set/all/fetch_spec.rb +6 -6
  29. data/mod/01_core/spec/set/all/permissions_spec.rb +11 -11
  30. data/mod/01_core/spec/set/all/tracked_attributes_spec.rb +1 -1
  31. data/mod/01_history/lib/card/action.rb +52 -47
  32. data/mod/01_history/set/all/actions.rb +20 -16
  33. data/mod/01_history/set/all/history.rb +18 -13
  34. data/mod/02_basic_types/set/all/base.rb +23 -2
  35. data/mod/02_basic_types/set/type/pointer.rb +45 -36
  36. data/mod/02_basic_types/spec/set/all/base_spec.rb +40 -24
  37. data/mod/02_basic_types/spec/set/type/pointer_spec.rb +87 -0
  38. data/mod/03_machines/set/right/machine_output.rb +10 -6
  39. data/mod/04_settings/set/abstract/permission.rb +10 -5
  40. data/mod/04_settings/set/type/setting.rb +4 -1
  41. data/mod/05_email/set/all/follow.rb +39 -44
  42. data/mod/05_email/set/all/notify.rb +4 -1
  43. data/mod/05_email/set/right/followers.rb +16 -14
  44. data/mod/05_email/set/self/follow_defaults.rb +22 -19
  45. data/mod/05_standard/lib/carrier_wave/cardmount.rb +1 -0
  46. data/mod/05_standard/set/abstract/attachment.rb +85 -58
  47. data/mod/05_standard/set/all/comment.rb +35 -19
  48. data/mod/05_standard/set/all/error.rb +124 -98
  49. data/mod/05_standard/set/all/list_changes.rb +27 -22
  50. data/mod/05_standard/set/all/rich_html/editing.rb +96 -70
  51. data/mod/05_standard/set/all/rich_html/form.rb +123 -81
  52. data/mod/05_standard/set/all/rich_html/modal.rb +15 -58
  53. data/mod/05_standard/set/right/account.rb +2 -2
  54. data/mod/05_standard/set/right/email.rb +3 -2
  55. data/mod/05_standard/set/rstar/rules.rb +3 -3
  56. data/mod/05_standard/set/self/search.rb +45 -22
  57. data/mod/05_standard/set/type/cardtype.rb +13 -11
  58. data/mod/05_standard/set/type/listed_by.rb +3 -2
  59. data/mod/05_standard/set/type/set.rb +17 -13
  60. data/mod/05_standard/set/type/signup.rb +1 -2
  61. data/mod/05_standard/set/type/user.rb +1 -1
  62. data/mod/05_standard/spec/set/all/account_spec.rb +1 -1
  63. data/mod/05_standard/spec/set/all/history_spec.rb +1 -1
  64. data/mod/05_standard/spec/set/type/email_template_spec.rb +140 -134
  65. data/mod/05_standard/spec/set/type/image_spec.rb +2 -1
  66. data/mod/05_standard/spec/set/type/signup_spec.rb +2 -2
  67. data/spec/models/card/trash_spec.rb +1 -1
  68. data/spec/spec_helper.rb +0 -1
  69. metadata +2 -2
@@ -20,38 +20,56 @@ module Cardio
20
20
  @@cache ||= ::Rails.cache
21
21
  end
22
22
 
23
- def set_config config
24
- @@config, @@root = config, config.root
23
+ def default_configs
24
+ {
25
+ read_only: read_only?,
26
+ allow_inline_styles: false,
25
27
 
26
- config.autoload_paths += Dir["#{gem_root}/mod/*/lib/**/"]
27
- config.autoload_paths += Dir["#{gem_root}/lib/**/"]
28
- config.autoload_paths += Dir["#{root}/mod/*/lib/**/"]
28
+ recaptcha_public_key: nil,
29
+ recaptcha_private_key: nil,
30
+ recaptcha_proxy: nil,
31
+
32
+ cache_store: [:file_store, 'tmp/cache'],
33
+ override_host: nil,
34
+ override_protocol: nil,
35
+
36
+ no_authentication: false,
37
+ files_web_path: 'files',
38
+
39
+ max_char_count: 200,
40
+ max_depth: 20,
41
+ email_defaults: nil,
29
42
 
30
- set_default_value config, :read_only, !!ENV['WAGN_READ_ONLY']
31
- set_default_value config, :allow_inline_styles, false
43
+ token_expiry: 2.days,
44
+ revisions_per_page: 10,
45
+ space_last_in_multispace: true,
46
+ closed_search_limit: 50,
32
47
 
33
- set_default_value config, :recaptcha_public_key, nil
34
- set_default_value config, :recaptcha_private_key, nil
35
- set_default_value config, :recaptcha_proxy, nil
48
+ non_createable_types: [%w{ signup setting set }],
49
+ view_cache: false,
36
50
 
37
- set_default_value config, :cache_store, :file_store, 'tmp/cache'
38
- set_default_value config, :override_host, nil
39
- set_default_value config, :override_protocol, nil
51
+ encoding: 'utf-8',
52
+ request_logger: false,
53
+ performance_logger: false,
54
+ sql_comments: true
55
+ }
56
+ end
40
57
 
41
- set_default_value config, :no_authentication, false
42
- set_default_value config, :files_web_path, 'files'
58
+ def set_config config
59
+ @@config = config
60
+ @@root = config.root
43
61
 
44
- set_default_value config, :max_char_count, 200
45
- set_default_value config, :max_depth, 20
46
- set_default_value config, :email_defaults, nil
62
+ config.autoload_paths += Dir["#{gem_root}/mod/*/lib/**/"]
63
+ config.autoload_paths += Dir["#{gem_root}/lib/**/"]
64
+ config.autoload_paths += Dir["#{root}/mod/*/lib/**/"]
47
65
 
48
- set_default_value config, :token_expiry, 2.days
49
- set_default_value config, :revisions_per_page, 10
50
- set_default_value config, :space_last_in_multispace, true
51
- set_default_value config, :closed_search_limit, 50
66
+ default_configs.each_pair do |setting, value|
67
+ set_default_value(config, setting, *value)
68
+ end
69
+ end
52
70
 
53
- set_default_value config, :non_createable_types, %w{ signup setting set }
54
- set_default_value config, :view_cache, false
71
+ def read_only?
72
+ !ENV['WAGN_READ_ONLY'].nil?
55
73
  end
56
74
 
57
75
  # In production mode set_config gets called twice.
@@ -162,7 +180,7 @@ module Cardio
162
180
  root_dir = (type == :deck_cards ? root : gem_root)
163
181
  stamp_dir = ENV['SCHEMA_STAMP_PATH'] || File.join(root_dir, 'db')
164
182
 
165
- File.join stamp_dir, "version#{ schema_suffix(type) }.txt"
183
+ File.join stamp_dir, "version#{schema_suffix type}.txt"
166
184
  end
167
185
  end
168
186
  end
@@ -5,7 +5,7 @@ require_dependency File.expand_path( '../reference', __FILE__ )
5
5
  module Card::Chunk
6
6
  class Include < Reference
7
7
  cattr_reader :options
8
- @@options = ::Set.new [ :inc_name, :inc_syntax, :view, :items, :type, :size, :title, :hide, :show, :structure, :params ]
8
+ @@options = ::Set.new [ :inc_name, :inc_syntax, :view, :items, :type, :size, :title, :hide, :show, :structure, :params, :variant ]
9
9
  attr_reader :options
10
10
 
11
11
  Card::Chunk.register_class self, {
@@ -1,27 +1,28 @@
1
1
 
2
2
  module ClassMethods
3
- def search spec
4
- results = ::Card::Query.run(spec)
5
- if block_given? and Array===results
3
+ def search spec, comment=nil
4
+ results = ::Card::Query.run(spec, comment)
5
+ if block_given? && results.is_a?(Array)
6
6
  results.each { |result| yield result }
7
7
  end
8
8
  results
9
9
  end
10
10
 
11
- def count_by_wql(spec)
11
+ def count_by_wql spec
12
12
  spec = spec.clone
13
13
  spec.delete(:offset)
14
14
  search spec.merge(return: 'count')
15
15
  end
16
16
 
17
- def find_each(options = {})
18
- #this is a copy from rails (3.2.16) and is needed because this is performed by a relation (ActiveRecord::Relation)
17
+ def find_each options={}
18
+ # this is a copy from rails (3.2.16) and is needed because this
19
+ # is performed by a relation (ActiveRecord::Relation)
19
20
  find_in_batches(options) do |records|
20
21
  records.each { |record| yield record }
21
22
  end
22
23
  end
23
24
 
24
- def find_in_batches(options = {})
25
+ def find_in_batches options={}
25
26
  if block_given?
26
27
  super(options) do |records|
27
28
  yield(records)
@@ -33,11 +34,11 @@ module ClassMethods
33
34
  end
34
35
  end
35
36
 
36
- def item_names(args={})
37
+ def item_names _args={}
37
38
  format._render_raw.split /[,\n]/
38
39
  end
39
40
 
40
- def item_cards(args={}) ## FIXME this is inconsistent with item_names
41
+ def item_cards _args={} # FIXME: this is inconsistent with item_names
41
42
  [self]
42
43
  end
43
44
 
@@ -52,39 +53,32 @@ def item_keys args={}
52
53
  end
53
54
 
54
55
  def include_item? item
55
- key = if Card === item
56
- item.cardname.key
57
- else
58
- item.to_name.key
59
- end
60
- item_names.map{|name| name.to_name.key}.member? key
56
+ key = item.is_a?(Card) ? item.cardname.key : item.to_name.key
57
+ item_names.map { |name| name.to_name.key }.member? key
61
58
  end
62
59
 
63
60
  def add_item item
64
- unless include_item? item
65
- self.content="#{self.content}\n#{name}"
66
- end
61
+ return if include_item? item
62
+ self.content = "#{content}\n#{name}"
67
63
  end
68
64
 
69
65
  def drop_item item
70
- if include_item? item
71
- new_names = item_names.reject{ |i| i == item }
72
- self.content = new_names.empty? ? '' : new_names.join("\n")
73
- end
66
+ return unless include_item? item
67
+ new_names = item_names.reject { |i| i == item }
68
+ self.content = new_names.empty? ? '' : new_names.join("\n")
74
69
  end
75
70
 
76
71
  def insert_item index, name
77
72
  new_names = item_names
78
- new_names.delete(name)
79
- new_names.insert(index,name)
80
- self.content = new_names.join "\n"
73
+ new_names.delete name
74
+ new_names.insert index, name
75
+ self.content = new_names.join "\n"
81
76
  end
82
77
 
83
-
84
78
  def extended_item_cards context = nil
85
- context = (context ? context.cardname : self.cardname)
86
- args={ limit: '' }
87
- items = self.item_cards(args.merge(context: context))
79
+ context = (context ? context.cardname : cardname)
80
+ args = { limit: '' }
81
+ items = item_cards(args.merge(context: context))
88
82
  extended_list = []
89
83
  already_extended = ::Set.new # avoid loops
90
84
 
@@ -103,42 +97,44 @@ def extended_item_cards context = nil
103
97
  extended_list
104
98
  end
105
99
 
106
- def extended_item_contents context = nil
100
+ def extended_item_contents context=nil
107
101
  extended_item_cards(context).map(&:item_names).flatten
108
102
  end
109
103
 
110
- def extended_list context = nil
111
- context = (context ? context.cardname : self.cardname)
112
- args={ limit: '' }
113
- self.item_cards(args.merge(context: context)).map do |x|
104
+ def extended_list context=nil
105
+ context = (context ? context.cardname : cardname)
106
+ args = { limit: '' }
107
+ item_cards(args.merge(context: context)).map do |x|
114
108
  x.item_cards(args)
115
109
  end.flatten.map do |x|
116
110
  x.item_cards(args)
117
111
  end.flatten.map do |y|
118
112
  y.item_names(args)
119
113
  end.flatten
120
- # this could go on and on. more elegant to recurse until you don't have a collection
114
+ # this could go on and on. more elegant to recurse until you don't have
115
+ # a collection
121
116
  end
122
117
 
123
118
  def contextual_content context_card, format_args={}, view_args={}
124
119
  context_card.format(format_args).process_content(
125
- self.format(format_args)._render_raw(view_args), view_args
120
+ format(format_args)._render_raw(view_args), view_args
126
121
  )
127
122
  end
128
123
 
129
124
  format do
130
-
131
- def item_links(args={})
125
+ def item_links _args={}
132
126
  raw(render_core).split /[,\n]/
133
127
  end
134
128
 
135
129
  def item_view args
136
- args[:item] || (@inclusion_opts && @inclusion_opts[:view]) || default_item_view
130
+ args[:item] ||
131
+ (@inclusion_opts && @inclusion_opts[:view]) ||
132
+ default_item_view
137
133
  end
138
134
 
139
135
  def item_args args
140
- i_args = { view: item_view(args)}
141
- if type = card.item_type
136
+ i_args = { view: item_view(args) }
137
+ if (type = card.item_type)
142
138
  i_args[:type] = type
143
139
  end
144
140
  i_args
@@ -171,24 +167,24 @@ format do
171
167
 
172
168
  def set_search_params_variables! hash
173
169
  hash[:vars] = params[:vars] || {}
174
- params.each do |key,val|
170
+ params.each do |key, val|
175
171
  case key.to_s
176
- when '_wql' ; hash.merge! val
177
- when /^\_(\w+)$/ ; hash[:vars][$1.to_sym] = val
172
+ when '_wql' then hash.merge! val
173
+ when /^\_(\w+)$/ then hash[:vars][$1.to_sym] = val
178
174
  end
179
175
  end
180
176
  end
181
177
 
182
-
183
178
  def each_reference_with_args args={}
184
- Card::Content.new(_render_raw(args), card).find_chunks( Card::Chunk::Reference ).each do |chunk|
185
- yield(chunk.referee_name.to_s, nest_args(args,chunk))
179
+ content_object = Card::Content.new _render_raw(args), card
180
+ content_object.find_chunks(Card::Chunk::Reference).each do |chunk|
181
+ yield chunk.referee_name.to_s, nest_args(args, chunk)
186
182
  end
187
183
  end
188
184
 
189
-
190
185
  def each_nested_chunk args={}
191
- Card::Content.new(_render_raw(args), card).find_chunks( Card::Chunk::Include).each do |chunk|
186
+ content_object = Card::Content.new(_render_raw(args), card)
187
+ content_object.find_chunks(Card::Chunk::Include).each do |chunk|
192
188
  yield(chunk) if chunk.referee_name # filter commented nests
193
189
  end
194
190
  end
@@ -202,19 +198,21 @@ format do
202
198
  end
203
199
 
204
200
  def unique_chunks chunk, processed_set, &block
205
- if !processed_set.include? chunk.referee_name.key
206
- processed_set << chunk.referee_name.key
207
- block.call(chunk)
208
- end
201
+ return if processed_set.include? chunk.referee_name.key
202
+ processed_set << chunk.referee_name.key
203
+ block.call(chunk)
209
204
  end
210
205
 
211
206
  def each_nested_field args, &block
212
207
  processed_chunk_keys = ::Set.new([card.key])
213
208
 
214
209
  each_nested_chunk(args) do |chunk|
215
- # TODO handle structures that are non-virtual
210
+ # TODO: handle structures that are non-virtual
216
211
  if chunk.referee_name.to_name.field_of? card.name
217
- if chunk.referee_card && chunk.referee_card.virtual? && !processed_chunk_keys.include?(chunk.referee_name.key)
212
+ if chunk.referee_card &&
213
+ chunk.referee_card.virtual? &&
214
+ !processed_chunk_keys.include?(chunk.referee_name.key)
215
+
218
216
  processed_chunk_keys << chunk.referee_name.key
219
217
  subformat(chunk.referee_card).each_nested_field(args) do |sub_chunk|
220
218
  unique_chunks sub_chunk, processed_chunk_keys, &block
@@ -224,7 +222,6 @@ format do
224
222
  end
225
223
  end
226
224
  end
227
-
228
225
  end
229
226
 
230
227
  def map_references_with_args args={}, &block
@@ -238,21 +235,19 @@ format do
238
235
  # process args for links and nests
239
236
  def nest_args args, chunk=nil
240
237
  r_args = item_args(args)
241
- if @inclusion_opts
242
- r_args.merge! @inclusion_opts.clone
243
- end
244
- if chunk.kind_of? Card::Chunk::Include
238
+ r_args.merge! @inclusion_opts.clone if @inclusion_opts
239
+
240
+ case chunk
241
+ when Card::Chunk::Include
245
242
  r_args.merge!(chunk.options)
246
- elsif chunk.kind_of? Card::Chunk::Link
243
+ when Card::Chunk::Link
247
244
  r_args.reverse_merge!(view: :link)
248
245
  r_args.reverse_merge!(title: chunk.link_text) if chunk.link_text
249
246
  end
250
247
  r_args
251
248
  end
252
-
253
249
  end
254
250
 
255
-
256
251
  format :html do
257
252
  view :count do |args|
258
253
  card.item_names(args).size
@@ -266,11 +261,13 @@ format :html do
266
261
  id = "#{card.cardname.safe_key}-#{name.to_name.safe_key}"
267
262
  url = nest_path name, nest_args
268
263
  tab_name = nest_args[:title] || name
269
- tab_buttons += tab_button( "##{id}", tab_name, active_tab, 'data-url'=>url.html_safe, class: (active_tab ? nil : 'load'))
264
+ tab_buttons += tab_button(
265
+ "##{id}", tab_name, active_tab, 'data-url' => url.html_safe,
266
+ class: (active_tab ? nil : 'load'))
270
267
 
271
268
  # only render the first active tab, other tabs get loaded via ajax
272
269
  tab_content = active_tab ? nest(Card.fetch(name, new: {}), nest_args) : ''
273
- tab_panes += tab_pane( id, tab_content, active_tab )
270
+ tab_panes += tab_pane(id, tab_content, active_tab)
274
271
  active_tab = false
275
272
  end
276
273
  tab_panel tab_buttons, tab_panes, args[:tab_type]
@@ -279,7 +276,6 @@ format :html do
279
276
  args[:tab_type] ||= 'tabs'
280
277
  end
281
278
 
282
-
283
279
  # create a path for a nest with respect ot the inclusion options
284
280
  def nest_path name, nest_args
285
281
  path_args = {}
@@ -299,9 +295,9 @@ format :html do
299
295
  tab_panes = ''
300
296
  card.item_cards.each_with_index do |item, index|
301
297
  id = "#{card.cardname.safe_key}-#{item.cardname.safe_key}"
302
- tab_buttons += tab_button( "##{id}", item.name, index == 0 )
298
+ tab_buttons += tab_button("##{id}", item.name, index == 0)
303
299
  tab_content = nest item, item_args(args)
304
- tab_panes += tab_pane( id, tab_content, index == 0 )
300
+ tab_panes += tab_pane(id, tab_content, index == 0)
305
301
  end
306
302
  tab_panel tab_buttons, tab_panes, args[:tab_type]
307
303
  end
@@ -315,24 +311,31 @@ format :html do
315
311
  end
316
312
 
317
313
  def tab_panel tab_buttons, tab_panes, tab_type='tabs'
318
- wrap_with :div, role: "tabpanel" do
314
+ wrap_with :div, role: 'tabpanel' do
319
315
  [
320
- content_tag(:ul, tab_buttons.html_safe, class: "nav nav-#{tab_type}", role: "tablist"),
316
+ content_tag(:ul, tab_buttons.html_safe, class: "nav nav-#{tab_type}",
317
+ role: 'tablist'),
321
318
  content_tag(:div, tab_panes.html_safe, class: 'tab-content')
322
319
  ]
323
320
  end
324
321
  end
325
322
 
326
323
  def tab_button target, text, active=false, link_attr={}
327
- link = link_to fancy_title(text), target, link_attr.merge('role'=>'tab','data-toggle'=>'tab')
324
+ link = link_to(
325
+ fancy_title(text),
326
+ target,
327
+ link_attr.merge('role' => 'tab', 'data-toggle' => 'tab'))
328
328
  li_args = { role: :presentation }
329
329
  li_args[:class] = 'active' if active
330
330
  content_tag :li, link, li_args
331
331
  end
332
332
 
333
333
  def tab_pane id, content, active=false
334
- div_args = {role: :tabpanel, id: id, class: "tab-pane #{'active' if active}"}
334
+ div_args = {
335
+ role: :tabpanel,
336
+ id: id,
337
+ class: "tab-pane #{'active' if active}"
338
+ }
335
339
  content_tag :div, content.html_safe, div_args
336
340
  end
337
341
  end
338
-
@@ -34,10 +34,6 @@ def clean_html?
34
34
  true
35
35
  end
36
36
 
37
- def history?
38
- false
39
- end
40
-
41
37
  def save_content_draft _content
42
38
  clear_drafts
43
39
  end
@@ -24,23 +24,16 @@ module ClassMethods
24
24
  #
25
25
  def fetch mark, opts={}
26
26
  validate_fetch_opts! opts
27
- mark = normalize_mark mark
27
+ mark = normalize_mark mark, opts
28
28
 
29
- if mark.present?
30
- card, mark, needs_caching = fetch_from_cache_or_db mark, opts
31
- else
32
- return unless opts[:new]
33
- end
29
+ card, needs_caching = fetch_existing mark, opts
34
30
 
35
- if mark.is_a?(Integer)
36
- return if card.nil?
37
- elsif card && card.new_card? && opts[:new].present?
38
- return card.renew(opts)
39
- elsif !card || (card.type_unknown? && !skip_type_lookup?(opts))
31
+ if (new_card = new_for_cache card, mark, opts)
32
+ card = new_card
40
33
  needs_caching = true
41
- card = new_for_cache mark, opts # new (or improved) card for cache
42
34
  end
43
35
 
36
+ return if card.nil?
44
37
  write_to_cache card, opts if needs_caching
45
38
  standard_fetch_results card, mark, opts
46
39
  end
@@ -63,11 +56,10 @@ module ClassMethods
63
56
  fetch mark, opts.merge(local_only: true)
64
57
  end
65
58
 
66
- def fetch_id mark
59
+ def fetch_id mark, opts={}
60
+ mark = normalize_mark mark, opts
67
61
  if mark.is_a?(Integer)
68
62
  mark
69
- elsif mark.is_a?(Symbol) && Card::Codename[mark]
70
- Card::Codename[mark]
71
63
  else
72
64
  card = quick_fetch mark.to_s
73
65
  card && card.id
@@ -154,34 +146,34 @@ module ClassMethods
154
146
  end
155
147
  end
156
148
 
157
- def parse_mark! mark, opts
149
+ def parse_mark! mark
158
150
  # return mark_type, mark_value, and absolutized mark
159
151
  if mark.is_a? Integer
160
- [:id, mark, mark]
152
+ [:id, mark]
161
153
  else
162
- fullname = fullname_from_name mark, opts[:new]
163
- [:key, fullname.key, fullname.s]
154
+ [:key, mark.key]
164
155
  end
165
156
  end
166
157
 
167
- def fetch_from_cache_or_db mark, opts
168
- mark_type, mark_key, mark = parse_mark! mark, opts
158
+ def fetch_existing mark, opts
159
+ return [nil, false] if !mark.present?
160
+ mark_type, mark_key = parse_mark! mark
169
161
  needs_caching = false # until proven true :)
170
162
 
171
163
  # look in cache
172
164
  card = send "fetch_from_cache_by_#{mark_type}", mark_key, opts[:local_only]
173
165
 
174
- # if that doesn't work, look in db
175
- if card.nil? || retrieve_trashed_from_db?(card, opts)
166
+ if retrieve_from_db?(card, opts)
167
+ # look in db if needed
176
168
  card = fetch_from_db mark_type, mark_key, opts
177
- needs_caching = card && !card.trash
169
+ needs_caching = !card.nil? && !card.trash
178
170
  end
179
171
 
180
- [card, mark, needs_caching]
172
+ [card, needs_caching]
181
173
  end
182
174
 
183
- def retrieve_trashed_from_db? card, opts
184
- opts[:look_in_trash] && card.new_card? && !card.trash
175
+ def retrieve_from_db? card, opts
176
+ card.nil? || (opts[:look_in_trash] && card.new_card? && !card.trash)
185
177
  end
186
178
 
187
179
  def fetch_from_cache_by_id id, local_only=false
@@ -201,7 +193,10 @@ module ClassMethods
201
193
  card
202
194
  end
203
195
 
204
- def new_for_cache name, opts
196
+ def new_for_cache card, name, opts
197
+ return if name.is_a? Integer
198
+ return if !name.present? && !opts[:new]
199
+ return unless !card || (card.type_unknown? && !skip_type_lookup?(opts))
205
200
  new name: name,
206
201
  skip_modules: true,
207
202
  skip_type_lookup: skip_type_lookup?(opts)
@@ -230,25 +225,23 @@ module ClassMethods
230
225
  Card.cache.write_local "~#{card.id}", card.key if card.id && card.id != 0
231
226
  end
232
227
 
233
- def normalize_mark mark
228
+ def normalize_mark mark, opts
234
229
  case mark
235
- when String
236
- case mark
237
- when /^\~(\d+)$/ # get by id
238
- $1.to_i
239
- when /^\:(\w+)$/ # get by codename
240
- Card::Codename[$1.to_sym]
241
- else
242
- mark
230
+ when Symbol then Card::Codename[mark]
231
+ when Integer then mark.to_i
232
+ when String, SmartName
233
+ # there are some situations where this breaks if we use Card::Name
234
+ # rather than SmartName, which would seem more correct.
235
+ # very hard to reproduce, not captured in a spec :(
236
+ case mark.to_s
237
+ when /^\~(\d+)$/ then $1.to_i # id
238
+ when /^\:(\w+)$/ then Card::Codename[$1.to_sym] # codename
239
+ else fullname_from_mark mark, opts[:new] # name
243
240
  end
244
- when Symbol
245
- Card::Codename[mark] # id from codename
246
- else
247
- mark
248
241
  end
249
242
  end
250
243
 
251
- def fullname_from_name name, new_opts={}
244
+ def fullname_from_mark name, new_opts={}
252
245
  if new_opts && (supercard = new_opts[:supercard])
253
246
  name.to_name.to_absolute_name supercard.name
254
247
  else