card 1.20.1 → 1.20.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/card.gemspec +2 -2
  4. data/db/migrate_core_cards/20160811115836_rename_stats_to_admin.rb +2 -2
  5. data/db/migrate_core_cards/20170209132834_email_test_context.rb +47 -0
  6. data/db/migrate_core_cards/data/mailer/follower_notification_email.html +1 -1
  7. data/db/migrate_core_cards/data/mailer/follower_notification_email.txt +1 -1
  8. data/db/migrate_core_cards/data/mailer/mail_config.json +1 -1
  9. data/db/seed/new/card_actions.yml +699 -771
  10. data/db/seed/new/card_acts.yml +64 -616
  11. data/db/seed/new/card_changes.yml +9222 -25055
  12. data/db/seed/new/card_references.yml +767 -606
  13. data/db/seed/new/cards.yml +2125 -1829
  14. data/db/seed/test/fixtures/card_actions.yml +1713 -1825
  15. data/db/seed/test/fixtures/card_acts.yml +341 -893
  16. data/db/seed/test/fixtures/card_changes.yml +20868 -36801
  17. data/db/seed/test/fixtures/card_references.yml +1516 -1250
  18. data/db/seed/test/fixtures/cards.yml +3194 -2898
  19. data/db/version_core_cards.txt +1 -1
  20. data/lib/card/env/location.rb +1 -1
  21. data/lib/card/format/content.rb +1 -1
  22. data/lib/card/format/nest/subformat.rb +2 -1
  23. data/lib/card/format/render.rb +19 -1
  24. data/lib/card/mailer.rb +5 -2
  25. data/lib/card/model/save_helper.rb +15 -4
  26. data/lib/card/name.rb +1 -45
  27. data/lib/card/name/fields_and_traits.rb +10 -9
  28. data/lib/card/name/{variants.rb → name_variants.rb} +1 -1
  29. data/lib/card/query.rb +28 -2
  30. data/lib/card/query/helpers.rb +1 -0
  31. data/lib/card/query/interpretation.rb +1 -0
  32. data/lib/card/query/relational_attributes.rb +27 -5
  33. data/lib/card/query/sql_statement.rb +1 -1
  34. data/lib/card/tasks/card/create.rake +177 -0
  35. data/mod/carrierwave/set/type/file.rb +1 -1
  36. data/mod/carrierwave/set/type/image.rb +9 -2
  37. data/mod/core/chunk/reference.rb +1 -1
  38. data/mod/core/set/all/assign_attributes.rb +14 -14
  39. data/mod/core/set/all/collection.rb +1 -1
  40. data/mod/core/set/all/fetch.rb +4 -0
  41. data/mod/core/set/all/haml.rb +82 -3
  42. data/mod/core/set/all/name.rb +7 -6
  43. data/mod/core/set/all/references.rb +1 -1
  44. data/mod/core/set/all/subcards.rb +4 -0
  45. data/mod/core/set_pattern/07_type_plus_right.rb +1 -1
  46. data/mod/core/spec/set/all/{tracked_attributes_spec.rb → assign_attributes_spec.rb} +1 -1
  47. data/mod/email/set/abstract/test_context.rb +26 -0
  48. data/mod/email/set/all/notify.rb +10 -5
  49. data/mod/email/set/right/html_message.rb +2 -0
  50. data/mod/email/set/right/subject.rb +1 -0
  51. data/mod/email/set/right/text_message.rb +1 -0
  52. data/mod/history/set/all/content_history.rb +2 -1
  53. data/mod/machines/set/type/coffee_script.rb +1 -1
  54. data/mod/pointer/set/abstract/01_pointer/edit.rb +10 -7
  55. data/mod/pointer/spec/set/type/pointer_spec.rb +2 -2
  56. data/mod/standard/set/abstract/wql_search.rb +1 -1
  57. data/mod/standard/set/all/rich_html/form.rb +128 -169
  58. data/mod/standard/set/all/rich_html/form_elements.rb +52 -0
  59. data/mod/standard/set/all/rich_html/formgroup.rb +34 -0
  60. data/mod/standard/set/type/session.rb +2 -8
  61. data/mod/standard/set/type/toggle.rb +23 -3
  62. data/mod/standard/spec/set/type/email_template/email_config_spec.rb +24 -13
  63. data/mod/standard/spec/set/type/toggle_spec.rb +13 -2
  64. data/spec/lib/card/name_spec.rb +1 -224
  65. data/spec/lib/card/query_spec.rb +56 -1
  66. data/spec/lib/card/stage_director_spec.rb +19 -0
  67. data/spec/support/helper/render_helper.rb +2 -0
  68. data/tmpsets/set/mod001-core/all/actify.rb +6 -5
  69. data/tmpsets/set/mod001-core/all/fetch.rb +12 -14
  70. data/tmpsets/set/mod001-core/all/name.rb +1 -1
  71. data/tmpsets/set/mod001-core/all/permissions.rb +22 -12
  72. data/tmpsets/set/mod001-core/all/tracked_attributes.rb +0 -76
  73. data/tmpsets/set/mod001-core/all/utils.rb +3 -40
  74. data/tmpsets/set/mod002-history/all/history.rb +2 -1
  75. data/tmpsets/set/mod008-solid_cache/abstract/solid_cache.rb +1 -1
  76. metadata +15 -17
  77. data/tmpsets/set/mod013-carrierwave/abstract/attachment.rb +0 -282
  78. data/tmpsets/set/mod013-carrierwave/type/file.rb +0 -155
  79. data/tmpsets/set/mod013-carrierwave/type/image.rb +0 -96
  80. data/tmpsets/set/mod014-admin/self/admin.rb +0 -113
  81. data/tmpsets/set/mod014-admin/self/admin_info.rb +0 -110
  82. data/tmpsets/set/mod014-admin/self/version.rb +0 -15
  83. data/tmpsets/set/mod015-developer/all/event_viz.rb +0 -59
  84. data/tmpsets/set/mod015-developer/all/view_viz.rb +0 -30
  85. data/tmpsets/set/mod015-developer/right/debug.rb +0 -96
