card 1.18.4 → 1.18.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +263 -240
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/VERSION +1 -1
  6. data/card.gemspec +5 -2
  7. data/config/initializers/01_core_extensions/array.rb +9 -0
  8. data/config/initializers/01_core_extensions/hash.rb +57 -0
  9. data/config/initializers/01_core_extensions/module.rb +14 -0
  10. data/config/initializers/01_core_extensions/object.rb +43 -0
  11. data/config/initializers/02_extensions/kaminari.rb +38 -0
  12. data/config/initializers/core_extensions.rb +24 -0
  13. data/config/initializers/extensions.rb +3 -0
  14. data/lib/card.rb +15 -10
  15. data/lib/card/director_register.rb +7 -0
  16. data/lib/card/loader.rb +29 -39
  17. data/lib/card/migration.rb +14 -0
  18. data/lib/card/query.rb +17 -128
  19. data/lib/card/query/attributes.rb +2 -244
  20. data/lib/card/query/conjunctions.rb +36 -0
  21. data/lib/card/query/helpers.rb +72 -0
  22. data/lib/card/query/interpretation.rb +124 -0
  23. data/lib/card/query/relational_attributes.rb +95 -0
  24. data/lib/card/query/sorting.rb +52 -0
  25. data/lib/card/set.rb +73 -362
  26. data/lib/card/set/format.rb +122 -0
  27. data/lib/card/set/helpers.rb +100 -0
  28. data/lib/card/set/loader.rb +98 -0
  29. data/lib/card/set/trait.rb +5 -2
  30. data/lib/card/subcards.rb +38 -12
  31. data/lib/cardio.rb +1 -1
  32. data/lib/generators/card/migration/templates/card_migration.erb +1 -1
  33. data/mod/01_core/chunk/include.rb +13 -1
  34. data/mod/01_core/set/all/event.rb +14 -0
  35. data/mod/01_core/set/all/fetch.rb +21 -7
  36. data/mod/01_core/set/all/states.rb +3 -1
  37. data/mod/01_core/set/all/subcards.rb +4 -2
  38. data/mod/01_core/spec/set/all/fetch_spec.rb +14 -1
  39. data/mod/02_basic_types/set/abstract/code_file.rb +6 -0
  40. data/mod/04_settings/set/type/setting.rb +10 -0
  41. data/mod/05_standard/set/all/rich_html/wrapper.rb +1 -1
  42. data/mod/05_standard/set/self/codenames.rb +0 -0
  43. data/mod/05_standard/spec/chunk/include_spec.rb +10 -10
  44. data/spec/config/initializers/core_extensions_spec.rb +15 -0
  45. data/spec/lib/card/stage_director_spec.rb +25 -1
  46. metadata +25 -8
  47. data/config/initializers/01_init_ruby_extensions.rb +0 -3
  48. data/lib/card/core_ext.rb +0 -107
