card 1.101.0 → 1.101.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/config/locales/en.yml +9 -6
  4. data/db/migrate_core_cards/data/decko_logo.svg +1 -59
  5. data/lib/card.rb +3 -0
  6. data/lib/card/content/diff/result.rb +40 -29
  7. data/lib/card/env.rb +2 -2
  8. data/lib/card/model/save_helper.rb +16 -182
  9. data/lib/card/model/save_helper/save_arguments.rb +94 -0
  10. data/lib/card/model/save_helper/save_helper_helper.rb +93 -0
  11. data/lib/card/name/all.rb +125 -0
  12. data/lib/card/name/all/class_methods.rb +28 -0
  13. data/lib/card/name/all/descendants.rb +46 -0
  14. data/lib/card/name/all/parts.rb +67 -0
  15. data/lib/card/query.rb +7 -2
  16. data/lib/card/query/card_query/interpretation.rb +2 -2
  17. data/lib/card/query/card_query/sorting.rb +12 -4
  18. data/lib/card/query/sql_statement.rb +1 -1
  19. data/lib/card/set/advanced_api.rb +8 -5
  20. data/lib/card/set/event/options.rb +13 -5
  21. data/lib/card/set/format.rb +16 -9
  22. data/lib/card/set/trait.rb +11 -8
  23. data/lib/card/subcards/add.rb +3 -24
  24. data/lib/card/subcards/args.rb +42 -0
  25. data/lib/card/tasks/card/file_card_creator/output_helper.rb +15 -10
  26. data/lib/card/view/options.rb +2 -1
  27. data/lib/card/view/permission.rb +14 -3
  28. data/lib/cardio.rb +9 -66
  29. data/lib/cardio/defaults.yml +70 -0
  30. data/lib/cardio/migration.rb +1 -1
  31. data/mod/core/set/all/assign_attributes.rb +8 -21
  32. data/mod/core/set/all/initialize.rb +9 -9
  33. data/mod/core/set/all/name_events.rb +3 -1
  34. data/mod/core/set/all/references.rb +2 -2
  35. data/mod/{settings → core}/set/right/autoname.rb +0 -0
  36. data/mod/{settings → core}/set/self/autoname.rb +0 -0
  37. data/mod/core/set/type/cardtype.rb +28 -0
  38. data/mod/{standard → core}/spec/set/type/cardtype_spec.rb +3 -24
  39. data/mod/standard/file/logo/image-original.svg +1 -59
  40. metadata +16 -27
  41. data/config/initializers/uuid_state_file.rb +0 -3
  42. data/mod/Modfile +0 -4
  43. data/mod/core/set/all/name.rb +0 -229
  44. data/mod/core/spec/set/all/name_spec.rb +0 -11
  45. data/mod/standard/set/all/rich_html/html_views/info.rb +0 -84
  46. data/mod/standard/set/type/cardtype.rb +0 -119
data/lib/card/query.rb CHANGED
@@ -37,12 +37,13 @@ class Card
37
37
  require "card/query/clause"
38
38
  require "card/query/card_query"
39
39
  require "card/query/sql_statement"
40
+
40
41
  # Card::Query::CardQuery
41
- # After conversion, ATTRIBUTES is a Hash where the key is the attribute
42
+ # After conversion, @attributes is a Hash where the key is the attribute
42
43
  # and the value is the attribute type:
43
44
  # { id: :basic, name: :basic, key: :basic ...}
44
45
  # This is used for rapid attribute type lookups in the interpretation phase.
