card 1.16.8 → 1.16.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/db/migrate_core_cards/{20150611203506_rails_inflection_updates.rb → 20120611203506_rails_inflection_updates.rb} +0 -0
  4. data/db/migrate_core_cards/20150326205655_bootswatch_themes.rb +0 -1
  5. data/db/migrate_core_cards/20150429090551_search_card_context.rb +1 -1
  6. data/db/migrate_core_cards/20150708224756_add_list_cards.rb +22 -0
  7. data/db/seed/new/card_actions.yml +789 -509
  8. data/db/seed/new/card_acts.yml +1 -1
  9. data/db/seed/new/card_changes.yml +2618 -1920
  10. data/db/seed/new/card_references.yml +1034 -901
  11. data/db/seed/new/cards.yml +2303 -1675
  12. data/db/seed/test/fixtures/card_actions.yml +1926 -1606
  13. data/db/seed/test/fixtures/card_acts.yml +354 -324
  14. data/db/seed/test/fixtures/card_changes.yml +5950 -5175
  15. data/db/seed/test/fixtures/card_references.yml +1861 -1630
  16. data/db/seed/test/fixtures/cards.yml +3768 -3048
  17. data/db/seed/test/seed.rb +121 -107
  18. data/lib/card.rb +2 -3
  19. data/lib/card/active_record_helper.rb +44 -0
  20. data/lib/card/auth.rb +51 -47
  21. data/lib/card/cache.rb +7 -3
  22. data/lib/card/codename.rb +7 -7
  23. data/lib/card/format.rb +2 -1
  24. data/lib/card/migration.rb +17 -16
  25. data/lib/card/name.rb +71 -20
  26. data/lib/card/set.rb +202 -166
  27. data/lib/card/simplecov_helper.rb +11 -7
  28. data/lib/card/subcards.rb +249 -0
  29. data/mod/01_core/set/all/collection.rb +1 -2
  30. data/mod/01_core/set/all/fetch.rb +167 -92
  31. data/mod/01_core/set/all/initialize.rb +8 -22
  32. data/mod/01_core/set/all/name.rb +128 -79
  33. data/mod/01_core/set/all/phases.rb +93 -95
  34. data/mod/01_core/set/all/subcards.rb +70 -0
  35. data/mod/01_core/set/all/tracked_attributes.rb +83 -59
  36. data/mod/01_core/set/all/trash.rb +14 -12
  37. data/mod/01_core/set/all/type.rb +3 -24
  38. data/mod/01_core/spec/set/all/initialize_spec.rb +44 -14
  39. data/mod/01_core/spec/set/all/permissions_spec.rb +206 -185
  40. data/mod/01_core/spec/set/all/tracked_attributes_spec.rb +0 -10
  41. data/mod/01_core/spec/set/all/trash_spec.rb +38 -13
  42. data/mod/01_core/spec/set/all/type_spec.rb +0 -19
  43. data/mod/01_history/set/all/content_history.rb +5 -3
  44. data/mod/01_history/set/all/history.rb +117 -82
  45. data/mod/02_basic_types/set/all/base.rb +50 -49
  46. data/mod/03_machines/lib/card/machine.rb +2 -1
  47. data/mod/03_machines/lib/javascript/wagn_mod.js.coffee +55 -17
  48. data/mod/03_machines/spec/set/type/javascript_spec.rb +18 -12
  49. data/mod/05_email/set/right/followers.rb +5 -5
  50. data/mod/05_email/set/right/following.rb +1 -1
  51. data/mod/05_email/set/type_plus_right/user/follow.rb +1 -1
  52. data/mod/05_standard/lib/carrier_wave/cardmount.rb +19 -11
  53. data/mod/05_standard/lib/file_uploader.rb +1 -1
  54. data/mod/05_standard/set/abstract/attachment.rb +20 -8
  55. data/mod/05_standard/set/all/list_changes.rb +43 -0
  56. data/mod/05_standard/set/all/rich_html/form.rb +21 -11
  57. data/mod/05_standard/set/all/rich_html/menu.rb +1 -1
  58. data/mod/05_standard/set/right/account.rb +5 -5
  59. data/mod/05_standard/set/self/head.rb +0 -1
  60. data/mod/05_standard/set/self/signin.rb +43 -35
  61. data/mod/05_standard/set/type/file.rb +9 -2
  62. data/mod/05_standard/set/type/list.rb +134 -0
  63. data/mod/05_standard/set/type/listed_by.rb +94 -0
  64. data/mod/05_standard/set/type/search_type.rb +62 -61
  65. data/mod/05_standard/set/type/signup.rb +94 -63
  66. data/mod/05_standard/set/type/user.rb +48 -39
  67. data/mod/05_standard/spec/set/all/account_spec.rb +1 -1
  68. data/mod/05_standard/spec/set/all/rich_html/form_spec.rb +2 -2
  69. data/mod/05_standard/spec/set/self/signin_spec.rb +23 -27
  70. data/mod/05_standard/spec/set/type/email_template_spec.rb +0 -2
  71. data/mod/05_standard/spec/set/type/list_spec.rb +140 -0
  72. data/mod/05_standard/spec/set/type/listed_by_spec.rb +157 -0
  73. data/mod/05_standard/spec/set/type/signup_spec.rb +38 -32
  74. data/spec/lib/card/subcards_spec.rb +126 -0
  75. metadata +14 -3