@@ -1 +1 @@
1
- 20160811115836
1
+ 20170209132834
@@ -23,7 +23,7 @@ class Card
23
23
  Rails.logger.warn "Pass only strings to card_path. "\
24
24
  "(#{rel_path} = #{rel_path.class})"
25
25
  end
26
- if rel_path =~ /^\//
26
+ if rel_path =~ %r{^(https?\:)?/}
27
27
  rel_path
28
28
  else
29
29
  "#{Card.config.relative_url_root}/#{rel_path}"
@@ -57,7 +57,7 @@ class Card
57
57
  end
58
58
 
59
59
  def output *content
60
- content ||= yield
60
+ content = yield if block_given?
61
61
  Array.wrap(content).flatten.compact.join "\n"
62
62
  end
63
63
  end
@@ -5,7 +5,8 @@ class Card
5
5
  def subformat subcard
6
6
  subcard = Card.fetch(subcard, new: {}) if subcard.is_a?(String)
7
7
  self.class.new subcard, root: @root, parent: self, depth: @depth + 1,
8
- form: @form, mode: @mode,
8
+ form: @form,
9
+ mode: @mode,
9
10
  context_names: @context_names
10
11
  end
11
12
 
@@ -42,11 +42,29 @@ class Card
42
42
  current_view(view) do
43
43
  with_nest_mode view do
44
44
  method = view_method view
45
- method.arity.zero? ? method.call : method.call(args)
45
+ rendered = method.arity.zero? ? method.call : method.call(args)
46
+ add_debug_info view, method, rendered
46
47
  end
47
48
  end
48
49
  end
49
50
 
51
+ def add_debug_info view, method, rendered
52
+ return rendered unless show_debug_info?
53
+ <<-HTML
54
+ <view-debug view='#{card.name}##{view}' src='#{pretty_path method.source_location}' module='#{method.owner}'/>
55
+ #{rendered}
56
+ HTML
57
+ end
58
+
59
+ def show_debug_info?
60
+ Env.params[:debug] == "view"
61
+ end
62
+
63
+ def pretty_path source_location
64
+ source_location.first.gsub(%r{^.+mod\d+-([^/]+)}, '\1: ') + ':' +
65
+ source_location.second.to_s
66
+ end
67
+
50
68
  # setting (:alway, :never, :nested) designated in view definition
51
69
  def view_cache_setting view
52
70
  method = self.class.view_cache_setting_method view
@@ -20,14 +20,17 @@ class Card
20
20
  end
21
21
 
22
22
  def layout message
