card 1.18.4 → 1.18.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardoc/checksums +263 -240
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/VERSION +1 -1
- data/card.gemspec +5 -2
- data/config/initializers/01_core_extensions/array.rb +9 -0
- data/config/initializers/01_core_extensions/hash.rb +57 -0
- data/config/initializers/01_core_extensions/module.rb +14 -0
- data/config/initializers/01_core_extensions/object.rb +43 -0
- data/config/initializers/02_extensions/kaminari.rb +38 -0
- data/config/initializers/core_extensions.rb +24 -0
- data/config/initializers/extensions.rb +3 -0
- data/lib/card.rb +15 -10
- data/lib/card/director_register.rb +7 -0
- data/lib/card/loader.rb +29 -39
- data/lib/card/migration.rb +14 -0
- data/lib/card/query.rb +17 -128
- data/lib/card/query/attributes.rb +2 -244
- data/lib/card/query/conjunctions.rb +36 -0
- data/lib/card/query/helpers.rb +72 -0
- data/lib/card/query/interpretation.rb +124 -0
- data/lib/card/query/relational_attributes.rb +95 -0
- data/lib/card/query/sorting.rb +52 -0
- data/lib/card/set.rb +73 -362
- data/lib/card/set/format.rb +122 -0
- data/lib/card/set/helpers.rb +100 -0
- data/lib/card/set/loader.rb +98 -0
- data/lib/card/set/trait.rb +5 -2
- data/lib/card/subcards.rb +38 -12
- data/lib/cardio.rb +1 -1
- data/lib/generators/card/migration/templates/card_migration.erb +1 -1
- data/mod/01_core/chunk/include.rb +13 -1
- data/mod/01_core/set/all/event.rb +14 -0
- data/mod/01_core/set/all/fetch.rb +21 -7
- data/mod/01_core/set/all/states.rb +3 -1
- data/mod/01_core/set/all/subcards.rb +4 -2
- data/mod/01_core/spec/set/all/fetch_spec.rb +14 -1
- data/mod/02_basic_types/set/abstract/code_file.rb +6 -0
- data/mod/04_settings/set/type/setting.rb +10 -0
- data/mod/05_standard/set/all/rich_html/wrapper.rb +1 -1
- data/mod/05_standard/set/self/codenames.rb +0 -0
- data/mod/05_standard/spec/chunk/include_spec.rb +10 -10
- data/spec/config/initializers/core_extensions_spec.rb +15 -0
- data/spec/lib/card/stage_director_spec.rb +25 -1
- metadata +25 -8
- data/config/initializers/01_init_ruby_extensions.rb +0 -3
- data/lib/card/core_ext.rb +0 -107
@@ -0,0 +1,95 @@
|
|
1
|
+
class Card
|
2
|
+
class Query
|
3
|
+
module RelationalAttributes
|
4
|
+
|
5
|
+
def type val
|
6
|
+
restrict :type_id, val
|
7
|
+
end
|
8
|
+
|
9
|
+
def part val
|
10
|
+
right_val = val.is_a?(Integer) ? val : val.clone
|
11
|
+
any(left: val, right: right_val)
|
12
|
+
end
|
13
|
+
|
14
|
+
def left val
|
15
|
+
restrict :left_id, val
|
16
|
+
end
|
17
|
+
|
18
|
+
def right val
|
19
|
+
restrict :right_id, val
|
20
|
+
end
|
21
|
+
|
22
|
+
def editor_of val
|
23
|
+
act_join = Join.new(
|
24
|
+
from: self,
|
25
|
+
to: ['card_acts', "a#{table_id true}", 'actor_id']
|
26
|
+
)
|
27
|
+
joins << act_join
|
28
|
+
action_join = Join.new(
|
29
|
+
from: act_join,
|
30
|
+
to: ['card_actions', "an#{table_id true}", 'card_act_id'],
|
31
|
+
superjoin: act_join
|
32
|
+
)
|
33
|
+
join_cards val, from: action_join, from_field: 'card_id'
|
34
|
+
end
|
35
|
+
|
36
|
+
def edited_by val
|
37
|
+
action_join = Join.new(
|
38
|
+
from: self,
|
39
|
+
to: ['card_actions', "an#{table_id true}", 'card_id']
|
40
|
+
)
|
41
|
+
joins << action_join
|
42
|
+
act_join = Join.new(
|
43
|
+
from: action_join,
|
44
|
+
from_field: 'card_act_id',
|
45
|
+
to: ['card_acts', "a#{table_id true}"]
|
46
|
+
)
|
47
|
+
join_cards val, from: act_join, from_field: 'actor_id'
|
48
|
+
end
|
49
|
+
|
50
|
+
def last_editor_of val
|
51
|
+
join_cards val, to_field: 'updater_id'
|
52
|
+
end
|
53
|
+
|
54
|
+
def last_edited_by val
|
55
|
+
restrict :updater_id, val
|
56
|
+
end
|
57
|
+
|
58
|
+
def creator_of val
|
59
|
+
join_cards val, to_field: 'creator_id'
|
60
|
+
end
|
61
|
+
|
62
|
+
def created_by val
|
63
|
+
restrict :creator_id, val
|
64
|
+
end
|
65
|
+
|
66
|
+
def member_of val
|
67
|
+
interpret right_plus: [RolesID, refer_to: val]
|
68
|
+
end
|
69
|
+
|
70
|
+
def member val
|
71
|
+
interpret referred_to_by: { left: val, right: RolesID }
|
72
|
+
end
|
73
|
+
|
74
|
+
# ~~~~~~ PLUS RELATIONAL
|
75
|
+
|
76
|
+
def left_plus val
|
77
|
+
junction val, :left, :right_id
|
78
|
+
end
|
79
|
+
|
80
|
+
def right_plus val
|
81
|
+
junction val, :right, :left_id
|
82
|
+
end
|
83
|
+
|
84
|
+
def plus val
|
85
|
+
any(left_plus: val, right_plus: val.deep_clone)
|
86
|
+
end
|
87
|
+
|
88
|
+
def junction val, side, to_field
|
89
|
+
part_clause, junction_clause = val.is_a?(Array) ? val : [val, {}]
|
90
|
+
junction_val = clause_to_hash(junction_clause).merge side => part_clause
|
91
|
+
join_cards junction_val, to_field: to_field
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Card
|
2
|
+
class Query
|
3
|
+
module Sorting
|
4
|
+
SORT_JOIN_TO_ITEM_MAP = { left: 'left_id', right: 'right_id' }.freeze
|
5
|
+
|
6
|
+
def sort val
|
7
|
+
return nil if @superquery
|
8
|
+
sort_field = val[:return] || 'db_content'
|
9
|
+
item = val.delete(:item) || 'left'
|
10
|
+
|
11
|
+
if sort_field == 'count'
|
12
|
+
sort_by_count val, item
|
13
|
+
elsif (join_field = SORT_JOIN_TO_ITEM_MAP[item.to_sym])
|
14
|
+
sq = join_cards(val, to_field: join_field,
|
15
|
+
side: 'LEFT',
|
16
|
+
conditions_on_join: true)
|
17
|
+
@mods[:sort] ||= "#{sq.table_alias}.#{sort_field}"
|
18
|
+
else
|
19
|
+
raise BadQuery, "sort item: #{item} not yet implemented"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# EXPERIMENTAL!
|
24
|
+
def sort_by_count val, item
|
25
|
+
if item == 'referred_to'
|
26
|
+
@mods[:sort] = 'coalesce(count,0)' # needed for postgres
|
27
|
+
cs = Query.new(
|
28
|
+
return: 'coalesce(count(*), 0) as count',
|
29
|
+
group: 'sort_join_field',
|
30
|
+
superquery: self
|
31
|
+
)
|
32
|
+
subselect = Query.new val.merge(return: 'id', superquery: self)
|
33
|
+
cs.add_condition "referer_id in (#{subselect.sql})"
|
34
|
+
# FIXME: - SQL generated before SQL phase
|
35
|
+
cs.joins << Join.new(
|
36
|
+
from: cs,
|
37
|
+
to: %w(card_references wr referee_id)
|
38
|
+
)
|
39
|
+
cs.mods[:sort_join_field] = "#{cs.table_alias}.id as sort_join_field"
|
40
|
+
# HACK!
|
41
|
+
|
42
|
+
joins << Join.new(
|
43
|
+
from: self,
|
44
|
+
to: [cs, 'srtbl', 'sort_join_field']
|
45
|
+
)
|
46
|
+
else
|
47
|
+
raise BadQuery, "count with item: #{item} not yet implemented"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/card/set.rb
CHANGED
@@ -1,374 +1,85 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
3
|
class Card
|
4
|
+
#
|
5
|
+
# A 'Set' is a group of Cards to which 'Rules' may be applied.
|
6
|
+
# Sets can be as specific as a single card, as general as all cards, or
|
7
|
+
# anywhere in between.
|
8
|
+
#
|
9
|
+
# Rules take two main forms: card rules and code rules.
|
10
|
+
#
|
11
|
+
# 'Card rules' are defined in card content. These are generally configured
|
12
|
+
# via the web interface and are thus documented at http://wagn.org/rules.
|
13
|
+
#
|
14
|
+
# 'Code rules' can be defined in a 'set module'. Card::Set supports
|
15
|
+
# creating and organizing these set modules. It also provides an API for
|
16
|
+
# defining special methods within set modules.
|
17
|
+
#
|
18
|
+
# Set modules follow the following naming convention:
|
19
|
+
#
|
20
|
+
# MOD/set/PATTERN/ANCHOR[/FREENAME].rb
|
21
|
+
#
|
22
|
+
# For example, suppose you created a "mod" (a Card modification) for managing
|
23
|
+
# your contacts called "contactmanager", and you wanted to write code
|
24
|
+
# that would apply to all +address cards. You could add a file here:
|
25
|
+
#
|
26
|
+
# ./contactmanager/set/right/address.rb
|
27
|
+
#
|
28
|
+
# or here:
|
29
|
+
#
|
30
|
+
# ./contactmanager/set/right/address/countries.rb
|
31
|
+
#
|
32
|
+
# Then, whenever you fetch or instantiate a +address card, the card will
|
33
|
+
# automatically include code from that set module. In fact, it will include
|
34
|
+
# all the set modules associated with sets of which it is a member.
|
35
|
+
#
|
36
|
+
# For example, say you have a Plaintext card named 'Philipp+address', and
|
37
|
+
# you have set files for the following sets:
|
38
|
+
#
|
39
|
+
# * all cards
|
40
|
+
# * all Plaintext cards
|
41
|
+
# * all cards ending in +address
|
42
|
+
#
|
43
|
+
# When you run this:
|
44
|
+
#
|
45
|
+
# mycard = Card.fetch 'Philipp+address'
|
46
|
+
#
|
47
|
+
# ...then mycard will include the set modules associated with each of those
|
48
|
+
# sets in the above order.
|
49
|
+
#
|
50
|
+
# You can quickly create a new set module running
|
51
|
+
#
|
52
|
+
# `wagn generate set MOD PATTERN ANCHOR`
|
53
|
+
#
|
54
|
+
# In the current example, this would translate to:
|
55
|
+
#
|
56
|
+
# `wagn generate set contactmanager right address`.
|
57
|
+
#
|
58
|
+
# Note that the set module's filename connects it to the set, so both
|
59
|
+
# the set_pattern and the set_anchor must correspond to the
|
60
|
+
# codename of a card in the database to function correctly.
|
61
|
+
#
|
62
|
+
# A set module is "just ruby", but is generally quite concise because
|
63
|
+
# Card (a) uses its the set module's file location to autogenerate ruby
|
64
|
+
# module names and (b) then uses Card::Set module to provide API for the
|
65
|
+
# most common set methods.
|
66
|
+
#
|
4
67
|
module Set
|
5
68
|
include Event
|
6
69
|
include Trait
|
7
|
-
mattr_accessor :modules, :traits
|
8
|
-
@@modules = { base: [], base_format: {}, nonbase: {}, nonbase_format: {},
|
9
|
-
abstract: {}, abstract_format: {} }
|
10
|
-
|
11
|
-
# A 'Set' is a group of Cards to which 'Rules' may be applied.
|
12
|
-
# Sets can be as specific as a single card, as general as all cards, or
|
13
|
-
# anywhere in between.
|
14
|
-
#
|
15
|
-
# Rules take two main forms: card rules and code rules.
|
16
|
-
#
|
17
|
-
# 'Card rules' are defined in card content. These are generally configured
|
18
|
-
# via the web interface and are thus documented at http://wagn.org/rules.
|
19
|
-
#
|
20
|
-
# 'Code rules' can be defined in a 'set file' within any 'Mod' (short for
|
21
|
-
# both 'module' and 'modification'). In accordance with Wagn's 'MoVE'
|
22
|
-
# architecture, there are two main kinds of code rules you can create in
|
23
|
-
# set file: Views, and Events.
|
24
|
-
# Events are associated with the Card class, and Views are associated with
|
25
|
-
# a Format class.
|
26
|
-
# You can also use set files to add or override Card and/or Format methods
|
27
|
-
# directly. The majority of Card code is contained in these files.
|
28
|
-
#
|
29
|
-
# (FIXME - define mod, add generator)
|
30
|
-
#
|
31
|
-
# Whenever you fetch or instantiate a card, it will automatically include
|
32
|
-
# all the set modules defined in set files associated with sets of which it
|
33
|
-
# is a member. This entails both simple model methods and 'events', which
|
34
|
-
# are special methods explored in greater detail below.
|
35
|
-
#
|
36
|
-
# For example, say you have a Plaintext card named 'Philipp+address', and
|
37
|
-
# you have set files for the following sets:
|
38
|
-
#
|
39
|
-
# * all cards
|
40
|
-
# * all Plaintext cards
|
41
|
-
# * all cards ending in +address
|
42
|
-
#
|
43
|
-
# When you run this:
|
44
|
-
#
|
45
|
-
# mycard = Card.fetch 'Philipp+address'
|
46
|
-
#
|
47
|
-
# ...then mycard will include the set modules associated with each of those
|
48
|
-
# ets in the above order. (The order is determined by the set pattern;
|
49
|
-
# ee lib/card/set_pattern.rb for more information about set_ptterns and
|
50
|
-
# od/core/set/all/fetch.rb for more about fetching.)
|
51
|
-
#
|
52
|
-
# imilarly, whenever a Format object is instantiated for a card, it
|
53
|
-
# ncludes all views associated with BOTH (a) sets of which the card is a
|
54
|
-
# ember and (b) the current format or its ancestors. More on defining
|
55
|
-
# iews below.
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# In order to have a set file associated with "all cards ending in
|
59
|
-
# +address", you could create a file in
|
60
|
-
# mywagn/mod/mymod/set/right/address.rb.
|
61
|
-
# The recommended mechanism for doing so is running `wagn generate set
|
62
|
-
# modname set_pattern set_anchor`. In the current example, this
|
63
|
-
# would translate to `wagn generate set mymod right address`.
|
64
|
-
# Note that both the set_pattern and the set_anchor must correspond to the
|
65
|
-
# codename of a card in the database to function correctly but you can add
|
66
|
-
# arbitrary subdirectories to organize your code rules. The rule above
|
67
|
-
# for example could be saved in
|
68
|
-
# mywagn/mod/mymod/set/right/address/america/north/canada.rb.
|
69
|
-
#
|
70
|
-
#
|
71
|
-
# When a Card application loads, it uses these files to autogenerate a
|
72
|
-
# tmp_file that uses this set file to create a Card::Set::Right::Address
|
73
|
-
# module which itself is extended with Card::Set. A set file is 'just ruby'
|
74
|
-
# but is generally quite concise because Card uses its file location to
|
75
|
-
# autogenerate ruby module names and then uses Card::Set module to provide
|
76
|
-
# additional API.
|
77
|
-
#
|
78
|
-
#
|
79
|
-
# View definitions
|
80
|
-
#
|
81
|
-
# When you declare:
|
82
|
-
# view :view_name do |args|
|
83
|
-
# #...your code here
|
84
|
-
# end
|
85
|
-
#
|
86
|
-
# Methods are defined on the format
|
87
|
-
#
|
88
|
-
# The external api with checks:
|
89
|
-
# render(:viewname, args)
|
90
|
-
|
91
|
-
module Format
|
92
|
-
mattr_accessor :views
|
93
|
-
@@views = {}
|
94
|
-
|
95
|
-
def view view, *args, &block
|
96
|
-
view = view.to_viewname.key.to_sym
|
97
|
-
views[self] ||= {}
|
98
|
-
view_block = views[self][view] =
|
99
|
-
if block_given?
|
100
|
-
Card::Format.extract_class_vars view, args[0]
|
101
|
-
block
|
102
|
-
else
|
103
|
-
alias_block view, args
|
104
|
-
end
|
105
|
-
define_method "_view_#{view}", view_block
|
106
|
-
end
|
107
|
-
|
108
|
-
def alias_block view, args
|
109
|
-
opts = args[0].is_a?(Hash) ? args.shift : { view: args.shift }
|
110
|
-
opts[:mod] ||= self
|
111
|
-
opts[:view] ||= view
|
112
|
-
views[opts[:mod]][opts[:view]] || raise
|
113
|
-
rescue
|
114
|
-
raise "cannot find #{opts[:view]} view in #{opts[:mod]}; " \
|
115
|
-
"failed to alias #{view} in #{self}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def format *format_names, &block
|
120
|
-
if format_names.empty?
|
121
|
-
format_names = [:base]
|
122
|
-
elsif format_names.first == :all
|
123
|
-
format_names =
|
124
|
-
Card::Format.registered.reject { |f| Card::Format.aliases[f] }
|
125
|
-
end
|
126
|
-
format_names.each do |f|
|
127
|
-
define_on_format f, &block
|
128
|
-
end
|
129
|
-
end
|
130
70
|
|
131
|
-
|
132
|
-
|
133
|
-
klass = Card::Format.format_class_name format_name
|
71
|
+
include Set::Format
|
72
|
+
include Set::Helpers
|
134
73
|
|
135
|
-
|
136
|
-
mod = const_get_or_set klass do
|
137
|
-
# yielding set format module, eg Card::Set::Type::Pointer::HtmlFormat
|
138
|
-
m = Module.new
|
139
|
-
register_set_format Card.const_get(klass), m
|
140
|
-
m.extend Card::Set::Format
|
141
|
-
m
|
142
|
-
end
|
143
|
-
mod.class_eval &block
|
144
|
-
end
|
74
|
+
extend Set::Loader
|
145
75
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def stage_method method, opts={}, &block
|
153
|
-
class_eval do
|
154
|
-
define_method "_#{method}", &block
|
155
|
-
define_method method do |*args|
|
156
|
-
error =
|
157
|
-
if !director.stage_ok? opts
|
158
|
-
if !stage
|
159
|
-
"phase method #{method} called outside of event phases"
|
160
|
-
else
|
161
|
-
"#{opts.inspect} method #{method} called in phase #{stage}"
|
162
|
-
end
|
163
|
-
elsif !on_condition_applies?(opts[:on])
|
164
|
-
"on: #{opts[:on]} method #{method} called on #{@action}"
|
165
|
-
end
|
166
|
-
if error
|
167
|
-
raise Card::Error, error
|
168
|
-
else
|
169
|
-
send "_#{method}", *args
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
# include a set module and all its format modules
|
176
|
-
# @param [Module] set
|
177
|
-
# @param [Hash] opts choose the formats you want to include
|
178
|
-
# @option opts [Symbol, Array<Symbol>] :only include only these formats
|
179
|
-
# @option opts [Symbol, Array<Symbol>] :except don't include these formats
|
180
|
-
# @example
|
181
|
-
# include_set Type::Basic, except: :css
|
182
|
-
def include_set set, opts={}
|
183
|
-
set_type = set.abstract_set? ? :abstract : :nonbase
|
184
|
-
@@modules[set_type][set.shortname].each do |set_mod|
|
185
|
-
include set_mod
|
186
|
-
end
|
187
|
-
include_set_formats set, opts
|
188
|
-
end
|
189
|
-
|
190
|
-
def each_format set
|
191
|
-
set_type = set.abstract_set? ? :abstract : :nonbase
|
192
|
-
format_type = "#{set_type}_format".to_sym
|
193
|
-
@@modules[format_type].each_pair do |format, set_format_mod_hash|
|
194
|
-
next unless (format_mods = set_format_mod_hash[set.shortname])
|
195
|
-
yield format, format_mods
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
# include a format modules of a set
|
200
|
-
# @param [Module] set
|
201
|
-
# @param [Hash] opts choose the formats you want to include
|
202
|
-
# @option opts [Symbol, Array<Symbol>] :only include only these formats
|
203
|
-
# @option opts [Symbol, Array<Symbol>] :except don't include these formats
|
204
|
-
# @example
|
205
|
-
# include_set Type::Basic, except: :css
|
206
|
-
def include_set_formats set, opts={}
|
207
|
-
each_format set do |format, format_mods|
|
208
|
-
match = format.to_s.match(/::(?<format>[^:]+)Format/)
|
209
|
-
format_sym = match ? match[:format] : :base
|
210
|
-
next if opts[:except] && Array(opts[:except]).include?(format_sym)
|
211
|
-
next if opts[:only] && !Array(opts[:only]).include?(format_sym)
|
212
|
-
format_mods.each do |format_mod|
|
213
|
-
define_on_format format_sym do
|
214
|
-
include format_mod
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def ensure_set &block
|
221
|
-
set_module = yield
|
222
|
-
rescue NameError => e
|
223
|
-
if e.message =~ /uninitialized constant (?:Card::Set::)?(.+)$/
|
224
|
-
Regexp.last_match(1).split('::').inject(Card::Set) do |set_mod, module_name|
|
225
|
-
set_mod.const_get_or_set module_name do
|
226
|
-
Module.new
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
# try again - there might be another submodule that doesn't exist
|
231
|
-
ensure_set &block
|
232
|
-
else
|
233
|
-
set_module.extend Card::Set
|
234
|
-
end
|
235
|
-
# the set loading process has two main phases:
|
236
|
-
|
237
|
-
# 1. Definition: interpret each set file, creating/defining set and
|
238
|
-
# set_format modules
|
239
|
-
# 2. Organization: have base classes include modules associated with the
|
240
|
-
# 'all' set, and clean up the other modules
|
241
|
-
|
242
|
-
class << self
|
243
|
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
244
|
-
# Definition Phase
|
245
|
-
|
246
|
-
# each set file calls `extend Card::Set` when loaded
|
247
|
-
def extended mod
|
248
|
-
register_set mod
|
249
|
-
end
|
250
|
-
|
251
|
-
# make the set available for use
|
252
|
-
def register_set set_module
|
253
|
-
if set_module.all_set?
|
254
|
-
# automatically included in Card class
|
255
|
-
modules[:base] << set_module
|
256
|
-
else
|
257
|
-
set_type = set_module.abstract_set? ? :abstract : :nonbase
|
258
|
-
# made ready for dynamic loading via #include_set_modules
|
259
|
-
modules[set_type][set_module.shortname] ||= []
|
260
|
-
modules[set_type][set_module.shortname] << set_module
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
def write_tmp_file from_file, to_file, rel_path
|
265
|
-
name_parts = rel_path.gsub(/\.rb/, '').split(File::SEPARATOR)
|
266
|
-
submodules = name_parts.map { |a| "module #{a.camelize};" } * ' '
|
267
|
-
file_content = <<EOF
|
268
|
-
# -*- encoding : utf-8 -*-
|
269
|
-
class Card; module Set; #{submodules} extend Card::Set
|
270
|
-
# ~~~~~~~~~~~ above autogenerated; below pulled from #{from_file} ~~~~~~~~~~~
|
271
|
-
#{File.read from_file}
|
272
|
-
|
273
|
-
# ~~~~~~~~~~~ below autogenerated; above pulled from #{from_file} ~~~~~~~~~~~
|
274
|
-
end;end;#{'end;' * name_parts.size}
|
275
|
-
EOF
|
276
|
-
|
277
|
-
FileUtils.mkdir_p File.dirname(to_file)
|
278
|
-
File.write to_file, file_content
|
279
|
-
to_file
|
280
|
-
end
|
281
|
-
|
282
|
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
283
|
-
# Organization Phase
|
284
|
-
|
285
|
-
# 'base modules' are modules that are permanently included on the Card or
|
286
|
-
# Format class
|
287
|
-
# 'nonbase modules' are included dynamically on singleton_classes
|
288
|
-
def process_base_modules
|
289
|
-
process_base_module_list modules[:base], Card
|
290
|
-
modules[:base_format].each do |format_class, modules_list|
|
291
|
-
process_base_module_list modules_list, format_class
|
292
|
-
end
|
293
|
-
modules.delete :base
|
294
|
-
modules.delete :base_format
|
295
|
-
end
|
296
|
-
|
297
|
-
def process_base_module_list list, klass
|
298
|
-
list.each do |mod|
|
299
|
-
klass.send :include, mod if mod.instance_methods.any?
|
300
|
-
if (class_methods = mod.const_get_if_defined(:ClassMethods))
|
301
|
-
klass.send :extend, class_methods
|
302
|
-
end
|
303
|
-
end
|
304
|
-
end
|
305
|
-
|
306
|
-
def clean_empty_modules
|
307
|
-
clean_empty_module_from_hash modules[:nonbase]
|
308
|
-
modules[:nonbase_format].values.each do |hash|
|
309
|
-
clean_empty_module_from_hash hash
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
def clean_empty_module_from_hash hash
|
314
|
-
hash.each do |mod_name, modlist|
|
315
|
-
modlist.delete_if { |x| x.instance_methods.empty? }
|
316
|
-
hash.delete mod_name if modlist.empty?
|
317
|
-
end
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
def register_set_format format_class, mod
|
322
|
-
if all_set?
|
323
|
-
# ready to include in base format classes
|
324
|
-
modules[:base_format][format_class] ||= []
|
325
|
-
modules[:base_format][format_class] << mod
|
326
|
-
else
|
327
|
-
format_type = abstract_set? ? :abstract_format : :nonbase_format
|
328
|
-
# ready to include dynamically in set members' format singletons
|
329
|
-
format_hash = modules[format_type][format_class] ||= {}
|
330
|
-
format_hash[shortname] ||= []
|
331
|
-
format_hash[shortname] << mod
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
def shortname
|
336
|
-
parts = name.split '::'
|
337
|
-
first = 2 # shortname eliminates Card::Set
|
338
|
-
pattern_name = parts[first].underscore
|
339
|
-
last = if pattern_name == 'abstract'
|
340
|
-
first + 1
|
341
|
-
else
|
342
|
-
set_class = Card::SetPattern.find pattern_name
|
343
|
-
first + set_class.anchor_parts_count
|
344
|
-
end
|
345
|
-
parts[first..last].join '::'
|
346
|
-
end
|
347
|
-
|
348
|
-
def abstract_set?
|
349
|
-
name =~ /^Card::Set::Abstract::/
|
350
|
-
end
|
351
|
-
|
352
|
-
def all_set?
|
353
|
-
name =~ /^Card::Set::All::/
|
354
|
-
end
|
355
|
-
|
356
|
-
private
|
357
|
-
|
358
|
-
def set_specific_attributes *args
|
359
|
-
Card.set_specific_attributes ||= []
|
360
|
-
Card.set_specific_attributes += args.map(&:to_s)
|
361
|
-
end
|
76
|
+
mattr_accessor :modules, :traits
|
77
|
+
self.modules = { base: [], base_format: {}, nonbase: {}, nonbase_format: {},
|
78
|
+
abstract: {}, abstract_format: {} }
|
362
79
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
:action_id_of_cached_upload,
|
368
|
-
:empty_ok,
|
369
|
-
"remote_#{name}_url".to_sym
|
370
|
-
uploader_class = args[:uploader] || FileUploader
|
371
|
-
mount_uploader name, uploader_class
|
372
|
-
end
|
80
|
+
# SET MODULE API
|
81
|
+
#
|
82
|
+
# The most important parts of the set module API are views (see
|
83
|
+
# Card::Set::Format) and events (see Card::Set::Event)
|
373
84
|
end
|
374
85
|
end
|