card 1.16.8 → 1.16.9
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/db/migrate_core_cards/{20150611203506_rails_inflection_updates.rb → 20120611203506_rails_inflection_updates.rb} +0 -0
- data/db/migrate_core_cards/20150326205655_bootswatch_themes.rb +0 -1
- data/db/migrate_core_cards/20150429090551_search_card_context.rb +1 -1
- data/db/migrate_core_cards/20150708224756_add_list_cards.rb +22 -0
- data/db/seed/new/card_actions.yml +789 -509
- data/db/seed/new/card_acts.yml +1 -1
- data/db/seed/new/card_changes.yml +2618 -1920
- data/db/seed/new/card_references.yml +1034 -901
- data/db/seed/new/cards.yml +2303 -1675
- data/db/seed/test/fixtures/card_actions.yml +1926 -1606
- data/db/seed/test/fixtures/card_acts.yml +354 -324
- data/db/seed/test/fixtures/card_changes.yml +5950 -5175
- data/db/seed/test/fixtures/card_references.yml +1861 -1630
- data/db/seed/test/fixtures/cards.yml +3768 -3048
- data/db/seed/test/seed.rb +121 -107
- data/lib/card.rb +2 -3
- data/lib/card/active_record_helper.rb +44 -0
- data/lib/card/auth.rb +51 -47
- data/lib/card/cache.rb +7 -3
- data/lib/card/codename.rb +7 -7
- data/lib/card/format.rb +2 -1
- data/lib/card/migration.rb +17 -16
- data/lib/card/name.rb +71 -20
- data/lib/card/set.rb +202 -166
- data/lib/card/simplecov_helper.rb +11 -7
- data/lib/card/subcards.rb +249 -0
- data/mod/01_core/set/all/collection.rb +1 -2
- data/mod/01_core/set/all/fetch.rb +167 -92
- data/mod/01_core/set/all/initialize.rb +8 -22
- data/mod/01_core/set/all/name.rb +128 -79
- data/mod/01_core/set/all/phases.rb +93 -95
- data/mod/01_core/set/all/subcards.rb +70 -0
- data/mod/01_core/set/all/tracked_attributes.rb +83 -59
- data/mod/01_core/set/all/trash.rb +14 -12
- data/mod/01_core/set/all/type.rb +3 -24
- data/mod/01_core/spec/set/all/initialize_spec.rb +44 -14
- data/mod/01_core/spec/set/all/permissions_spec.rb +206 -185
- data/mod/01_core/spec/set/all/tracked_attributes_spec.rb +0 -10
- data/mod/01_core/spec/set/all/trash_spec.rb +38 -13
- data/mod/01_core/spec/set/all/type_spec.rb +0 -19
- data/mod/01_history/set/all/content_history.rb +5 -3
- data/mod/01_history/set/all/history.rb +117 -82
- data/mod/02_basic_types/set/all/base.rb +50 -49
- data/mod/03_machines/lib/card/machine.rb +2 -1
- data/mod/03_machines/lib/javascript/wagn_mod.js.coffee +55 -17
- data/mod/03_machines/spec/set/type/javascript_spec.rb +18 -12
- data/mod/05_email/set/right/followers.rb +5 -5
- data/mod/05_email/set/right/following.rb +1 -1
- data/mod/05_email/set/type_plus_right/user/follow.rb +1 -1
- data/mod/05_standard/lib/carrier_wave/cardmount.rb +19 -11
- data/mod/05_standard/lib/file_uploader.rb +1 -1
- data/mod/05_standard/set/abstract/attachment.rb +20 -8
- data/mod/05_standard/set/all/list_changes.rb +43 -0
- data/mod/05_standard/set/all/rich_html/form.rb +21 -11
- data/mod/05_standard/set/all/rich_html/menu.rb +1 -1
- data/mod/05_standard/set/right/account.rb +5 -5
- data/mod/05_standard/set/self/head.rb +0 -1
- data/mod/05_standard/set/self/signin.rb +43 -35
- data/mod/05_standard/set/type/file.rb +9 -2
- data/mod/05_standard/set/type/list.rb +134 -0
- data/mod/05_standard/set/type/listed_by.rb +94 -0
- data/mod/05_standard/set/type/search_type.rb +62 -61
- data/mod/05_standard/set/type/signup.rb +94 -63
- data/mod/05_standard/set/type/user.rb +48 -39
- data/mod/05_standard/spec/set/all/account_spec.rb +1 -1
- data/mod/05_standard/spec/set/all/rich_html/form_spec.rb +2 -2
- data/mod/05_standard/spec/set/self/signin_spec.rb +23 -27
- data/mod/05_standard/spec/set/type/email_template_spec.rb +0 -2
- data/mod/05_standard/spec/set/type/list_spec.rb +140 -0
- data/mod/05_standard/spec/set/type/listed_by_spec.rb +157 -0
- data/mod/05_standard/spec/set/type/signup_spec.rb +38 -32
- data/spec/lib/card/subcards_spec.rb +126 -0
- metadata +14 -3
data/lib/card/cache.rb
CHANGED
@@ -147,7 +147,7 @@ class Card
|
|
147
147
|
|
148
148
|
def write_variable key, variable, value
|
149
149
|
key = @prefix + key
|
150
|
-
if @store
|
150
|
+
if @store && (object = @store.read key)
|
151
151
|
object.instance_variable_set "@#{ variable }", value
|
152
152
|
@store.write key, object
|
153
153
|
end
|
@@ -174,7 +174,7 @@ class Card
|
|
174
174
|
end
|
175
175
|
|
176
176
|
def fetch_local key
|
177
|
-
read_local
|
177
|
+
read_local(key) || write_local(key, yield)
|
178
178
|
end
|
179
179
|
|
180
180
|
def delete key
|
@@ -182,6 +182,10 @@ class Card
|
|
182
182
|
@local.delete key
|
183
183
|
end
|
184
184
|
|
185
|
+
def delete_local key
|
186
|
+
@local.delete key
|
187
|
+
end
|
188
|
+
|
185
189
|
def dump
|
186
190
|
p "dumping local...."
|
187
191
|
@local.each do |k, v|
|
@@ -211,7 +215,7 @@ class Card
|
|
211
215
|
end
|
212
216
|
|
213
217
|
def exist? key
|
214
|
-
@local.has_key?(key) || @store.exist?(key)
|
218
|
+
@local.has_key?(key) || (@store && @store.exist?(key))
|
215
219
|
end
|
216
220
|
end
|
217
221
|
end
|
data/lib/card/codename.rb
CHANGED
@@ -29,10 +29,10 @@ class Card
|
|
29
29
|
def bootdata hash
|
30
30
|
@@codehash = hash
|
31
31
|
end
|
32
|
-
|
33
|
-
|
32
|
+
|
33
|
+
|
34
34
|
private
|
35
|
-
|
35
|
+
|
36
36
|
def cache
|
37
37
|
Card::Cache[Codename]
|
38
38
|
end
|
@@ -53,10 +53,10 @@ class Card
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
end
|
58
|
-
|
59
|
-
|
58
|
+
|
59
|
+
|
60
60
|
def self.const_missing const
|
61
61
|
if const.to_s =~ /^([A-Z]\S*)ID$/ and code=$1.underscore.to_sym
|
62
62
|
if card_id = Codename[code]
|
@@ -68,5 +68,5 @@ class Card
|
|
68
68
|
super
|
69
69
|
end
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
end
|
data/lib/card/format.rb
CHANGED
@@ -204,7 +204,8 @@ class Card
|
|
204
204
|
args = default_render_args view, args
|
205
205
|
with_inclusion_mode view do
|
206
206
|
Card::ViewCache.fetch(self, view, args) do
|
207
|
-
|
207
|
+
method = method "_view_#{ view }"
|
208
|
+
method.arity == 0 ? method.call : method.call(args)
|
208
209
|
end
|
209
210
|
end
|
210
211
|
end
|
data/lib/card/migration.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
3
|
class Card::Migration < ActiveRecord::Migration
|
4
|
+
include Card::ActiveRecordHelper
|
4
5
|
@type = :deck_cards
|
5
6
|
|
6
7
|
class << self
|
7
|
-
|
8
8
|
# Rake tasks use class methods, migrations use instance methods.
|
9
|
-
# To avoid repetition a lot of instance methods here just call class
|
9
|
+
# To avoid repetition a lot of instance methods here just call class
|
10
|
+
# methods.
|
10
11
|
# The subclass Card::CoreMigration needs a different @type so we can't use a
|
11
12
|
# class variable @@type. It has to be a class instance variable.
|
12
|
-
# Migrations are subclasses of Card::Migration or Card::CoreMigration
|
13
|
-
# don't inherit the @type. The method below solves this problem.
|
13
|
+
# Migrations are subclasses of Card::Migration or Card::CoreMigration
|
14
|
+
# but they don't inherit the @type. The method below solves this problem.
|
14
15
|
def type
|
15
16
|
@type || (ancestors[1] && ancestors[1].type)
|
16
17
|
end
|
@@ -18,9 +19,9 @@ class Card::Migration < ActiveRecord::Migration
|
|
18
19
|
def find_unused_name base_name
|
19
20
|
test_name = base_name
|
20
21
|
add = 1
|
21
|
-
while Card.exists?(test_name)
|
22
|
+
while Card.exists?(test_name)
|
22
23
|
test_name = "#{base_name}#{add}"
|
23
|
-
add +=1
|
24
|
+
add += 1
|
24
25
|
end
|
25
26
|
test_name
|
26
27
|
end
|
@@ -50,18 +51,18 @@ class Card::Migration < ActiveRecord::Migration
|
|
50
51
|
|
51
52
|
def assume_migrated_upto_version
|
52
53
|
schema_mode do
|
53
|
-
ActiveRecord::Schema.assume_migrated_upto_version schema,
|
54
|
+
ActiveRecord::Schema.assume_migrated_upto_version schema,
|
55
|
+
migration_paths
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
57
59
|
def data_path filename=nil
|
58
60
|
path = migration_paths.first
|
59
|
-
File.join(
|
61
|
+
File.join([path, 'data', filename].compact)
|
60
62
|
end
|
61
|
-
|
62
63
|
end
|
63
64
|
|
64
|
-
def contentedly
|
65
|
+
def contentedly
|
65
66
|
Card::Cache.reset_global
|
66
67
|
Cardio.schema_mode '' do
|
67
68
|
Card::Auth.as_bot do
|
@@ -78,14 +79,15 @@ class Card::Migration < ActiveRecord::Migration
|
|
78
79
|
|
79
80
|
def import_json filename, merge_opts={}
|
80
81
|
Card.config.action_mailer.perform_deliveries = false
|
81
|
-
|
82
|
+
output_file = File.join data_path, "unmerged_#{ filename }"
|
83
|
+
merge_opts[:output_file] ||= output_file
|
82
84
|
Card.merge_list read_json(filename), merge_opts
|
83
85
|
end
|
84
86
|
|
85
87
|
def read_json filename
|
86
|
-
raw_json = File.read
|
88
|
+
raw_json = File.read data_path(filename)
|
87
89
|
json = JSON.parse raw_json
|
88
|
-
json[
|
90
|
+
json['card']['value']
|
89
91
|
end
|
90
92
|
|
91
93
|
def data_path filename=nil
|
@@ -101,8 +103,8 @@ class Card::Migration < ActiveRecord::Migration
|
|
101
103
|
end
|
102
104
|
|
103
105
|
# Execute this migration in the named direction
|
104
|
-
# copied from ActiveRecord to wrap
|
105
|
-
def exec_migration
|
106
|
+
# copied from ActiveRecord to wrap 'up' in 'contentendly'
|
107
|
+
def exec_migration conn, direction
|
106
108
|
@connection = conn
|
107
109
|
if respond_to?(:change)
|
108
110
|
if direction == :down
|
@@ -117,7 +119,6 @@ class Card::Migration < ActiveRecord::Migration
|
|
117
119
|
@connection = nil
|
118
120
|
end
|
119
121
|
|
120
|
-
|
121
122
|
def down
|
122
123
|
raise ActiveRecord::IrreversibleMigration
|
123
124
|
end
|
data/lib/card/name.rb
CHANGED
@@ -5,61 +5,112 @@ require 'smart_name'
|
|
5
5
|
|
6
6
|
class Card
|
7
7
|
class Name < SmartName
|
8
|
+
RELATIVE_REGEXP = /\b_(left|right|whole|self|user|main|\d+|L*R?)\b/
|
8
9
|
|
9
10
|
self.params = Card::Env # yuck!
|
10
11
|
self.session = proc { Card::Auth.current.name }
|
11
12
|
self.banned_array = ['/']
|
12
13
|
|
13
14
|
def star?
|
14
|
-
simple?
|
15
|
+
simple? && '*' == s[0, 1]
|
15
16
|
end
|
16
17
|
|
17
18
|
def rstar?
|
18
|
-
right
|
19
|
+
right && '*' == right[0, 1]
|
19
20
|
end
|
20
21
|
|
21
22
|
def trait_name? *traitlist
|
22
23
|
junction? && begin
|
23
24
|
right_key = right_name.key
|
24
|
-
|
25
|
-
card_id = Card::Codename[
|
25
|
+
traitlist.find do |codename|
|
26
|
+
(card_id = Card::Codename[codename]) &&
|
27
|
+
(card = Card.quick_fetch card_id) &&
|
26
28
|
card.key == right_key
|
27
|
-
end
|
29
|
+
end.present?
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
33
|
def trait_name tag_code
|
32
|
-
card_id = Card::Codename[
|
33
|
-
|
34
|
+
(card_id = Card::Codename[tag_code]) &&
|
35
|
+
(card = Card.quick_fetch card_id) &&
|
36
|
+
[self, card.cardname].to_name
|
34
37
|
end
|
35
38
|
|
36
39
|
def trait tag_code
|
37
|
-
name = trait_name(
|
38
|
-
name ? name.s : (
|
40
|
+
name = trait_name(tag_code)
|
41
|
+
name ? name.s : (fail Card::NotFound, "unknown codename: #{tag_code}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def field tag_name
|
45
|
+
field_name(tag_name).s
|
39
46
|
end
|
40
47
|
|
41
48
|
def code
|
42
|
-
Card::Codename[
|
49
|
+
Card::Codename[Card.fetch_id self]
|
50
|
+
end
|
51
|
+
|
52
|
+
# returns full name for a field
|
53
|
+
def field_name tag_name
|
54
|
+
case tag_name
|
55
|
+
when Symbol
|
56
|
+
trait_name tag_name
|
57
|
+
else
|
58
|
+
if tag_name.to_s[0] == '+'
|
59
|
+
tag_name = tag_name.to_s[1..-1]
|
60
|
+
end
|
61
|
+
[ self, tag_name ].to_name
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def relative_field_name tag_name
|
66
|
+
field_name(tag_name).relative_name(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
def relative_name context_name
|
70
|
+
to_show(*context_name.to_name.parts).to_name
|
43
71
|
end
|
44
72
|
|
73
|
+
def absolute_name context_name
|
74
|
+
to_absolute_name(context_name)
|
75
|
+
end
|
45
76
|
|
46
|
-
def
|
77
|
+
def field_of? context_name
|
47
78
|
if context_name.present?
|
48
|
-
# Do I still equal myself after I've been relativised in the context
|
49
|
-
|
50
|
-
|
51
|
-
relative_name.key != absolute_name.key
|
79
|
+
# Do I still equal myself after I've been relativised in the context
|
80
|
+
# of context_name?
|
81
|
+
relative_name(context_name).key != absolute_name(context_name).key
|
52
82
|
else
|
53
|
-
|
83
|
+
s.match(/^\s*\+/)
|
54
84
|
end
|
55
85
|
end
|
56
86
|
|
57
|
-
def
|
58
|
-
Set::Type::Setting.member_names[
|
87
|
+
def setting?
|
88
|
+
Set::Type::Setting.member_names[key]
|
89
|
+
end
|
90
|
+
|
91
|
+
def set?
|
92
|
+
SetPattern.card_keys[tag_name.key]
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def relative?
|
97
|
+
s =~ RELATIVE_REGEXP || starts_with_joint?
|
98
|
+
end
|
99
|
+
|
100
|
+
def absolute?
|
101
|
+
!relative?
|
102
|
+
end
|
103
|
+
|
104
|
+
def stripped
|
105
|
+
s.gsub RELATIVE_REGEXP, ''
|
106
|
+
end
|
107
|
+
|
108
|
+
def starts_with_joint?
|
109
|
+
s =~ /^\+/
|
59
110
|
end
|
60
111
|
|
61
|
-
def
|
62
|
-
|
112
|
+
def to_sym
|
113
|
+
s.to_sym
|
63
114
|
end
|
64
115
|
end
|
65
116
|
end
|
data/lib/card/set.rb
CHANGED
@@ -1,86 +1,91 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
3
|
class Card
|
4
|
-
#remove_const :Set if const_defined?(:Set, false)
|
4
|
+
# remove_const :Set if const_defined?(:Set, false)
|
5
5
|
|
6
6
|
module Set
|
7
|
-
|
8
7
|
mattr_accessor :modules, :traits
|
9
8
|
@@modules = { base: [], base_format: {}, nonbase: {}, nonbase_format: {} }
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
* all
|
40
|
-
* all cards
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
for
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
10
|
+
# A 'Set' is a group of Cards to which 'Rules' may be applied.
|
11
|
+
# Sets can be as specific as a single card, as general as all cards, or
|
12
|
+
# anywhere in between.
|
13
|
+
#
|
14
|
+
# Rules take two main forms: card rules and code rules.
|
15
|
+
#
|
16
|
+
# 'Card rules' are defined in card content. These are generally configured
|
17
|
+
# via the web interface and are thus documented at http://wagn.org/rules.
|
18
|
+
#
|
19
|
+
# 'Code rules' can be defined in a 'set file' within any 'Mod' (short for
|
20
|
+
# both 'module' and 'modification'). In accordance with Wagn's 'MoVE'
|
21
|
+
# architecture, there are two main kinds of code rules you can create in
|
22
|
+
# set file: Views, and Events.
|
23
|
+
# Events are associated with the Card class, and Views are associated with
|
24
|
+
# a Format class.
|
25
|
+
# You can also use set files to add or override Card and/or Format methods
|
26
|
+
# directly. The majority of Card code is contained in these files.
|
27
|
+
#
|
28
|
+
# (FIXME - define mod, add generator)
|
29
|
+
#
|
30
|
+
# Whenever you fetch or instantiate a card, it will automatically include
|
31
|
+
# all the set modules defined in set files associated with sets of which it
|
32
|
+
# is a member. This entails both simple model methods and 'events', which
|
33
|
+
# are special methods explored in greater detail below.
|
34
|
+
#
|
35
|
+
# For example, say you have a Plaintext card named 'Philipp+address', and
|
36
|
+
# you have set files for the following sets:
|
37
|
+
#
|
38
|
+
# * all cards
|
39
|
+
# * all Plaintext cards
|
40
|
+
# * all cards ending in +address
|
41
|
+
#
|
42
|
+
# When you run this:
|
43
|
+
#
|
44
|
+
# mycard = Card.fetch 'Philipp+address'
|
45
|
+
#
|
46
|
+
# ...then mycard will include the set modules associated with each of those
|
47
|
+
# sets in the above order. (The order is determined by the set pattern;
|
48
|
+
# see lib/card/set_pattern.rb for more information about set_ptterns and
|
49
|
+
# mod/core/set/all/fetch.rb for more about fetching.)
|
50
|
+
#
|
51
|
+
# Similarly, whenever a Format object is instantiated for a card, it
|
52
|
+
# includes all views associated with BOTH (a) sets of which the card is a
|
53
|
+
# member and (b) the current format or its ancestors. More on defining
|
54
|
+
# views below.
|
55
|
+
#
|
56
|
+
#
|
57
|
+
# In order to have a set file associated with "all cards ending in
|
58
|
+
# +address", you could create a file in
|
59
|
+
# mywagn/mod/mymod/set/right/address.rb.
|
60
|
+
# The recommended mechanism for doing so is running `wagn generate set
|
61
|
+
# modname set_pattern set_anchor`. In the current example, this
|
62
|
+
# would translate to `wagn generate set mymod right address`.
|
63
|
+
# Note that both the set_pattern and the set_anchor must correspond to the
|
64
|
+
# codename of a card in the database to function correctly but you can add
|
65
|
+
# arbitrary subdirectories to organize your code rules. The rule above
|
66
|
+
# for example could be saved in
|
67
|
+
# mywagn/mod/mymod/set/right/address/america/north/canada.rb.
|
68
|
+
#
|
69
|
+
#
|
70
|
+
# When a Card application loads, it uses these files to autogenerate a
|
71
|
+
# tmp_file that uses this set file to create a Card::Set::Right::Address
|
72
|
+
# module which itself is extended with Card::Set. A set file is 'just ruby'
|
73
|
+
# but is generally quite concise because Card uses its file location to
|
74
|
+
# autogenerate ruby module names and then uses Card::Set module to provide
|
75
|
+
# additional API.
|
76
|
+
#
|
77
|
+
#
|
78
|
+
# View definitions
|
79
|
+
#
|
80
|
+
# When you declare:
|
81
|
+
# view :view_name do |args|
|
82
|
+
# #...your code here
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# Methods are defined on the format
|
86
|
+
#
|
87
|
+
# The external api with checks:
|
88
|
+
# render(:viewname, args)
|
84
89
|
|
85
90
|
module Format
|
86
91
|
mattr_accessor :views
|
@@ -89,32 +94,33 @@ class Card
|
|
89
94
|
def view view, *args, &block
|
90
95
|
view = view.to_viewname.key.to_sym
|
91
96
|
views[self] ||= {}
|
92
|
-
view_block = views[self][view] =
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
97
|
+
view_block = views[self][view] =
|
98
|
+
if block_given?
|
99
|
+
Card::Format.extract_class_vars view, args[0]
|
100
|
+
block
|
101
|
+
else
|
102
|
+
alias_block view, args
|
103
|
+
end
|
98
104
|
define_method "_view_#{ view }", view_block
|
99
105
|
end
|
100
106
|
|
101
107
|
def alias_block view, args
|
102
|
-
opts =
|
108
|
+
opts = args[0].is_a?(Hash) ? args.shift : { view: args.shift }
|
103
109
|
opts[:mod] ||= self
|
104
110
|
opts[:view] ||= view
|
105
|
-
views[
|
111
|
+
views[opts[:mod]][opts[:view]] || fail
|
106
112
|
rescue
|
107
|
-
raise "cannot find #{ opts[:view] } view in #{ opts[:mod] };
|
113
|
+
raise "cannot find #{ opts[:view] } view in #{ opts[:mod] }; " \
|
114
|
+
"failed to alias #{view} in #{self}"
|
108
115
|
end
|
109
|
-
|
110
116
|
end
|
111
117
|
|
112
|
-
|
113
118
|
def format *format_names, &block
|
114
119
|
if format_names.empty?
|
115
120
|
format_names = [:base]
|
116
121
|
elsif format_names.first == :all
|
117
|
-
format_names = Card::Format.registered
|
122
|
+
format_names = Card::Format.registered
|
123
|
+
.reject { |f| Card::Format.aliases[f] }
|
118
124
|
end
|
119
125
|
format_names.each do |f|
|
120
126
|
define_on_format f, &block
|
@@ -122,9 +128,12 @@ class Card
|
|
122
128
|
end
|
123
129
|
|
124
130
|
def define_on_format format_name=:base, &block
|
125
|
-
|
126
|
-
|
127
|
-
|
131
|
+
# format class name, eg. HtmlFormat
|
132
|
+
klass = Card::Format.format_class_name format_name
|
133
|
+
# called on current set module, eg Card::Set::Type::Pointer
|
134
|
+
mod = const_get_or_set klass do
|
135
|
+
# yielding set format module, eg Card::Set::Type::Pointer::HtmlFormat
|
136
|
+
m = Module.new
|
128
137
|
register_set_format Card.const_get(klass), m
|
129
138
|
m.extend Card::Set::Format
|
130
139
|
m
|
@@ -138,11 +147,11 @@ class Card
|
|
138
147
|
end
|
139
148
|
end
|
140
149
|
|
141
|
-
|
142
150
|
def event event, opts={}, &final
|
143
|
-
perform_later =
|
144
|
-
|
145
|
-
|
151
|
+
perform_later = (opts[:before] == :subsequent) ||
|
152
|
+
(opts[:after] == :subsequent)
|
153
|
+
final_method = "#{event}_without_callbacks" # should be private?
|
154
|
+
opts[:on] = [:create, :update] if opts[:on] == :save
|
146
155
|
|
147
156
|
Card.define_callbacks event
|
148
157
|
|
@@ -161,26 +170,53 @@ class Card
|
|
161
170
|
set_event_callbacks event, opts
|
162
171
|
end
|
163
172
|
|
173
|
+
def phase_method method, opts={}, &block
|
174
|
+
class_eval do
|
175
|
+
define_method "_#{method}", &block
|
176
|
+
define_method method do |*args|
|
177
|
+
error =
|
178
|
+
if !phase_ok? opts
|
179
|
+
if !phase
|
180
|
+
"phase method #{method} called outside of event phases"
|
181
|
+
else
|
182
|
+
"#{opts.inspect} method #{method} called in phase #{phase}"
|
183
|
+
end
|
184
|
+
elsif !on_condition_applies?(opts[:on])
|
185
|
+
"on: #{opts[:on]} method #{method} called on #{@action}"
|
186
|
+
end
|
187
|
+
if error
|
188
|
+
raise Card::Error, error
|
189
|
+
else
|
190
|
+
send "_#{method}", *args
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
164
196
|
def define_event_perform_later_method event, method_name
|
165
197
|
class_eval do
|
166
198
|
define_method method_name, proc {
|
167
|
-
s_attr =
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
199
|
+
s_attr =
|
200
|
+
serializable_attributes.each_with_object({}) do |name, hash|
|
201
|
+
value = instance_variable_get("@#{name}")
|
202
|
+
hash[name] =
|
203
|
+
# ActiveJob doesn't accept symbols as arguments
|
204
|
+
if Symbol === value
|
205
|
+
{ value: value.to_s, symbol: true }
|
206
|
+
else
|
207
|
+
{ value: value }
|
175
208
|
end
|
209
|
+
end
|
176
210
|
Object.const_get(event.to_s.camelize).perform_later(self, s_attr)
|
177
211
|
}
|
178
212
|
end
|
179
213
|
end
|
180
214
|
|
181
|
-
def define_event_method event, call_method,
|
215
|
+
def define_event_method event, call_method, _opts
|
182
216
|
class_eval do
|
183
217
|
define_method event do
|
218
|
+
# Rails.logger.rspec event
|
219
|
+
# puts "#{self.name}: #{event}"
|
184
220
|
run_callbacks event do
|
185
221
|
send call_method
|
186
222
|
end
|
@@ -188,14 +224,15 @@ class Card
|
|
188
224
|
end
|
189
225
|
end
|
190
226
|
|
191
|
-
|
192
227
|
# creates an Active Job.
|
193
|
-
# The scheduled job gets the card object as argument and all serializable
|
194
|
-
#
|
228
|
+
# The scheduled job gets the card object as argument and all serializable
|
229
|
+
# attributes of the card.
|
230
|
+
# (when the job is executed ActiveJob fetches the card from the database so
|
231
|
+
# all attributes get lost)
|
195
232
|
# @param name [String] the name for the ActiveJob child class
|
196
233
|
# @param final_method [String] the name of the card instance method to be queued
|
197
234
|
# @option queue [Symbol] (:default) the name of the queue
|
198
|
-
def define_active_job name, final_method, queue
|
235
|
+
def define_active_job name, final_method, queue=:default
|
199
236
|
class_name = name.to_s.camelize
|
200
237
|
eval %{
|
201
238
|
class ::#{class_name} < ActiveJob::Base
|
@@ -203,15 +240,15 @@ class Card
|
|
203
240
|
end
|
204
241
|
}
|
205
242
|
Object.const_get(class_name).class_eval do
|
206
|
-
|
243
|
+
define_method :perform, proc { |card, attributes|
|
207
244
|
attributes.each do |name, args|
|
208
245
|
# symbols are not allowed so all symbols arrive here as strings
|
209
246
|
# convert strings that were symbols before back to symbols
|
210
247
|
value = args[:symbol] ? args[:value].to_sym : args[:value]
|
211
|
-
card.instance_variable_set("@#{name}", value
|
248
|
+
card.instance_variable_set("@#{name}", value)
|
212
249
|
end
|
213
250
|
card.send final_method
|
214
|
-
|
251
|
+
}
|
215
252
|
end
|
216
253
|
end
|
217
254
|
|
@@ -220,47 +257,45 @@ class Card
|
|
220
257
|
#
|
221
258
|
def card_accessor *args
|
222
259
|
options = args.extract_options!
|
223
|
-
add_traits args, options.merge(
|
260
|
+
add_traits args, options.merge(reader: true, writer: true)
|
224
261
|
end
|
225
262
|
|
226
263
|
def card_reader *args
|
227
264
|
options = args.extract_options!
|
228
|
-
add_traits args, options.merge(
|
265
|
+
add_traits args, options.merge(reader: true)
|
229
266
|
end
|
230
267
|
|
231
268
|
def card_writer *args
|
232
269
|
options = args.extract_options!
|
233
|
-
add_traits args, options.merge(
|
270
|
+
add_traits args, options.merge(writer: true)
|
234
271
|
end
|
235
272
|
|
236
|
-
|
237
273
|
def ensure_set &block
|
238
274
|
begin
|
239
275
|
set_module = block.call
|
240
276
|
rescue NameError => e
|
241
277
|
if e.message.match /uninitialized constant (?:Card::Set::)?(.+)$/
|
242
|
-
$1.split('::').inject(Card::Set) do |
|
243
|
-
|
278
|
+
$1.split('::').inject(Card::Set) do |set_mod, module_name|
|
279
|
+
set_mod.const_get_or_set module_name do
|
244
280
|
Module.new
|
245
281
|
end
|
246
282
|
end
|
247
283
|
end
|
248
|
-
|
284
|
+
# try again - there might be another submodule that doesn't exist
|
285
|
+
ensure_set &block
|
249
286
|
else
|
250
287
|
set_module.extend Card::Set
|
251
288
|
end
|
252
289
|
end
|
253
|
-
|
254
|
-
|
255
290
|
# the set loading process has two main phases:
|
256
291
|
|
257
|
-
# 1. Definition: interpret each set file, creating/defining set and
|
258
|
-
#
|
259
|
-
#
|
292
|
+
# 1. Definition: interpret each set file, creating/defining set and
|
293
|
+
# set_format modules
|
294
|
+
# 2. Organization: have base classes include modules associated with the
|
295
|
+
# 'all' set, and clean up the other modules
|
260
296
|
|
261
297
|
class << self
|
262
|
-
|
263
|
-
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
298
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
264
299
|
# Definition Phase
|
265
300
|
|
266
301
|
# each set file calls `extend Card::Set` when loaded
|
@@ -268,44 +303,44 @@ class Card
|
|
268
303
|
register_set mod
|
269
304
|
end
|
270
305
|
|
271
|
-
|
272
306
|
# make the set available for use
|
273
307
|
def register_set set_module
|
274
|
-
if set_module.abstract_set?
|
275
|
-
|
276
|
-
|
308
|
+
return if set_module.abstract_set? # noop; only used by explicit
|
309
|
+
# inclusion in other set modules
|
310
|
+
if set_module.all_set?
|
277
311
|
# automatically included in Card class
|
278
|
-
modules[
|
312
|
+
modules[:base] << set_module
|
279
313
|
else
|
280
314
|
# made ready for dynamic loading via #include_set_modules
|
281
|
-
modules[
|
282
|
-
modules[
|
315
|
+
modules[:nonbase][set_module.shortname] ||= []
|
316
|
+
modules[:nonbase][set_module.shortname] << set_module
|
283
317
|
end
|
284
318
|
end
|
285
319
|
|
286
320
|
def write_tmp_file from_file, to_file, rel_path
|
287
|
-
name_parts = rel_path.gsub(/\.rb/,'').split(File::SEPARATOR)
|
321
|
+
name_parts = rel_path.gsub(/\.rb/, '').split(File::SEPARATOR)
|
288
322
|
submodules = name_parts.map { |a| "module #{a.camelize};" } * ' '
|
289
323
|
file_content = <<EOF
|
290
324
|
# -*- encoding : utf-8 -*-
|
291
|
-
class Card; module Set; #{submodules} extend Card::Set
|
325
|
+
class Card; module Set; #{submodules} extend Card::Set
|
326
|
+
# ~~~~~~~~~~~ above autogenerated; below pulled from #{from_file} ~~~~~~~~~~~
|
292
327
|
#{ File.read from_file }
|
293
328
|
|
294
329
|
# ~~~~~~~~~~~ below autogenerated; above pulled from #{from_file} ~~~~~~~~~~~
|
295
|
-
end;end;#{'end;'*name_parts.size}
|
330
|
+
end;end;#{'end;' * name_parts.size}
|
296
331
|
EOF
|
297
332
|
|
298
|
-
FileUtils.mkdir_p to_file
|
333
|
+
FileUtils.mkdir_p File.dirname(to_file)
|
299
334
|
File.write to_file, file_content
|
300
335
|
to_file
|
301
336
|
end
|
302
337
|
|
303
|
-
|
304
|
-
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
338
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
305
339
|
# Organization Phase
|
306
340
|
|
307
|
-
#
|
308
|
-
#
|
341
|
+
# 'base modules' are modules that are permanently included on the Card or
|
342
|
+
# Format class
|
343
|
+
# 'nonbase modules' are included dynamically on singleton_classes
|
309
344
|
def process_base_modules
|
310
345
|
process_base_module_list modules[:base], Card
|
311
346
|
modules[:base_format].each do |format_class, modules_list|
|
@@ -320,15 +355,15 @@ EOF
|
|
320
355
|
if mod.instance_methods.any?
|
321
356
|
klass.send :include, mod
|
322
357
|
end
|
323
|
-
if class_methods = mod.const_get_if_defined(
|
358
|
+
if (class_methods = mod.const_get_if_defined(:ClassMethods))
|
324
359
|
klass.send :extend, class_methods
|
325
360
|
end
|
326
361
|
end
|
327
362
|
end
|
328
363
|
|
329
364
|
def clean_empty_modules
|
330
|
-
clean_empty_module_from_hash modules[
|
331
|
-
modules[
|
365
|
+
clean_empty_module_from_hash modules[:nonbase]
|
366
|
+
modules[:nonbase_format].values.each do |hash|
|
332
367
|
clean_empty_module_from_hash hash
|
333
368
|
end
|
334
369
|
end
|
@@ -339,22 +374,21 @@ EOF
|
|
339
374
|
hash.delete mod_name if modlist.empty?
|
340
375
|
end
|
341
376
|
end
|
342
|
-
|
343
377
|
end
|
344
378
|
|
345
|
-
|
346
379
|
def register_set_format format_class, mod
|
347
|
-
if self.abstract_set?
|
348
|
-
|
349
|
-
|
380
|
+
return if self.abstract_set? # noop; only used by explicit inclusion in
|
381
|
+
# other set modules
|
382
|
+
|
383
|
+
if self.all_set?
|
350
384
|
# ready to include in base format classes
|
351
|
-
modules[
|
352
|
-
modules[
|
385
|
+
modules[:base_format][format_class] ||= []
|
386
|
+
modules[:base_format][format_class] << mod
|
353
387
|
else
|
354
388
|
# ready to include dynamically in set members' format singletons
|
355
|
-
format_hash = modules[
|
356
|
-
format_hash[
|
357
|
-
format_hash[
|
389
|
+
format_hash = modules[:nonbase_format][format_class] ||= {}
|
390
|
+
format_hash[shortname] ||= []
|
391
|
+
format_hash[shortname] << mod
|
358
392
|
end
|
359
393
|
end
|
360
394
|
|
@@ -377,15 +411,18 @@ EOF
|
|
377
411
|
|
378
412
|
private
|
379
413
|
|
380
|
-
|
381
414
|
def set_event_callbacks event, opts
|
382
415
|
[:before, :after, :around].each do |kind|
|
383
|
-
if object_method = opts.delete(kind)
|
416
|
+
if (object_method = opts.delete(kind))
|
384
417
|
this_set_module = self
|
385
418
|
Card.class_eval do
|
386
|
-
set_callback
|
387
|
-
|
388
|
-
|
419
|
+
set_callback(
|
420
|
+
object_method, kind, event,
|
421
|
+
prepend: true, if: proc do |c|
|
422
|
+
c.singleton_class.include?(this_set_module) &&
|
423
|
+
c.event_applies?(opts)
|
424
|
+
end
|
425
|
+
)
|
389
426
|
end
|
390
427
|
end
|
391
428
|
end
|
@@ -393,16 +430,16 @@ EOF
|
|
393
430
|
|
394
431
|
def get_traits mod
|
395
432
|
Card::Set.traits ||= {}
|
396
|
-
Card::Set.traits[mod]
|
433
|
+
Card::Set.traits[mod] || Card::Set.traits[mod] = {}
|
397
434
|
end
|
398
435
|
|
399
436
|
def add_traits args, options
|
400
437
|
mod = self
|
401
|
-
|
438
|
+
# raise "Can't define card traits on all set" if mod == Card
|
402
439
|
mod_traits = get_traits mod
|
403
440
|
|
404
|
-
new_opts = options[:type] ? {type: options[:type]} : {}
|
405
|
-
new_opts.merge!(
|
441
|
+
new_opts = options[:type] ? { type: options[:type] } : {}
|
442
|
+
new_opts.merge!(content: options[:default]) if options[:default]
|
406
443
|
|
407
444
|
args.each do |trait|
|
408
445
|
define_trait_card trait, new_opts
|
@@ -424,7 +461,7 @@ EOF
|
|
424
461
|
def define_trait_reader trait
|
425
462
|
define_method trait do
|
426
463
|
trait_var "@#{trait}" do
|
427
|
-
send(
|
464
|
+
send("#{trait}_card").content
|
428
465
|
end
|
429
466
|
end
|
430
467
|
end
|
@@ -432,8 +469,7 @@ EOF
|
|
432
469
|
def define_trait_writer trait
|
433
470
|
define_method "#{trait}=" do |value|
|
434
471
|
card = send "#{trait}_card"
|
435
|
-
|
436
|
-
self.subcards[card.name] = {type_id: card.type_id, content: value }
|
472
|
+
subcards.add name: card.name, type_id: card.type_id, content: value
|
437
473
|
instance_variable_set "@#{trait}", value
|
438
474
|
end
|
439
475
|
end
|
@@ -445,10 +481,10 @@ EOF
|
|
445
481
|
|
446
482
|
def attachment name, args
|
447
483
|
include Abstract::Attachment
|
448
|
-
set_specific_attributes name,
|
484
|
+
set_specific_attributes name, :load_from_mod, :cached_upload,
|
485
|
+
"remote_#{name}_url".to_sym
|
449
486
|
uploader_class = args[:uploader] || FileUploader
|
450
487
|
mount_uploader name, uploader_class
|
451
488
|
end
|
452
489
|
end
|
453
490
|
end
|
454
|
-
|