23
- %(
23
+ <<-HTML
24
24
  <!DOCTYPE html>
25
25
  <html>
26
+ <head>
27
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8"/>
28
+ </head>
26
29
  <body>
27
30
  #{message}
28
31
  </body>
29
32
  </html>
30
- )
33
+ HTML
31
34
  end
32
35
  end
33
36
  end
@@ -96,7 +96,7 @@ class Card
96
96
  if name_or_args.is_a?(Hash)
97
97
  name_or_args
98
98
  else
99
- add_name name_or_args, content_or_args
99
+ add_name name_or_args, content_or_args || {}
100
100
  end
101
101
  end
102
102
 
@@ -128,9 +128,20 @@ class Card
128
128
  end
129
129
 
130
130
  def ensure_attributes card, args
131
- update_args = args.select { |key, value| card.send(key) != value }
132
- return if update_args.empty?
133
- card.update_attributes! update_args
131
+ args = args.with_indifferent_access
132
+ subcards = card.extract_subcard_args! args
133
+ update_args =
134
+ args.select do |key, value|
135
+ if key =~ /^\+/
136
+ subfields[key] = value
137
+ false
138
+ else
139
+ card.send(key) != value
140
+ end
141
+ end
142
+ return if update_args.empty? && subcards.empty?
143
+ # FIXME: use ensure_attributes for subcards
144
+ card.update_attributes! update_args.merge(subcards: subcards)
134
145
  end
135
146
 
136
147
  def add_style name, opts={}
@@ -10,9 +10,7 @@ class Card
10
10
  # Card::Name adds support for deeper card integration
11
11
  class Name < SmartName
12
12
  include FieldsAndTraits
13
- include Variants
14
-
15
- RELATIVE_REGEXP = /\b_(left|right|whole|self|user|main|\d+|L*R?)\b/
13
+ include ::Card::Name::NameVariants
16
14
 
17
15
  self.params = Card::Env # yuck!
18
16
  self.session = proc { Card::Auth.current.name }
@@ -44,24 +42,6 @@ class Card
44
42
  Card::Codename[Card.fetch_id self]
45
43
  end
46
44
 
47
- def relative_name context_name
48
- to_show(*context_name.to_name.parts).to_name
49
- end
50
-
51
- def absolute_name context_name
52
- to_absolute_name(context_name)
53
- end
54
-
55
- def child_of? context_name
56
- if context_name.present?
57
- # Do I still equal myself after I've been relativised in the context
58
- # of context_name?
59
- relative_name(context_name).key != absolute_name(context_name).key
60
- else
61
- s.match(/^\s*\+/)
62
- end
63
- end
64
-
65
45
  def setting?
66
46
  Set::Type::Setting.member_names[key]
67
47
  end
@@ -70,30 +50,6 @@ class Card
70
50
  Set::Pattern.card_keys[tag_name.key]
71
51
  end
72
52
 
73
- def relative?
74
- s =~ RELATIVE_REGEXP || starts_with_joint?
75
- end
76
-
77
- def simple_relative?
78
- relative? && stripped.to_name.starts_with_joint?
79
- end
80
-
81
- def absolute?
82
- !relative?
83
- end
84
-
85
- def stripped
86
- s.gsub RELATIVE_REGEXP, ""
87
- end
88
-
89
- def starts_with_joint?
90
- s =~ /^\+/
91
- end
92
-
93
- def to_sym
94
- s.to_sym
95
- end
96
-
97
53
  # processes contextual title argument used in nests like "title: _left"
98
54
  def title title_directive, context_names
99
55
  title_directive.to_name.to_absolute_name(self).to_show(*context_names)
@@ -23,9 +23,12 @@ class Card
23
23
  end
24
24
 
25
25
  # @return [True/False]
26
- def field_of? context_name
27
- if context_name.present?
28
- child_of?(context_name) && relative_name(context_name).length == 2
26
+ def field_of? context
27
+ if context.present?
28
+ child_of?(context) && relative_name(context).length == 2
29
+ # junction? &&
30
+ # absolute_name(context).left_name.key == context.to_name.key
31
+ # @child_of?(context_name) && relative_name(context_name).length == 2
29
32
  else
30
33
  s.match(/^\s*\+[^+]+$/).present?
31
34
  end
@@ -38,13 +41,14 @@ class Card
38
41
  # @return [String]
39
42
  def trait tag_code
40
43
  name = trait_name tag_code