45
- ATTRIBUTES = {
46
+ @attributes = {
46
47
  # Each of the "basic" fields corresponds directly to a database field.
47
48
  # their values are translated fairly directly into SQL-safe values.
48
49
  # (These are referred to as "properties" in CQL documentation. Need to
@@ -87,6 +88,8 @@ class Card
87
88
  DEFAULT_ORDER_DIRS = { update: "desc", relevance: "desc" }.freeze
88
89
 
89
90
  class << self
91
+ attr_accessor :attributes
92
+
90
93
  def new statement, comment=nil
91
94
  Query::CardQuery.new statement, comment
92
95
  end
@@ -106,5 +109,7 @@ class Card
106
109
  txt
107
110
  end
108
111
  end
112
+
113
+ delegate :attributes, to: :class
109
114
  end
110
115
  end
@@ -29,7 +29,7 @@ class Card
29
29
  def interpret_as_content? key
30
30
  # eg "match" is both operator and attribute;
31
31
  # interpret as attribute when "match" is key
32
- OPERATORS.key?(key.to_s) && !ATTRIBUTES[key]
32
+ OPERATORS.key?(key.to_s) && !Query.attributes[key]
33
33
  end
34
34
 
35
35
  def interpret_as_modifier? key, val
@@ -43,7 +43,7 @@ class Card
43
43
  end
44
44
 
45
45
  def interpret_attributes attribute, val
46
- attribute_type = ATTRIBUTES[attribute]
46
+ attribute_type = Query.attributes[attribute]
47
47
  if (method = INTERPRET_METHOD[attribute_type])
48
48
  send method, attribute, val
49
49
  else
@@ -16,10 +16,9 @@ class Card
16
16
  def sort val
17
17
  return nil unless full?
18
18
 
19
- sort_field = val[:return] || "db_content"
20
- val = val.clone
21
- item = val.delete(:item) || "left"
22
- sort_by val, item, sort_field
19
+ interpret_sort_hash val do |value, item, sort_field|
20
+ sort_by value, item, sort_field
21
+ end
23
22
  end
24
23
 
25
24
  def sort_by val, item, sort_field
@@ -95,6 +94,15 @@ class Card
95
94
  s.interpret val
96
95
  s
97
96
  end
97
+
98
+ private
99
+
100
+ def interpret_sort_hash val
101
+ val = val.symbolize_keys
102
+ item = val.delete(:item) || "left"
103
+ sort_field = val[:return] || "db_content"
104
+ yield val, item, sort_field
105
+ end
98
106
  end
99
107
  end
100
108
  end
@@ -79,7 +79,7 @@ class Card
79
79
  end
80
80
 
81
81
  def standard_full_field table, field
82
- if ATTRIBUTES[field.to_sym] == :basic
82
+ if Query.attributes[field.to_sym] == :basic
83
83
  "#{table}.#{field}"
84
84
  else
85
85
  safe_sql field
@@ -10,17 +10,20 @@ class Card
10
10
  def ensure_set &block
11
11
  set_module = yield
12
12
  set_module = card_set_module_const_get(set_module) unless set_module.is_a?(Module)
13
- set_module
14
- rescue NameError => e
15
- if e.message =~ /uninitialized constant (?:Card::Set::)?(.+)$/
16
- define_set Regexp.last_match(1)
17
- end
13
+ rescue NameError => error
14
+ define_set_from_error error
18
15
  # try again - there might be another submodule that doesn't exist
19
16
  ensure_set(&block)
20
17
  else
21
18
  set_module.extend Card::Set
22
19
  end
23
20
 
21
+ def define_set_from_error error
22
+ match = error.message.match(/uninitialized constant (?:Card::Set::)?(.+)$/)
23
+ return unless match
24
+ define_set match[1]
25
+ end
26
+
24
27
  def attachment name, args
25
28
  include_set Abstract::Attachment
26
29
  add_attributes name, "remote_#{name}_url".to_sym,
@@ -23,13 +23,21 @@ class Card
23
23
  if condition == :when
24
24
  validate_when_value val
25
25
  else
26
- invalid = Array.wrap(val) - Api::OPTIONS[condition]
27
- return if invalid.empty?
26
+ validate_standard_condition_value condition, val
27
+ end
28
+ end
28
29
 
29
- raise ArgumentError,
30
- "invalid option#{'s' if invalid.size > 1} '#{invalid}' "\
30
+ def validate_standard_condition_value condition, val
31
+ invalid = invalid_condition_values condition, val
32
+ return if invalid.empty?
33
+
34
+ raise ArgumentError,
35
+ "invalid option#{'s' if invalid.size > 1} '#{invalid}' "\
31
36
  "for condition '#{condition}' in event '#{@event}'"
32
- end
37
+ end
38
+
39
+ def invalid_condition_values condition, val
40
+ Array.wrap(val) - Api::OPTIONS[condition]
33
41
  end
34
42
 
35
43
  def validate_when_value val
@@ -106,20 +106,27 @@ class Card
106
106
 
107
107
  def register_set_format format_class, mod
108
108
  if all_set?
109
- all_set_format_mod! format_class, mod
109
+ register_all_set_format format_class, mod
110
110
  else
111
- format_type = abstract_set? ? :abstract_format : :nonbase_format
112
- # ready to include dynamically in set members' format singletons
113
- format_hash = modules[format_type][format_class] ||= {}
114
- format_hash[shortname] ||= []
115
- format_hash[shortname] << mod
111
+ register_standard_set_format format_class, mod
116
112
  end
117
113
  end
118
114
 
119
115
  # make mod ready to include in base (non-set-specific) format classes
120
- def all_set_format_mod! format_class, mod
121
- modules[:base_format][format_class] ||= []
122
- modules[:base_format][format_class] << mod
116
+ def register_all_set_format format_class, mod
117
+ add_to_array_val modules[:base_format], format_class, mod
118
+ end
119
+
120
+ def register_standard_set_format format_class, mod
121
+ format_type = abstract_set? ? :abstract_format : :nonbase_format
122
+ # ready to include dynamically in set members' format singletons
123
+ format_hash = modules[format_type][format_class] ||= {}
124
+ add_to_array_val format_hash, shortname, mod
125
+ end
126
+
127
+ def add_to_array_val hash, key, val
128
+ hash[key] ||= []
129
+ hash[key] << val
123
130
  end
124
131
 
125
132
  class << self
@@ -28,7 +28,7 @@ class Card
28
28
 
29
29
  def add_attributes *args
30
30
  Card.set_specific_attributes ||= []
31
- Card.set_specific_attributes += args.map(&:to_s)
31
+ Card.set_specific_attributes += args.map(&:to_sym)
32
32
  Card.set_specific_attributes.uniq!
33
33
  end
34
34
 
@@ -37,14 +37,11 @@ class Card
37
37
  Card::Set.traits[mod] || Card::Set.traits[mod] = {}
38
38
  end
39
39
 
40
- def add_traits args, options
41
- mod = self
42
- mod_traits = get_traits mod
40
+ def add_traits traits, options
41
+ mod_traits = get_traits self
42
+ new_opts = new_trait_opts options
43
43
 
44
- new_opts = options[:type] ? { type: options[:type] } : {}
45
- new_opts[:default_content] = options[:default] if options[:default]
46
-
47
- args.each do |trait|
44
+ traits.each do |trait|
48
45
  define_trait_card trait, new_opts
49
46
  define_trait_reader trait if options[:reader]
50
47
  define_trait_writer trait if options[:writer]
@@ -53,6 +50,12 @@ class Card
53
50
  end
54
51
  end
55
52
 
53
+ def new_trait_opts options
54
+ %i[type default_content].each_with_object({}).each do |key, hash|
55
+ hash[key] = options[key] if options[key]
56
+ end
57
+ end
58
+
56
59
  def define_trait_card trait, opts
57
60
  define_method "#{trait}_card" do
58
61
  fetch trait.to_sym, new: opts.clone, eager_cache: true
@@ -14,6 +14,8 @@ class Card
14
14
  # add 'spoiler', content: 'John Snow is a Targaryen'
15
15
  # add card_obj, delayed: true
16
16
 
17
+ include Args
18
+
17
19
  def << value
18
20
  add value
19
21
  end
@@ -61,7 +63,7 @@ class Card
61
63
  end
62
64
 
63
65
  def new_by_attributes name, attributes={}
64
- attributes ||= {}
66
+ attributes = attributes&.symbolize_keys || {}
65
67
  absolute_name = absolutize_subcard_name name
66
68
  subcard_args = extract_subcard_args! attributes
67
69
  card = initialize_by_attributes absolute_name, attributes
@@ -75,31 +77,8 @@ class Card
75
77
  Card.assign_or_newish name, attributes, local_only: true
76
78
  end
77
79
 
78
- # TODO: this method already exists as card instance method in
79
- # tracked_attributes.rb. Find a place for it where its accessible
80
- # for both. There is one important difference. The keys are symbols
81
- # here instead of strings
82
- def extract_subcard_args! args
83
- subcards = args.delete(:subcards) || {}
84
- if (subfields = args.delete(:subfields))
85
- subfields.each_pair do |key, value|
86
- subcards[normalize_subfield_key(key)] = value
87
- end
88
- end
89
- args.keys.each do |key|
90
- subcards[key] = args.delete(key) if key =~ /^\+/
91
- end
92
- subcards
93
- end
94
-
95
80
  private
96
81
 
97
- # ensure a leading '+'
98
- def normalize_subfield_key key
99
- key = Card::Codename.name(key) if key.is_a?(Symbol) && Card::Codename.exist?(key)
100
- key.to_name.prepend_joint
101
- end
102
-
103
82
  # Handles hash with several subcards
104
83
  def multi_add args
105
84
  args.each_pair do |key, val|
@@ -0,0 +1,42 @@
1
+ class Card
2
+ class Subcards
3
+ # Handling shared subcard args processing
4
+ module Args
5
+ def extract_subcard_args! args
6
+ safe_subcard_args args do |subcards|
7
+ extract_explicit_subfields subcards, args
8
+ extract_implicit_subfields subcards, args
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ # FIXME: the following should be handled before it gets this far
15
+ def safe_subcard_args args
16
+ subcards = args.delete(:subcards) || {}
17
+ yield subcards
18
+ subcards.try(:to_unsafe_h) || subcards
19
+ end
20
+
21
+ def extract_explicit_subfields subcards, args
22
+ return unless (subfields = args.delete :subfields)
23
+
24
+ subfields.each_pair do |key, value|
25
+ subcards[normalize_subfield_key(key)] = value
26
+ end
27
+ end
28
+
29
+ # ensure a leading '+'
30
+ def normalize_subfield_key key
31
+ key = Card::Codename.name(key) if key.is_a?(Symbol) && Card::Codename.exist?(key)
32
+ key.to_name.prepend_joint
33
+ end
34
+
35
+ def extract_implicit_subfields subcards, args
36
+ args.each_key do |key|
37
+ subcards[key.to_s] = args.delete(key) if key.to_s.match?(/^\+/)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -14,17 +14,22 @@ class Card
14
14
  end
15
15
  end
16
16
 
17
- def log_file_action path
18
- status, color =
19
- if File.exist?(path) && !@force
20
- ["file exists (use 'force=true' to override)", :yellow]
21
- else
22
- status = File.exist?(path) ? "overridden" : "created"
23
- yield
24
- [status, :green]
25
- end
17
+ def log_file_action path, &block
18
+ if File.exist?(path) && !@force
19
+ log_file_exists path
20
+ else
21
+ execute_and_log_file_action path, &block
22
+ end
23
+ end
24
+
25
+ def execute_and_log_file_action path
26
+ status = File.exist?(path) ? "overridden" : "created"
27
+ yield
28
+ color_puts status, :green, path
29
+ end
26
30
 
27
- color_puts status, color, path
31
+ def log_file_exists path
32
+ color_puts "file exists (use 'force=true' to override)", :yellow, path
28
33
  end
29
34
 
30
35
  # insert content into a file at a given line number
@@ -30,7 +30,8 @@ class Card
30
30
  :nest_syntax, # full nest syntax
31
31
  :wrap, # wrap the nest with a wrapper
32
32
  :show, # render these views when optional
33
- :hide # do not render these views when optional
33
+ :hide, # do not render these views when optional
34
+ :unknown # view to use if nested card is unknown
34
35
  ], # show/hide can be view (Symbol), list of views (Array),
35
36
  # or comma separated views (String)
36
37
  # NOTE: although show and hide are in this non-inheriting group, they are
@@ -28,11 +28,22 @@ class Card
28
28
  format.view_setting setting_name, view
29
29
  end
30
30
 
31
- # by default views can't handle unknown cards, but this can be overridden in
32
- # view definitions with the `unknown` directive
31
+ # views for unknown cards can be configured in view definitions
32
+ # or render/nest options (the latter take precedence)
33
33
  def alter_unknown
34
+ return if card.known?
35
+
36
+ unknown_from_options || unknown_from_view_definition
37
+ end
38
+
39
+ def unknown_from_options
40
+ unknown.to_sym if unknown.present?
41
+ end
42
+
43
+ def unknown_from_view_definition
34
44
  setting = setting(:unknown)
35
- return if setting == true || card.known?
45
+ return if setting == true # use original view
46
+
36
47
  setting.is_a?(Symbol) ? setting : format.view_for_unknown(requested_view)
37
48
  end
38
49
 
data/lib/cardio.rb CHANGED
@@ -39,70 +39,17 @@ module Cardio
39
39
  @cache ||= ::Rails.cache
40
40
  end
41
41
 
42
+ # TODO: many of these defaults should be in mods!
42
43
  def default_configs
43
- {
44
- read_only: read_only?,
45
-
46
- # if you disable inline styles tinymce's formatting options stop working
47
- allow_inline_styles: true,
48
-
49
- delaying: nil,
50
-
51
- recaptcha_public_key: nil, # deprecated; use recaptcha_site_key instead
52
- recaptcha_private_key: nil, # deprecated; use recaptcha_secret_key instead
53
- recaptcha_proxy: nil,
54
- recaptcha_site_key: nil,
55
- recaptcha_secret_key: nil,
56
- recaptcha_minimum_score: 0.5,
57
-
58
- google_analytics_key: nil,
59
-
60
- override_host: nil,
61
- override_protocol: nil,
62
-
63
- no_authentication: false,
64
- files_web_path: "files",
65
-
66
- max_char_count: 200,
67
- max_depth: 20,
68
- email_defaults: nil,
69
-
70
- token_expiry: 2.days,
71
- acts_per_page: 10,
72
- space_last_in_multispace: true,
73
- closed_search_limit: 10,
74
- paging_limit: 20,
75
-
76
- non_createable_types: [%w[signup setting set session bootswatch_skin customized_bootswatch_skin]], # FIXME
77
- view_cache: false,
78
- rss_enabled: false,
79
- double_click: :signed_in,
80
-
81
- encoding: "utf-8",
82
- request_logger: false,
83
- performance_logger: false,
84
- sql_comments: true,
85
-
86
- file_storage: :local,
87
- file_buckets: {},
88
- file_default_bucket: nil,
89
- protocol_and_host: nil,
90
-
91
- rich_text_editor: :tinymce,
92
-
93
- persistent_cache: true,
94
- prepopulate_cache: false,
95
- machine_refresh: :cautious, # options: eager, cautious, never
96
- compress_javascript: true,
97
-
98
- allow_irreversible_admin_tasks: false,
99
- raise_all_rendering_errors: false,
100
- rescue_all_in_controller: true,
101
- navbox_match_start_only: true,
44
+ defaults_from_yaml.merge(
45
+ read_only: !ENV["DECKO_READ_ONLY"].nil?,
46
+ load_strategy: (ENV["REPO_TMPSETS"] || ENV["TMPSETS"] ? :tmp_files : :eval)
47
+ )
48
+ end
102
49
 
103
- load_strategy: (ENV["REPO_TMPSETS"] || ENV["TMPSETS"] ? :tmp_files : :eval),
104
- cache_set_module_list: false
105
- }
50
+ def defaults_from_yaml
51
+ filename = File.expand_path "cardio/defaults.yml", __dir__
52
+ YAML.load_file filename
106
53
  end
107
54
 
108
55
  def set_config config
@@ -136,10 +83,6 @@ module Cardio
136
83
  config.watchable_dirs["#{mod_path}/set"] = [:rb]
137
84
  end
138
85
 
139
- def read_only?
140
- !ENV["DECKO_READ_ONLY"].nil?
141
- end
142
-
143
86
  # In production mode set_config gets called twice.
144
87
  # The second call overrides all deck config settings
145
88
  # so don't change settings here if they already exist