card 1.101.0 → 1.101.1

Sign up to get free protection for your applications and to get access to all the features.
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