41
- raise Card::Error::NotFound, "unknown codename: #{tag_code}" unless name
44
+
42
45
  name.s
43
46
  end
44
47
 
45
48
  # @return [Card::Name]
46
49
  def trait_name tag_code
47
- return unless (card_id = Card::Codename[tag_code])
50
+ card_id = Card::Codename[tag_code]
51
+ raise Card::Error::NotFound, "unknown codename: #{tag_code}" unless card_id
48
52
  [self, Card.quick_fetch(card_id).name].to_name
49
53
  end
50
54
 
@@ -59,9 +63,6 @@ class Card
59
63
  end.present?
60
64
  end
61
65
  end
62
-
63
-
64
-
65
66
  end
66
67
  end
67
- end
68
+ end
@@ -1,6 +1,6 @@
1
1
  class Card
2
2
  class Name
3
- module Variants
3
+ module NameVariants
4
4
  @@variant_methods = [:capitalize, :singularize, :pluralize, :titleize,
5
5
  :downcase, :upcase, :swapcase, :reverse, :succ]
6
6
  @@variant_aliases = { capitalized: :capitalize, singular: :singularize,
@@ -54,7 +54,8 @@ class Card
54
54
  creator_id updater_id codename read_rule_id ),
55
55
  relational: %w( type part left right
56
56
  editor_of edited_by last_editor_of last_edited_by
57
- creator_of created_by member_of member ),
57
+ creator_of created_by member_of member
58
+ updater_of updated_by),
58
59
  plus_relational: %w(plus left_plus right_plus),