@@ -6,20 +6,24 @@ def card_simplecov_filters
6
6
  add_filter 'spec/'
7
7
  add_filter '/config/'
8
8
  add_filter '/tasks/'
9
-
10
- # filter all card mods
9
+ # filter all card mods
11
10
  add_filter do |src_file|
12
11
  src_file.filename =~ /tmp\// and not
13
- /\d+-(.+\.rb)/.match(src_file.filename) { |m| Dir["mod/**/#{m[1].gsub("-","/")}"].present? }
12
+ /\d+-(.+\.rb)/.match(src_file.filename) do |m|
13
+ Dir["mod/**/#{m[1].gsub("-","/").sub("/","/set/")}"].present?
14
+ end
14
15
  end
15
-
16
- # add group for each deck mod
16
+ # add group for each deck mod
17
17
  Dir['mod/*'].map{ |path| path.sub('mod/','') }.each do |mod|
18
18
  add_group mod.capitalize do |src_file|
19
- src_file.filename =~ /mod\/#{mod}\// or
19
+ src_file.filename =~ /mod\/#{mod}\// or
20
20
  (
21
21
  src_file.filename =~ /tmp\// and
22
- /\d+-(.+\.rb)/.match(src_file.filename) { |m| Dir["mod/#{mod}/**/#{m[1].gsub("-","/")}"].present? } and
22
+ match = /\d+-(.+\.rb)/.match(src_file.filename) do |m|
23
+ # '/set' is not in the path anymore after some updates
24
+ # but `set` exists in the path of the source files
25
+ Dir["mod/**/#{m[1].gsub("-","/").sub("/","/set/")}"].present?
26
+ end and
23
27
  is_in_mod? src_file,mod
24
28
  )
25
29
  end