@@ -0,0 +1,122 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Card
4
+ module Set
5
+ # Whenever a Format object is instantiated for a card, it
6
+ # includes all views associated with BOTH (a) sets of which the card is a
7
+ # member and (b) the current format or its ancestors. More on defining
8
+ # views below.
9
+ #
10
+ # View definitions
11
+ #
12
+ # When you declare:
13
+ # view :view_name do |args|
14
+ # #...your code here
15
+ # end
16
+ #
17
+ # Methods are defined on the format
18
+ #
19
+ # The external api with checks:
20
+ # render(:viewname, args)
21
+ #
22
+ module Format
23
+ def format *format_names, &block
24
+ if format_names.empty?
25
+ format_names = [:base]
26
+ elsif format_names.first == :all
27
+ format_names =
28
+ Card::Format.registered.reject { |f| Card::Format.aliases[f] }
29
+ end
30
+ format_names.each do |f|
31
+ define_on_format f, &block
32
+ end
33
+ end
34
+
35
+ def view *args, &block
36
+ format do
37
+ view(*args, &block)
38
+ end
39
+ end
40
+
41
+ def define_on_format format_name=:base, &block
42
+ # format class name, eg. HtmlFormat
43
+ klass = Card::Format.format_class_name format_name
44
+
45
+ # called on current set module, eg Card::Set::Type::Pointer
46
+ mod = const_get_or_set klass do
47
+ # yielding set format module, eg Card::Set::Type::Pointer::HtmlFormat
48
+ m = Module.new
49
+ register_set_format Card.const_get(klass), m
50
+ m.extend Card::Set::AbstractFormat
51
+ m
52
+ end
53
+ mod.class_eval(&block)
54
+ end
55
+
56
+ def register_set_format format_class, mod
57
+ if all_set?
58
+ all_set_format_mod! format_class, mod
59
+ else
60
+ format_type = abstract_set? ? :abstract_format : :nonbase_format
61
+ # ready to include dynamically in set members' format singletons
62
+ format_hash = modules[format_type][format_class] ||= {}
63
+ format_hash[shortname] ||= []
64
+ format_hash[shortname] << mod
65
+ end
66
+ end
67
+
68
+ # make mod ready to include in base (non-set-specific) format classes
69
+ def all_set_format_mod! format_class, mod
70
+ modules[:base_format][format_class] ||= []
71
+ modules[:base_format][format_class] << mod
72
+ end
73
+
74
+ # iterate through each format associated with a set
75
+ def each_format set
76
+ set_type = set.abstract_set? ? :abstract : :nonbase
77
+ format_type = "#{set_type}_format".to_sym
78
+ modules[format_type].each_pair do |format, set_format_mod_hash|
79
+ next unless (format_mods = set_format_mod_hash[set.shortname])
80
+ yield format, format_mods
81
+ end
82
+ end
83
+
84
+ def applicable_format? format, except, only
85
+ return false if except && Array(except).include?(format)
86
+ return false if only && !Array(only).include?(format)
87
+ true
88
+ end
89
+
90
+ # All Format modules are extended with this module in order to support
91
+ # the basic format API (ok, view definitions. It's just view
92
+ # definitions.)
93
+ module AbstractFormat
94
+ mattr_accessor :views
95
+ self.views = {}
96
+
97
+ def view view, *args, &block
98
+ view = view.to_viewname.key.to_sym
99
+ views[self] ||= {}
100
+ view_block = views[self][view] =
101
+ if block_given?
102
+ Card::Format.extract_class_vars view, args[0]
103
+ block
104
+ else
105
+ lookup_alias_block view, args
106
+ end
107
+ define_method "_view_#{view}", view_block
108
+ end
109
+
110
+ def lookup_alias_block view, args
111
+ opts = args[0].is_a?(Hash) ? args.shift : { view: args.shift }
112
+ opts[:mod] ||= self
113
+ opts[:view] ||= view
114
+ views[opts[:mod]][opts[:view]] || begin
115
+ raise "cannot find #{opts[:view]} view in #{opts[:mod]}; " \
116
+ "failed to alias #{view} in #{self}"
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,100 @@
1
+ class Card
2
+ module Set
3
+ # advanced set module API
4
+ module Helpers
5
+ # include a set module and all its format modules
6
+ # @param [Module] set
7
+ # @param [Hash] opts choose the formats you want to include
8
+ # @option opts [Symbol, Array<Symbol>] :only include only these formats
9
+ # @option opts [Symbol, Array<Symbol>] :except don't include these formats
10
+ # @example
11
+ # include_set Type::Basic, except: :css
12
+ def include_set set, opts={}
13
+ set_type = set.abstract_set? ? :abstract : :nonbase
14
+ Card::Set.modules[set_type][set.shortname].each do |set_mod|
15
+ include set_mod
16
+ end
17
+ include_set_formats set, opts
18
+ end
19
+
20
+ # include a format modules of a set
21
+ # @param [Module] set
22
+ # @param [Hash] opts choose the formats you want to include
23
+ # @option opts [Symbol, Array<Symbol>] :only include only these formats
24
+ # @option opts [Symbol, Array<Symbol>] :except don't include these formats
25
+ # @example
26
+ # include_set Type::Basic, except: :css
27
+ def include_set_formats set, opts={}
28
+ each_format set do |format, format_mods|
29
+ match = format.to_s.match(/::(?<format>[^:]+)Format/)
30
+ format_sym = match ? match[:format] : :base
31
+ next unless applicable_format?(format_sym, opts[:except], opts[:only])
32
+ format_mods.each do |format_mod|
33
+ define_on_format format_sym do
34
+ include format_mod
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def ensure_set &block
41
+ set_module = yield
42
+ rescue NameError => e
43
+ if e.message =~ /uninitialized constant (?:Card::Set::)?(.+)$/
44
+ constant_pieces = Regexp.last_match(1).split('::')
45
+ constant_pieces.inject(Card::Set) do |set_mod, module_name|
46
+ set_mod.const_get_or_set module_name do
47
+ Module.new
48
+ end
49
+ end
50
+ end
51
+ # try again - there might be another submodule that doesn't exist
52
+ ensure_set(&block)
53
+ else
54
+ set_module.extend Card::Set
55
+ end
56
+
57
+ def attachment name, args
58
+ include Abstract::Attachment
59
+ add_attributes name, "remote_#{name}_url".to_sym, :load_from_mod,
60
+ :action_id_of_cached_upload, :empty_ok
61
+ uploader_class = args[:uploader] || FileUploader
62
+ mount_uploader name, uploader_class
63
+ end
64
+
65
+ def stage_method method, opts={}, &block
66
+ class_eval do
67
+ define_method "_#{method}", &block
68
+ define_method method do |*args|
69
+ if (error = wrong_stage(opts) || wrong_action(opts[:on]))
70
+ raise Card::Error, error
71
+ else
72
+ send "_#{method}", *args
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def shortname
79
+ parts = name.split '::'
80
+ first = 2 # shortname eliminates Card::Set
81
+ pattern_name = parts[first].underscore
82
+ last = if pattern_name == 'abstract'
83
+ first + 1
84
+ else
85
+ set_class = Card::SetPattern.find pattern_name
86
+ first + set_class.anchor_parts_count
87
+ end
88
+ parts[first..last].join '::'
89
+ end
90
+
91
+ def abstract_set?
92
+ name =~ /^Card::Set::Abstract::/
93
+ end
94
+
95
+ def all_set?
96
+ name =~ /^Card::Set::All::/
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,98 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Card
4
+ module Set
5
+ # the set loading process has two main phases:
6
+
7
+ # 1. Definition: interpret each set file, creating/defining set and
8
+ # set_format modules
9
+ # 2. Organization: have base classes include modules associated with the
10
+ # 'all' set, and clean up the other modules
11
+ module Loader
12
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13
+ # Definition Phase
14
+
15
+ # each set file calls `extend Card::Set` when loaded
16
+ def extended mod
17
+ register_set mod
18
+ end
19
+
20
+ # make the set available for use
21
+ def register_set set_module
22
+ if set_module.all_set?
23
+ # automatically included in Card class
24
+ modules[:base] << set_module
25
+ else
26
+ set_type = set_module.abstract_set? ? :abstract : :nonbase
27
+ # made ready for dynamic loading via #include_set_modules
28
+ modules[set_type][set_module.shortname] ||= []
29
+ modules[set_type][set_module.shortname] << set_module
30
+ end
31
+ end
32
+
33
+ #
34
+ # When a Card application loads, it uses set modules to autogenerate
35
+ # tmp files that add module names (Card::Set::PATTERN::ANCHOR) and
36
+ # extend the module with Card::Set.
37
+ #
38
+
39
+ #
40
+ #
41
+ def write_tmp_file from_file, to_file, rel_path
42
+ name_parts = rel_path.gsub(/\.rb/, '').split(File::SEPARATOR)
43
+ submodules = name_parts.map { |a| "module #{a.camelize};" } * ' '
44
+ file_content = <<EOF
45
+ # -*- encoding : utf-8 -*-
46
+ class Card; module Set; #{submodules} extend Card::Set
47
+ # ~~~~~~~~~~~ above autogenerated; below pulled from #{from_file} ~~~~~~~~~~~
48
+ #{File.read from_file}
49
+
50
+ # ~~~~~~~~~~~ below autogenerated; above pulled from #{from_file} ~~~~~~~~~~~
51
+ end;end;#{'end;' * name_parts.size}
52
+ EOF
53
+
54
+ FileUtils.mkdir_p File.dirname(to_file)
55
+ File.write to_file, file_content
56
+ to_file
57
+ end
58
+
59
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60
+ # Organization Phase
61
+
62
+ # 'base modules' are modules that are permanently included on the Card or
63
+ # Format class
64
+ # 'nonbase modules' are included dynamically on singleton_classes
65
+ def process_base_modules
66
+ process_base_module_list modules[:base], Card
67
+ modules[:base_format].each do |format_class, modules_list|
68
+ process_base_module_list modules_list, format_class
69
+ end
70
+ modules.delete :base
71
+ modules.delete :base_format
72
+ end
73
+
74
+ def process_base_module_list list, klass
75
+ list.each do |mod|
76
+ klass.send :include, mod if mod.instance_methods.any?
77
+ if (class_methods = mod.const_get_if_defined(:ClassMethods))
78
+ klass.send :extend, class_methods
79
+ end
80
+ end
81
+ end
82
+
83
+ def clean_empty_modules
84
+ clean_empty_module_from_hash modules[:nonbase]
85
+ modules[:nonbase_format].values.each do |hash|
86
+ clean_empty_module_from_hash hash
87
+ end
88
+ end
89
+
90
+ def clean_empty_module_from_hash hash
91
+ hash.each do |mod_name, modlist|
92
+ modlist.delete_if { |x| x.instance_methods.empty? }
93
+ hash.delete mod_name if modlist.empty?
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -1,6 +1,5 @@
1
1
  class Card