59
60
  ref_relational: %w( refer_to referred_to_by
60
61
  link_to linked_to_by
@@ -134,7 +135,12 @@ class Card
134
135
  # @return Integer for :count, otherwise Array of Strings or Integers
135
136
  def get_results retrn
136
137
  rows = run_sql
137
- if retrn == "name" && (statement[:prepend] || statement[:append])
138
+ if retrn =~ /_\w+/
139
+ name_processor = contextual_name_processor retrn
140
+ rows.map do |row|
141
+ name_processor.call row["name"]
142
+ end
143
+ elsif retrn == "name" && (statement[:prepend] || statement[:append])
138
144
  rows.map do |row|
139
145
  [statement[:prepend], row["name"], statement[:append]].compact * "+"
140
146
  end
@@ -158,6 +164,26 @@ class Card
158
164
  @sql ||= SqlStatement.new(self).build.to_s
159
165
  end
160
166
 
167
+ def contextual_name_processor pattern
168
+ case pattern.downcase
169
+ when "_left", "_l"
170
+ ->(name) { name.to_name.left_name.to_s }
171
+ when "_right", "_r"
172
+ ->(name) { name.to_name.right_name.to_s }
173
+ else
174
+ chain = "name.to_name"
175
+ pattern.each_char do |ch|
176
+ case ch
177
+ when "l", "L"
178
+ chain += ".left_name"
179
+ when "r", "R"
180
+ chain += ".right_name"
181
+ end
182
+ end
183
+ eval "lambda { |name| #{chain}.to_s }"
184
+ end
185
+ end
186
+
161
187
  # Query Hierarchy
162
188
  # @root, @subqueries, and @superquery are used to track a hierarchy of
163
189
  # query objects. This nesting allows to find, for example, cards that
@@ -45,6 +45,7 @@ class Card
45
45
  end
46
46
  end
47
47
 
48
+ # generates an id used to identify a table variable in the sql statement
48
49
  def table_id force=false
49
50
  if force
50
51
  tick_table_seq!
@@ -42,6 +42,7 @@ class Card
42
42
  clause = clause_to_hash clause
43
43
  clause.symbolize_keys!
44
44
  clause.each do |key, val|
45
+ next if key.to_sym == :return
45
46
  clause[key] = normalize_value val
46
47
  end
47
48
  clause
@@ -1,7 +1,6 @@
1
1
  class Card
2
2
  class Query
3
3
  module RelationalAttributes
4
-
5
4
  def type val
6
5
  restrict :type_id, val
7
6
  end
@@ -19,7 +18,10 @@ class Card
19
18
  restrict :right_id, val
20
19
  end
21
20
 
22
- def editor_of val
21
+ # action_table_id and action_condition are needed to reuse that method
22
+ # for `updater_of`
23
+ def editor_of val, action_table_id=nil, action_condition=nil
24
+ action_table_id ||= table_id true
23
25
  act_join = Join.new(
24
26
  from: self,
25
27
  to: ["card_acts", "a#{table_id true}", "actor_id"]
@@ -27,17 +29,25 @@ class Card
27
29
  joins << act_join
28
30
  action_join = Join.new(
29
31
  from: act_join,
30
- to: ["card_actions", "an#{table_id true}", "card_act_id"],
32
+ to: ["card_actions", "an#{action_table_id}", "card_act_id"],
31
33
  superjoin: act_join
32
34
  )
35
+ # Join.new resets @conditions, so we have to set it after
36
+ # initialization
37
+ action_join.conditions << action_condition if action_condition
33
38
  join_cards val, from: action_join, from_field: "card_id"
34
39
  end
35
40
 
36
- def edited_by val
41
+
42
+ # action_table_id and action_condition are needed to reuse that method
43
+ # for `updated_by`
44
+ def edited_by val, action_table_id=nil, action_condition=nil
45
+ action_table_id ||= table_id true
37
46
  action_join = Join.new(
38
47
  from: self,
39
- to: ["card_actions", "an#{table_id true}", "card_id"]
48
+ to: ["card_actions", "an#{action_table_id}", "card_id"],
40
49
  )
50
+ action_join.conditions << action_condition if action_condition
41
51
  joins << action_join
42
52
  act_join = Join.new(
43
53
  from: action_join,
@@ -47,6 +57,18 @@ class Card
47
57
  join_cards val, from: act_join, from_field: "actor_id"
48
58
  end
49
59
 
60
+ # edited but not created
61
+ def updated_by val
62
+ action_table_id = table_id true
63
+ edited_by val, action_table_id, "an#{action_table_id}.action_type = 1"
64
+ end
65
+
66
+ # editor but not creator
67
+ def updater_of val
68
+ action_table_id = table_id true
69
+ editor_of val, action_table_id, "an#{action_table_id}.action_type = 1"
70
+ end
71
+
50
72
  def last_editor_of val
51
73
  join_cards val, to_field: "updater_id"
52
74
  end
@@ -40,7 +40,7 @@ class Card
40
40
 
41
41
  def fields
42
42
  table = @query.table_alias
43
- field = @mods[:return]
43
+ field = @mods[:return] unless @mods[:return] =~ /_\w+/
44
44
  field = field.blank? ? :card : field.to_sym
45
45
  field = full_field(table, field)
46
46
  [field, @mods[:sort_join_field]].compact * ", "
@@ -0,0 +1,177 @@
1
+ require "colorize"
2
+
3
+ namespace :card do
4
+ namespace :create do
5
+ DEFAULT_RULE = { style: "*all+*style",
6
+ script: "*all+*script" }.freeze
7
+
8
+ # 1. Creates a js/coffee/css/scss file with the appropriate path in
9
+ # the given mod.
10
+ # If a card with the given name exists it copies the content to that
11
+ # file.
12
+ # 2. Creates a self set file that loads the code file as content
13
+ # 3. Creates a card migration that adds the code card to the script/style
14
+ # rule defined by DEFAULT_RULE. Override the rule_card_name
15
+ # method to change it.
16
+ # @parem mod [String] the name of the mod where the files are created
17
+ # @param name [String] the card name
18
+ # A "script: " or "style: " prefix is added if missing.
19
+ # Whitespaces are translated to underscores for the codename.
20
+ # @param type supported options are js, coffee, css, scss
21
+ desc "create folders and files for scripts or styles"
22
+ task codefile: :environment do
23
+ with_params(:mod, :name, :type) do |mod, name, category, type, type_codename|
24
+ create_content_file mod, name, type
25
+ create_rb_file mod, name
26
+ create_migration_file name, category, type_codename
27
+ end
28
+ end
29
+
30
+ # shortcut for create:codefile type=scss
31
+ desc "create folders and files for stylesheet"
32
+ task style: :environment do
33
+ ENV["type"] ||= "scss"
34
+ Rake::Task["card:create:codefile"].invoke
35
+ end
36
+
37
+ # shortcut for create:codefile type=coffee
38
+ desc "create folders and files for script"
39
+ task script: :environment do
40
+ ENV["type"] ||= "coffee"
41
+ Rake::Task["card:create:codefile"].invoke
42
+ end
43
+
44
+ def create_content_file mod, name, type
45
+ write_to_mod(mod, content_dir(type), content_filename(name, type)) do |f|
46
+ content = (card = Card.fetch(name)) ? card.content : ""
47
+ f.puts content
48
+ end
49
+ end
50
+
51
+ def create_rb_file mod, name
52
+ self_dir = File.join "set", "self"
53
+ self_file = name + ".rb"
54
+ write_to_mod(mod, self_dir, self_file) do |f|
55
+ f.puts("include_set Abstract::CodeFile")
56
+ end
57
+ end
58
+
59
+ def create_migration_file name, category, type_codename
60
+ puts "creating migration file...".yellow
61
+ migration_out = `bundle exec wagn generate card:migration #{name}`
62
+ migration_file = migration_out[/db.*/]
63
+ content = migration_content name, category, type_codename
64
+ write_at migration_file, 5, content # 5 is line no.
65
+ end
66
+
67
+ def with_params *keys
68
+ return unless parameters_present?(*keys)
69
+ values = keys.map { |k| ENV[k.to_s] }
70
+ mod, name, type = values
71
+ name = remove_prefix name
72
+ typecode = type_codename(type)
73
+ yield mod, name, category(typecode), type, typecode
74
+ end
75
+
76
+ def remove_prefix name
77
+ name.sub(/^(?:script|style):?_?\s*/, "")
78
+ end
79
+
80
+ def category typecode
81
+ case typecode
82
+ when :java_script, :coffee_script then
83
+ :script
84
+ when :css, :scss then
85
+ :style
86
+ end
87
+ end
88
+
89
+ def parameters_present? *env_keys
90
+ missing = env_keys.select { |k| !ENV[k.to_s] }
91
+ return true if missing.empty?
92
+ missing.each do |key|
93
+ color_puts "missing parameter:", :red, key
94
+ end
95
+ false
96
+ end
97
+
98
+ def color_puts colored_text, color, text=""
99
+ puts "#{colored_text.send(color.to_s)} #{text}"
100
+ end
101
+
102
+ def write_at fname, at_line, sdat
103
+ open(fname, "r+") do |f|
104
+ (at_line - 1).times do # read up to the line you want to write after
105
+ f.readline
106
+ end
107
+ pos = f.pos # save your position in the file
108
+ rest = f.read # save the rest of the file
109
+ f.seek pos # go back to the old position
110
+ f.puts sdat # write new data & rest of file
111
+ f.puts rest
112
+ color_puts "created", :green, fname
113
+ end
114
+ end
115
+
116
+ def write_to_mod mod, relative_dir, filename
117
+ mod_dir = File.join "mod", mod
118
+ dir = File.join mod_dir, relative_dir
119
+ path = File.join dir, filename
120
+ Dir.mkdir(dir) unless Dir.exist?(dir)
121
+ if File.exist?(path)
122
+ color_puts "file exists", :yellow, path
123
+ else
124
+ File.open(path, "w") do |opened_file|
125
+ yield(opened_file)
126
+ color_puts "created", :green, path
127
+ end
128
+ end
129
+ end
130
+
131
+ def type_codename type
132
+ case type
133
+ when "js" then
134
+ :java_script
135
+ when "coffee" then
136
+ :coffee_script
137
+ when "css", "scss" then
138
+ type.to_sym
139
+ else
140
+ color_puts "'#{type}' is not a valid type. "\
141
+ "Please choose between js, coffee, css and scss.", :red
142
+ exit
143
+ end
144
+ end
145
+
146
+ def migration_content name, category, type_codename
147
+ <<-RUBY
148
+ add_#{category} "#{name}",
149
+ type_id: #{type_id type_codename},
150
+ to: "#{rule_card_name category}"
151
+ RUBY
152
+ end
153
+
154
+ def content_dir type
155
+ dir = case type
156
+ when "js", "coffee" then
157
+ "javascript"
158
+ when "css", "scss" then
159
+ "stylesheets"
160
+ end
161
+ File.join "lib", dir
162
+ end
163
+
164
+ def content_filename name, type
165
+ file_ext = type == "coffee" ? ".js.coffee" : "." + type
166
+ name + file_ext
167
+ end
168
+
169
+ def type_id type_codename
170
+ "Card::#{type_codename.to_s.camelcase}ID"
171
+ end
172
+
173
+ def rule_card_name category
174
+ DEFAULT_RULE[category]
175
+ end
176
+ end
177
+ end