@@ -0,0 +1,249 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ # API to create/update/delete additional cards together with the main card.
4
+ # The most common case is for fields but subcards don't have to be descendants.
5
+ #
6
+ # Example toghether with "my address" you want to create the subcards
7
+ # "my address+name", "my address+street", etc.
8
+ #
9
+ # Subcards can be added as card objects or attribute hashes.
10
+
11
+ class Card
12
+ def subcards
13
+ @subcards ||= Subcards.new(self)
14
+ end
15
+
16
+ def preserve_subcards
17
+ return unless subcards.present?
18
+ Card.cache.write_local subcards_cache_key, @subcards
19
+ end
20
+
21
+ def restore_subcards
22
+ cached_subcards = Card.cache.read_local(subcards_cache_key)
23
+ return unless cached_subcards
24
+ @subcards = cached_subcards
25
+ @subcards.context_card = self
26
+ end
27
+
28
+ def expire_subcards
29
+ Card.cache.delete_local subcards_cache_key
30
+ subcards.clear
31
+ end
32
+
33
+ def subcards_cache_key
34
+ "#{key}#SUBCARDS#"
35
+ end
36
+
37
+ class Subcards
38
+ attr_accessor :context_card, :keys
39
+ def initialize context_card
40
+ @context_card = context_card
41
+ @keys = ::Set.new
42
+ end
43
+
44
+ def clear
45
+ @keys.each do |key|
46
+ Card.cache.delete_local key
47
+ end
48
+ @keys = ::Set.new
49
+ end
50
+
51
+ def remove name_or_card
52
+ key = case name_or_card
53
+ when Card
54
+ name_or_card.key
55
+ when Symbol
56
+ fetch_subcard(name_or_card).key
57
+ else
58
+ name_or_card.to_name.key
59
+ end
60
+
61
+ key = absolutize_subcard_name(key).key unless @keys.include?(key)
62
+ @keys.delete key
63
+ removed_card = fetch_subcard key
64
+ Card.cache.delete_local key
65
+ removed_card
66
+ end
67
+
68
+ def add name_or_card_or_attr, card_or_attr=nil
69
+ if card_or_attr
70
+ name = name_or_card_or_attr
71
+ else
72
+ card_or_attr = name_or_card_or_attr
73
+ end
74
+ case card_or_attr
75
+ when Hash
76
+ args = card_or_attr
77
+ if name
78
+ new_by_attributes name, args
79
+ elsif args[:name]
80
+ new_by_attributes args.delete(:name), args
81
+ else
82
+ args.each_pair do |key, val|
83
+ case val
84
+ when String then new_by_attributes key, content: val
85
+ when Card
86
+ val.name = absolutize_subcard_name key
87
+ new_by_card val
88
+ else new_by_attributes key, val
89
+ end
90
+ end
91
+ end
92
+ when Card
93
+ new_by_card card_or_attr
94
+ when Symbol, String
95
+ new_by_attributes card_or_attr, {}
96
+ end
97
+ end
98
+
99
+ def rename old_name, new_name
100
+ return unless @keys.include? old_name.to_name.key
101
+
102
+ end
103
+
104
+
105
+
106
+ def << value
107
+ add value
108
+ end
109
+
110
+ def method_missing method, *args
111
+ return unless @keys.respond_to? method
112
+ @keys.send method, *args
113
+ end
114
+
115
+ def each_card
116
+ # fetch all cards first to avoid side effects
117
+ # e.g. deleting a user adds follow rules and +*account to subcards
118
+ # for deleting but deleting follow rules can remove +*account from the
119
+ # cache if it belongs to the rule cards
120
+ cards = @keys.map do |key|
121
+ fetch_subcard key
122
+ end
123
+ cards.each do |card|
124
+ yield(card) if card
125
+ end
126
+ end
127
+
128
+ alias_method :each, :each_card
129
+
130
+ def each_with_key
131
+ @keys.each do |key|
132
+ card = fetch_subcard(key)
133
+ yield(card, key) if card
134
+ end
135
+ end
136
+
137
+ def []= name, card_or_attr
138
+ case card_or_attr
139
+ when Hash
140
+ new_by_attributes name, card_or_attr
141
+ when Card
142
+ new_by_card card_or_attr
143
+ end
144
+ end
145
+
146
+ def [] name
147
+ card(name) || field(name)
148
+ end
149
+
150
+ def field name
151
+ key = field_name_to_key name
152
+ if @keys.include? key
153
+ fetch_subcard key
154
+ end
155
+ end
156
+
157
+ def card name
158
+ return unless @keys.include? name.to_name.key
159
+ fetch_subcard name
160
+ end
161
+
162
+ def add_child name, args
163
+ add prepend_plus(name), args
164
+ end
165
+
166
+ def remove_child name_or_card
167
+ if name_or_card.is_a? Card
168
+ remove name_or_card
169
+ else
170
+ absolute_name = @context_card.cardname.field_name(name_or_card)
171
+ if @keys.include? absolute_name.key
172
+ remove absolute_name
173
+ else
174
+ remove @context_card.cardname.relative_field_name(name_or_card)
175
+ end
176
+ end
177
+ end
178
+
179
+ alias_method :add_field, :add_child
180
+ alias_method :remove_field, :remove_child
181
+
182
+ def present?
183
+ @keys.present?
184
+ end
185
+
186
+ private
187
+
188
+ def fetch_subcard key
189
+ Card.fetch key, local_only: true, new: {}
190
+ end
191
+
192
+ def prepend_plus name
193
+ case name
194
+ when Symbol
195
+ "+#{Card[name].name}"
196
+ when /^\+/
197
+ name
198
+ else
199
+ "+#{name}"
200
+ end
201
+ end
202
+
203
+ def field_name_to_key name
204
+ if @context_card.name =~ /^\+/
205
+ @context_card.cardname.relative_field_name(name).key
206
+ else
207
+ absolute_key = @context_card.cardname.field_name(name).key
208
+ if @keys.include? absolute_key
209
+ absolute_key
210
+ else
211
+ @context_card.cardname.relative_field_name(name).key
212
+ end
213
+ end
214
+ end
215
+
216
+ def new_by_attributes name, attributes={}
217
+ absolute_name = absolutize_subcard_name name
218
+ if absolute_name.field_of?(@context_card.name) &&
219
+ (absolute_name.parts.size - @context_card.cardname.parts.size) > 2
220
+ left_card = new_by_attributes absolute_name.left
221
+ new_by_card left_card
222
+ left_card.new_by_attributes absolute_name, attributes
223
+ else
224
+ card = Card.assign_or_initialize_by absolute_name.s, attributes,
225
+ local_only: true
226
+ new_by_card card
227
+ end
228
+ end
229
+
230
+ def absolutize_subcard_name name
231
+ if @context_card.name =~ /^\+/
232
+ name.to_name
233
+ else
234
+ name.to_name.to_absolute_name(@context_card.name)
235
+ end
236
+ end
237
+
238
+ def new_by_card card
239
+ card.supercard = @context_card
240
+ if !card.cardname.simple? &&
241
+ card.cardname.field_of?(@context_card.cardname)
242
+ card.superleft = @context_card
243
+ end
244
+ @keys << card.key
245
+ Card.write_to_local_cache card
246
+ card
247
+ end
248
+ end
249
+ end
@@ -193,7 +193,6 @@ format do
193
193
  end