2
2
  module Set
3
- #
4
3
  # ActiveCard support: accessing plus cards as attributes
5
4
  module Trait
6
5
  def card_accessor *args
@@ -20,6 +19,11 @@ class Card
20
19
 
21
20
  private
22
21
 
22
+ def add_attributes *args
23
+ Card.set_specific_attributes ||= []
24
+ Card.set_specific_attributes += args.map(&:to_s)
25
+ end
26
+
23
27
  def get_traits mod
24
28
  Card::Set.traits ||= {}
25
29
  Card::Set.traits[mod] || Card::Set.traits[mod] = {}
@@ -27,7 +31,6 @@ class Card
27
31
 
28
32
  def add_traits args, options
29
33
  mod = self
30
- # raise "Can't define card traits on all set" if mod == Card
31
34
  mod_traits = get_traits mod
32
35
 
33
36
  new_opts = options[:type] ? { type: options[:type] } : {}
@@ -45,22 +45,14 @@ class Card
45
45
  end
46
46
 
47
47
  def remove name_or_card
48
- key = case name_or_card
49
- when Card
50
- name_or_card.key
51
- when Symbol
52
- fetch_subcard(name_or_card).key
53
- else
54
- name_or_card.to_name.key
55
- end
56
-
57
- key = absolutize_subcard_name(key).key unless @keys.include?(key)
48
+ key = subcard_key name_or_card
49
+ return unless @keys.include? key
58
50
  @keys.delete key