194
194
  end
195
195
 
196
-
197
196
  def nested_fields args={}
198
197
  result = []
199
198
  each_nested_field(args) do |chunk|
@@ -214,7 +213,7 @@ format do
214
213
 
215
214
  each_nested_chunk(args) do |chunk|
216
215
  # TODO handle structures that are non-virtual
217
- if chunk.referee_name.to_name.is_a_field_of? card.name
216
+ if chunk.referee_name.to_name.field_of? card.name
218
217
  if chunk.referee_card && chunk.referee_card.virtual? && !processed_chunk_keys.include?(chunk.referee_name.key)
219
218
  processed_chunk_keys << chunk.referee_name.key
220
219
  subformat(chunk.referee_card).each_nested_field(args) do |sub_chunk|
@@ -20,64 +20,77 @@ module ClassMethods
20
20
  # :skip_virtual Real cards only
21
21
  # :skip_modules Don't load Set modules
22
22
  # :look_in_trash Return trashed card objects
23
+ # :local_only Use only local cache for lookup and storing
23
24
  # new: { card opts } Return a new card when not found
24
25
  #
25
- def fetch mark, opts={}
26
- if String === mark
27
- case mark
28
- when /^\~(\d+)$/ # get by id
29
- mark = $1.to_i
30
- when /^\:(\w+)$/ # get by codename
31
- mark = $1.to_sym
32
- end
33
- end
34
- mark = Card::Codename[mark] if Symbol === mark # id from codename
26
+ def fetch mark, opts = {}
27
+ validate_fetch_opts! opts
28
+ mark = normalize_mark mark
35
29
 
36
30
  if mark.present?
37
- card, mark, needs_caching = fetch_from_cache_or_db mark, opts # have existing
31
+ card, mark, needs_caching = fetch_from_cache_or_db mark, opts
38
32
  else
39
33
  return unless opts[:new]
40
34
  end
41
35
 
42
- if Integer===mark
43
- return if card.nil? || mark.nil?
44
- else
45
- return card.renew(opts) if card and card.eager_renew?(opts)
46
- if !card or card.type_id==-1 && clean_cache_opts?(opts) # new (or improved) card for cache
47
- needs_caching = true
48
- card = new_for_cache mark, opts
49
- end
36
+ if mark.is_a?(Integer)
37
+ return if card.nil?
38
+ elsif card && card.new_card? && opts[:new].present?
39
+ return card.renew(opts)
40
+ elsif !card || (card.type_unknown? && !skip_type_lookup?(opts))
41
+ needs_caching = true
42
+ card = new_for_cache mark, opts # new (or improved) card for cache
50
43
  end
51
44
 
52
- write_to_cache card if Card.cache && needs_caching
45
+ write_to_cache card, opts if needs_caching
53
46
 
54
47
  if card.new_card?
55
- if opts[:new]
56
- return card.renew(opts) if !clean_cache_opts? opts
57
- elsif opts[:skip_virtual]
58
- return
59
- else
60
- card.include_set_modules unless opts[:skip_modules] # need to load modules here to call the right virtual? method
61
- return unless card.virtual?
48
+ case
49
+ when opts[:new].present? then return card.renew(opts)
50
+ when opts[:new] # noop for empty hash
51
+ when opts[:skip_virtual] then return nil
62
52
  end
63
- card.name = mark.to_s if mark && mark.to_s != card.name
53
+ card.rename_from_mark mark unless opts[:local_only]
64
54
  end
65
-
55
+ # need to load modules here to call the right virtual? method
66
56
  card.include_set_modules unless opts[:skip_modules]
67
- card
57
+ card if opts[:new] || card.known?
58
+ end
59
+
60
+ def fetch_local mark, opts = {}
61
+ fetch mark, opts.merge(:local_only=>true)
62
+ end
63
+
64
+ def fetch_id mark
65
+ if mark.is_a?(Integer)
66
+ mark
67
+ elsif mark.is_a?(Symbol) && Card::Codename[mark]
68
+ Card::Codename[mark]
69
+ else
70
+ card = quick_fetch mark.to_s
71
+ card && card.id
72
+ end
73
+ end
74
+
75
+ def quick_fetch mark
76
+ fetch mark, skip_virtual: true, skip_modules: true
68
77
  end
69
78
 
70
- def fetch_id mark #should optimize this. what if mark is int? or codename?
71
- card = fetch mark, skip_virtual: true, skip_modules: true
72
- card and card.id
79
+ def assign_or_initialize_by name, attributes, fetch_opts = {}
80
+ if (known_card = Card.fetch(name, fetch_opts))
81
+ known_card.refresh.assign_attributes attributes
82
+ known_card
83
+ else
84
+ Card.new attributes.merge(name: name)
85
+ end
73
86
  end
74
87
 
75
- def [](mark)
88
+ def [] mark
76
89
  fetch mark, skip_virtual: true
77
90
  end
78
91
 
79
92
  def exists? mark
80
- card = fetch mark, skip_virtual: true, skip_modules: true
93
+ card = quick_fetch mark
81
94
  card.present?
82
95
  end
83
96
 
@@ -86,14 +99,19 @@ module ClassMethods
86
99
  card.present?
87
100
  end
88
101
 
89
- def expire name
90
- #note: calling instance method breaks on dirty names
102
+ def expire name, subcards = false
103
+ # note: calling instance method breaks on dirty names
91
104
  key = name.to_name.key
92
- if card = Card.cache.read( key )
105
+ if card = Card.cache.read(key)
106
+ if subcards
107
+ card.expire_subcards
108
+ else
109
+ card.preserve_subcards
110
+ end
93
111
  Card.cache.delete key
94
112
  Card.cache.delete "~#{card.id}" if card.id
95
113
  end
96
- #Rails.logger.warn "expiring #{name}, #{card.inspect}"
114
+ # Rails.logger.warn "expiring #{name}, #{card.inspect}"
97
115
  end