59
51
  removed_card = fetch_subcard key
60
52
  if removed_card.current_action
61
53
  removed_card.current_action.delete
62
54
  end
63
- Card::DirectorRegister.delete removed_card.director
55
+ Card::DirectorRegister.deep_delete removed_card.director
64
56
  Card.cache.soft.delete key
65
57
  removed_card
66
58
  end
@@ -188,6 +180,19 @@ class Card
188
180
 
189
181
  private
190
182
 
183
+ def subcard_key name_or_card
184
+ key = case name_or_card
185
+ when Card
186
+ name_or_card.key
187
+ when Symbol
188
+ fetch_subcard(name_or_card).key
189
+ else
190
+ name_or_card.to_name.key
191
+ end
192
+ key = absolutize_subcard_name(key).key unless @keys.include?(key)
193
+ key
194
+ end
195
+
191
196
  def fetch_subcard key
192
197
  Card.fetch key, local_only: true, new: {}
193
198
  end
@@ -216,6 +221,23 @@ class Card
216
221
  end
217
222
  end
218
223
 
224
+ # TODO: this method already exists as card instance method in
225
+ # tracked_attributes.rb. Find a place for it where its accessible
226
+ # for both. There is one important difference. The keys are symbols
227
+ # here instead of strings
228
+ def extract_subcard_args! args
229
+ subcards = args.delete(:subcards) || {}
230
+ if (subfields = args.delete(:subfields))
231
+ subfields.each_pair do |key, value|
232
+ subcards[cardname.field(key)] = value
233
+ end
234
+ end
235
+ args.keys.each do |key|
236
+ subcards[key] = args.delete(key) if key =~ /^\+/
237
+ end
238
+ subcards
239
+ end
240
+
219
241
  def new_by_attributes name, attributes={}
220
242
  absolute_name = absolutize_subcard_name name
221
243
  if absolute_name.field_of?(@context_card.name) &&
@@ -224,9 +246,13 @@ class Card
224
246
  new_by_card left_card
225
247
  left_card.new_by_attributes absolute_name, attributes
226
248
  else
249
+
250
+ subcard_args = extract_subcard_args! attributes
227
251
  card = Card.assign_or_initialize_by absolute_name.s, attributes,
228
252
  local_only: true
229
- new_by_card card
253
+ subcard = new_by_card card
254
+ card.subcards.add subcard_args
255
+ subcard
230
256
  end
231
257
  end
232
258
 
@@ -96,7 +96,7 @@ module Cardio
96
96
  end
97
97
 
98
98
  def set_mod_paths
99
- each_mod_path { |mod_path| add_initializers mod_path }
99
+ each_mod_path { |mod_path| add_initializers File.join(mod_path, '*') }
100
100
  end
101
101
 
102
102
  def add_initializers dir
@@ -3,7 +3,7 @@
3
3
  class <%= migration_class_name %> < <%= @migration_parent_class %>
4
4
  def up
5
5
  <% if @migration_action == 'import' -%>
6
- import_json '<%= @json_filename %>'
6
+ import_cards '<%= @json_filename %>'
7
7
  <% end -%>
8
8
  end
9
9
  end