98
116
 
99
117
  # set_names reverse map (cached)
@@ -116,90 +134,135 @@ module ClassMethods
116
134
  end
117
135
  end
118
136
 
119
- def cache
120
- Card::Cache[Card]
137
+ def validate_fetch_opts! opts
138
+ if opts[:new] && opts[:skip_virtual]
139
+ fail Card::Error, 'fetch called with new args and skip_virtual'
140
+ end
121
141
  end
122
142
 
123
- def fetch_from_cache cache_key
124
- Card.cache.read cache_key if Card.cache
143
+ def cache
144
+ Card::Cache[Card]
125
145
  end
126
146
 
127
- def fullname_from_name name, new_opts={}
128
- if new_opts and supercard = new_opts[:supercard]
129
- name.to_name.to_absolute_name supercard.name
130
- else
131
- name.to_name
147
+ def fetch_from_cache cache_key, local_only=false
148
+ if Card.cache
149
+ if local_only
150
+ Card.cache.read_local cache_key
151
+ else
152
+ Card.cache.read cache_key
153
+ end
132
154
  end
133
155
  end
134
156
 
135
157
  def fetch_from_cache_or_db mark, opts
136
158
  needs_caching = false
137
- mark_type = Integer===mark ? :id : :key
138
-
139
- if mark_type == :key
140
- mark = fullname_from_name mark, opts[:new]
141
- val = mark.key
142
- else
143
- val = mark
159
+ mark_type = mark.is_a?(Integer) ? :id : :key
160
+ expanded_mark = expand_mark mark, opts
161
+ card = send("fetch_from_cache_by_#{mark_type}",
162
+ expanded_mark, opts[:local_only])
163
+
164
+ if card.nil? || (opts[:look_in_trash] && card.new_card? && !card.trash)
165
+ query = { mark_type => expanded_mark }
166
+ query[:trash] = false unless opts[:look_in_trash]
167
+ card = fetch_from_db query
168
+ needs_caching = card && !card.trash
169
+ card.restore_subcards if card
144
170
  end
145
171
 
146
- card = send( "fetch_from_cache_by_#{mark_type}", val )
172
+ [card, mark, needs_caching]
173
+ end
147
174
 
148
- if opts[:look_in_trash]
149
- if card.nil? || (card.new_card? && !card.trash)
150
- card = Card.where( mark_type => val ).take
151
- needs_caching = card && !card.trash
152
- end
153
- elsif card.nil?
154
- needs_caching = true
155
- card = Card.where( mark_type => val, trash: false).take
175
+ def fetch_from_cache_by_id id, local_only = false
176
+ if name = fetch_from_cache("~#{id}", local_only)
177
+ fetch_from_cache name, local_only
156
178
  end
179
+ end
157
180
 
158
- [ card, mark, needs_caching ]
181
+ def fetch_from_cache_by_key key, local_only = false
182
+ fetch_from_cache key, local_only
159
183
  end
160
184
 
185
+ def fetch_from_db query
186
+ Card.where(query).take
187
+ end
161
188
 
162
- def fetch_from_cache_by_id id
163
- if name = fetch_from_cache("~#{id}")
164
- fetch_from_cache name
165
- end
189
+ def new_for_cache name, opts
190
+ new name: name,
191
+ skip_modules: true,
192
+ skip_type_lookup: skip_type_lookup?(opts)
166
193
  end
167
194
 
168
- def fetch_from_cache_by_key key
169
- fetch_from_cache key
195
+ def skip_type_lookup? opts
196
+ # if opts[:new] is not empty then we are initializing a variant that is
197
+ # different from the cached variant
198
+ # and can postpone type lookup for the cached variant
199
+ # if skipping virtual no need to look for actual type
200
+ opts[:skip_virtual] || opts[:new].present?
170
201
  end
171
202
 
172
- def new_for_cache name, opts
173
- new_args = { name: name, skip_modules: true }
174
- new_args[:type_id] = -1 unless clean_cache_opts? opts
175
- # The -1 type_id allows us to skip all the type lookup and flag the need for
176
- # reinitialization later. *** It should NEVER be seen elsewhere ***
177
- new new_args
203
+ def write_to_cache card, opts
204
+ if opts[:local_only]
205
+ write_to_local_cache card
206
+ elsif Card.cache
207
+ Card.cache.write card.key, card
208
+ Card.cache.write "~#{card.id}", card.key if card.id && card.id != 0
209
+ end
178
210
  end
179
211
 
180
- def clean_cache_opts? opts
181
- !opts[:skip_virtual] && !opts[:new].present?
212
+ def write_to_local_cache card
213
+ if Card.cache
214
+ Card.cache.write_local card.key, card
215
+ Card.cache.write_local "~#{card.id}", card.key if card.id && card.id != 0
216
+ end
182
217
  end
183
218
 
184
- def write_to_cache card
185
- Card.cache.write card.key, card
186
- Card.cache.write "~#{card.id}", card.key if card.id and card.id != 0
219
+ def expand_mark mark, opts
220
+ if mark.is_a?(Integer)
221
+ mark
222
+ else
223
+ fullname_from_name(mark, opts[:new]).key
224
+ end
187
225
  end
188
226
 
227
+ def normalize_mark mark
228
+ case mark
229
+ when String
230
+ case mark
231
+ when /^\~(\d+)$/ # get by id
232
+ $1.to_i
233
+ when /^\:(\w+)$/ # get by codename
234
+ Card::Codename[$1.to_sym]
235
+ else
236
+ mark
237
+ end
238
+ when Symbol
239
+ Card::Codename[mark] # id from codename
240
+ else
241
+ mark
242
+ end
243
+ end
189
244
 
245
+ def fullname_from_name name, new_opts = {}
246
+ if new_opts && supercard = new_opts[:supercard]
247
+ name.to_name.to_absolute_name supercard.name
248
+ else
249
+ name.to_name
250
+ end
251
+ end
190
252
  end
191
253
 
192
254
  # ~~~~~~~~~~ Instance ~~~~~~~~~~~~~
193
255
 
194
- def fetch opts={}
256
+ def fetch opts = {}
195
257
  if traits = opts.delete(:trait)
196
- traits = [traits] unless Array===traits
197
- traits.inject(self) { |card, trait| Card.fetch( card.cardname.trait(trait), opts ) }
258
+ traits = Array.wrap traits
259
+ traits.inject(self) do |card, trait|
260
+ Card.fetch card.cardname.trait(trait), opts
261
+ end
198
262
  end
199
263
  end
200
264
 
201
-
202
- def renew args={}
265
+ def renew args = {}
203
266
  opts = args[:new].clone
204
267
  opts[:name] ||= cardname
205
268
  opts[:skip_modules] = args[:skip_modules]
@@ -208,18 +271,22 @@ end
208
271
 
209
272
  def expire_pieces
210
273
  cardname.piece_names.each do |piece|
211
- Card.expire piece
274
+ Card.expire piece, !cardname.field_of?(piece)
212
275
  end
213
276
  end
214
277
 
215
-
216
- def expire
217
- #Rails.logger.warn "expiring i:#{id}, #{inspect}"
278
+ def expire subcards = false
279
+ # Rails.logger.warn "expiring i:#{id}, #{inspect}"
280
+ if subcards
281
+ expire_subcards
282
+ else
283
+ preserve_subcards
284
+ end
218
285
  Card.cache.delete key
219
286
  Card.cache.delete "~#{id}" if id
220
287
  end
221
288
 
222
- def refresh force=false
289
+ def refresh force = false
223
290
  if force || self.frozen? || self.readonly?
224
291
  fresh_card = self.class.find id
225
292
  fresh_card.include_set_modules
@@ -233,4 +300,12 @@ def eager_renew? opts
233
300
  opts[:skip_virtual] && new_card? && opts[:new].present?
234
301
  end
235
302
 
303
+ def type_unknown?
304
+ type_id.nil?
305
+ end
306
+
307
+ def rename_from_mark mark
308
+ return unless mark && mark.to_s != name
309
+ self.name = mark.to_s
310
+ end
236
311