rails-erd 2.0.1 → 2.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96a5321997e7b69697914d2555515de577676734e66f60cf0b76ff6c6f307846
4
- data.tar.gz: 973f6409aeb4fcd221659e4c9dabadd4a32a047218ea056b4b24b9c71476454c
3
+ metadata.gz: 4d47dd705b783be29e06d39d8bea92d6c5ac8019fba658e0279287b2197938d3
4
+ data.tar.gz: 1417646a7d2731ad2964e9cbac400cb3b2325f765ba1db610b44f28ffe03f699
5
5
  SHA512:
6
- metadata.gz: c5b98489174be741cf8056040f3a6eadd2c5ab673dfb9463b2db0b4fe7e9209a742765464b02c842b859c700231285cae40cfbcbe4eca3bed24d3cf1a191b453
7
- data.tar.gz: 3793cbe2da98c90a4e379864e7dd5e3d4072248ff8245dfa5dfc75d0957e9c9e1f6aab4140ec54af00010312c53791e513e3898b41e2669c365802151497d9aa
6
+ metadata.gz: cde6a9b96a0d3e5c03ccbed1c3e04e37d27efb3a37d2a09e1a5ff298cbb4e9ec3148065c551efe8da349315d7601420b3b98bccf049f4c83b782863a6a13f495
7
+ data.tar.gz: 5d53400f939b341b02becb90a5b4b413d4556a0fc36ef856d4c831d13ccc343ab0fd48772c8245ebedcdcdfee028068d51b1a09027ae809614c568bf19506505
data/README.md CHANGED
@@ -70,6 +70,7 @@ sort: true
70
70
  warn: true
71
71
  title: true
72
72
  exclude: null
73
+ exclude_attributes: null
73
74
  only: null
74
75
  only_recursion_depth: null
75
76
  prepend_primary: false
@@ -77,6 +78,32 @@ cluster: false
77
78
  splines: spline
78
79
  ```
79
80
 
81
+ ### Hiding attributes for specific models
82
+
83
+ While `attributes: false` hides attributes for *every* model, `exclude_attributes`
84
+ lets you hide attributes for individual models without affecting the others. Map a
85
+ model name to `true` to hide all of its attributes, or to a list of attribute names
86
+ to hide only those:
87
+
88
+ ```yaml
89
+ exclude_attributes:
90
+ BigTable: true # hide all attributes for BigTable
91
+ User: # hide only these attributes for User
92
+ - password_digest
93
+ - remember_token
94
+ ```
95
+
96
+ From the command line, pass a comma separated list where each entry is either
97
+ `Model` (hide all of its attributes) or `Model.attribute` (hide a single one):
98
+
99
+ ```bash
100
+ # rake task (bare key=value)
101
+ bundle exec rake erd exclude_attributes="BigTable,User.password_digest,User.remember_token"
102
+
103
+ # erd binary (flags require the -- prefix)
104
+ bundle exec erd --exclude_attributes="BigTable,User.password_digest,User.remember_token"
105
+ ```
106
+
80
107
  You can also customize fonts (useful if the defaults aren't available on your system):
81
108
 
82
109
  ```yaml
data/lib/rails_erd/cli.rb CHANGED
@@ -72,6 +72,11 @@ Choice.options do
72
72
  desc "Filter to exclude listed models in diagram."
73
73
  end
74
74
 
75
+ option :exclude_attributes do
76
+ long "--exclude_attributes=MODEL[.ATTRIBUTE],..."
77
+ desc "Hide attributes per model. Use Model to hide all its attributes or Model.attribute to hide one, e.g. BigTable,User.password_digest."
78
+ end
79
+
75
80
  option :sort do
76
81
  long "--sort=BOOLEAN"
77
82
  desc "Sort attribute list alphabetically"
@@ -73,6 +73,11 @@ module RailsERD
73
73
  when :only, :exclude
74
74
  Array(value).join(",").split(",").map { |v| v.strip }
75
75
 
76
+ # nil | { <string> => true | [<string>] }
77
+ when :exclude_attributes
78
+ require "rails_erd/diagram"
79
+ RailsERD::Diagram.normalize_exclude_attributes(value)
80
+
76
81
  # true | false
77
82
  when :disconnected, :indirect, :inheritance, :markup, :polymorphism,
78
83
  :warn, :cluster
@@ -55,6 +55,14 @@ module RailsERD
55
55
  # attributes:: Selects which attributes to display. Can be any combination of
56
56
  # +:content+, +:primary_keys+, +:foreign_keys+, +:timestamps+, or
57
57
  # +:inheritance+.
58
+ # exclude_attributes:: Hides attributes on a per-model basis, without affecting
59
+ # other models. Accepts a hash that maps model names to
60
+ # either +true+ (hide all attributes for that model) or a
61
+ # list of attribute names to hide. From the command line it
62
+ # can also be given as a comma separated string where each
63
+ # entry is either +Model+ (hide all attributes) or
64
+ # +Model.attribute+ (hide a single attribute), for example
65
+ # <tt>exclude_attributes="BigTable,User.password_digest"</tt>.
58
66
  # disconnected:: Set to +false+ to exclude entities that are not connected to other
59
67
  # entities. Defaults to +false+.
60
68
  # indirect:: Set to +false+ to exclude relationships that are indirect.
@@ -76,6 +84,38 @@ module RailsERD
76
84
  new(Domain.generate(options), options).create
77
85
  end
78
86
 
87
+ # Canonicalises the +exclude_attributes+ option into a hash that maps
88
+ # model names (as strings) to either +true+ (hide all attributes) or an
89
+ # array of attribute names to hide. Accepts several input shapes:
90
+ #
91
+ # * a hash, e.g. <tt>{ "BigTable" => true, "User" => ["password_digest"] }</tt>
92
+ # (values may be +true+/+"all"+ to hide every attribute, +false+/+nil+
93
+ # to hide none, or a comma separated string / array of names);
94
+ # * a string or array of <tt>Model</tt> / <tt>Model.attribute</tt>
95
+ # entries, e.g. <tt>"BigTable,User.password_digest"</tt>.
96
+ def normalize_exclude_attributes(value)
97
+ return {} if value.nil? || value == false
98
+
99
+ if value.is_a?(Hash)
100
+ value.each_with_object({}) do |(model, attrs), result|
101
+ result[model.to_s] = normalize_exclude_attribute_value(attrs)
102
+ end
103
+ else
104
+ entries = Array(value).flat_map { |entry| entry.to_s.split(",") }
105
+ entries.each_with_object({}) do |entry, result|
106
+ model, attribute = entry.split(".", 2)
107
+ model = model.to_s.strip
108
+ next if model.empty?
109
+
110
+ if attribute.nil?
111
+ result[model] = true
112
+ elsif result[model] != true
113
+ (result[model] ||= []) << attribute.strip
114
+ end
115
+ end
116
+ end
117
+ end
118
+
79
119
  protected
80
120
 
81
121
  def setup(&block)
@@ -103,6 +143,16 @@ module RailsERD
103
143
  def callbacks
104
144
  @callbacks ||= Hash.new { proc {} }
105
145
  end
146
+
147
+ def normalize_exclude_attribute_value(attrs)
148
+ case attrs
149
+ when true then true
150
+ when false, nil then []
151
+ when "all", :all, "true" then true
152
+ else
153
+ Array(attrs).flat_map { |attr| attr.to_s.split(",") }.map(&:strip).reject(&:empty?)
154
+ end
155
+ end
106
156
  end
107
157
 
108
158
  # The options that are used to create this diagram.
@@ -205,13 +255,31 @@ module RailsERD
205
255
  end
206
256
 
207
257
  def filtered_attributes(entity)
258
+ excluded = excluded_attributes_for(entity)
259
+ return [] if excluded == :all
260
+
208
261
  entity.attributes.reject { |attribute|
262
+ # Hide attributes excluded for this specific model.
263
+ excluded.include?(attribute.name) or
209
264
  # Select attributes that satisfy the conditions in the :attributes option.
210
265
  !options.attributes or entity.specialized? or
211
266
  [*options.attributes].none? { |type| attribute.send(:"#{type.to_s.chomp('s')}?") }
212
267
  }
213
268
  end
214
269
 
270
+ # Returns the attribute exclusions configured for the given entity through
271
+ # the +exclude_attributes+ option. Returns +:all+ when every attribute
272
+ # should be hidden, or an array of attribute names otherwise.
273
+ def excluded_attributes_for(entity)
274
+ spec = normalized_exclude_attributes[entity.name]
275
+ return :all if spec == true
276
+ Array(spec)
277
+ end
278
+
279
+ def normalized_exclude_attributes
280
+ @normalized_exclude_attributes ||= self.class.normalize_exclude_attributes(options.exclude_attributes)
281
+ end
282
+
215
283
  def warn(message)
216
284
  puts "Warning: #{message}" if options.warn
217
285
  end
@@ -210,11 +210,13 @@ module RailsERD
210
210
  excluded_names = [options.exclude].flatten.map(&:to_sym)
211
211
 
212
212
  # Suppress warning if either the source model or target model is excluded
213
- excluded_names.include?(association.active_record.name.to_sym) ||
214
- (association.klass.name && excluded_names.include?(association.klass.name.to_sym))
213
+ return true if excluded_names.include?(association.active_record.name.to_sym)
214
+
215
+ target_name = association.options[:polymorphic] ? association.class_name : association.klass.name
216
+ target_name && excluded_names.include?(target_name.to_sym)
215
217
  rescue NameError
216
- # If we can't determine the target class, check only the source model
217
- excluded_names.include?(association.active_record.name.to_sym)
218
+ # If we can't determine the target class, the source model was already checked above
219
+ false
218
220
  end
219
221
 
220
222
  def check_polymorphic_association_validity(association)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsERD
4
- VERSION = "2.0.1"
4
+ VERSION = "2.0.2"
5
5
  BANNER = "RailsERD #{VERSION}"
6
6
  end
data/lib/rails_erd.rb CHANGED
@@ -55,6 +55,7 @@ module RailsERD
55
55
  :warn, true,
56
56
  :title, true,
57
57
  :exclude, nil,
58
+ :exclude_attributes, nil,
58
59
  :only, nil,
59
60
  :only_recursion_depth, nil,
60
61
  :prepend_primary, false,
@@ -122,6 +122,17 @@ class ConfigTest < ActiveSupport::TestCase
122
122
  assert_equal [:content, :primary_keys], normalize_value(:attributes, ["content", "primary_keys"])
123
123
  end
124
124
 
125
+ test "normalize_value should canonicalize a hash when key is :exclude_attributes." do
126
+ value = { "BigTable" => true, "User" => ["password_digest"] }
127
+ expected = { "BigTable" => true, "User" => ["password_digest"] }
128
+ assert_equal expected, normalize_value(:exclude_attributes, value)
129
+ end
130
+
131
+ test "normalize_value should parse a string when key is :exclude_attributes." do
132
+ expected = { "BigTable" => true, "User" => ["password_digest"] }
133
+ assert_equal expected, normalize_value(:exclude_attributes, "BigTable,User.password_digest")
134
+ end
135
+
125
136
  test "normalize_value should return hash with symbol keys when key is :fonts and value is a hash." do
126
137
  fonts_value = { "normal" => "Arial", "bold" => "Arial Bold", "italic" => "Arial Italic" }
127
138
  expected = {:normal => "Arial", :bold => "Arial Bold", :italic => "Arial Italic"}
@@ -358,4 +358,34 @@ class DiagramTest < ActiveSupport::TestCase
358
358
  Object.const_set :Whisky, Class.new(Beverage)
359
359
  assert_equal [], retrieve_attribute_lists(:inheritance => true)[Whisky].map(&:name)
360
360
  end
361
+
362
+ test "generate should hide all attributes for a model excluded with true" do
363
+ create_model "Book", :title => :string, :pages => :integer
364
+ create_model "Author", :name => :string
365
+ attribute_lists = retrieve_attribute_lists(:exclude_attributes => { "Book" => true })
366
+ assert_equal [], attribute_lists[Book].map(&:name)
367
+ assert_equal %w{name}, attribute_lists[Author].map(&:name)
368
+ end
369
+
370
+ test "generate should hide only listed attributes for a model" do
371
+ create_model "Book", :title => :string, :subtitle => :string, :pages => :integer
372
+ attribute_lists = retrieve_attribute_lists(:exclude_attributes => { "Book" => ["subtitle"] })
373
+ assert_equal %w{pages title}, attribute_lists[Book].map(&:name).sort
374
+ end
375
+
376
+ test "generate should hide attributes for the listed model only" do
377
+ create_model "Book", :title => :string
378
+ create_model "Author", :name => :string
379
+ attribute_lists = retrieve_attribute_lists(:exclude_attributes => { "Book" => ["title"] })
380
+ assert_equal [], attribute_lists[Book].map(&:name)
381
+ assert_equal %w{name}, attribute_lists[Author].map(&:name)
382
+ end
383
+
384
+ test "generate should accept exclude_attributes as a string" do
385
+ create_model "Book", :title => :string, :subtitle => :string
386
+ create_model "Author", :name => :string
387
+ attribute_lists = retrieve_attribute_lists(:exclude_attributes => "Book.subtitle,Author")
388
+ assert_equal %w{title}, attribute_lists[Book].map(&:name)
389
+ assert_equal [], attribute_lists[Author].map(&:name)
390
+ end
361
391
  end
@@ -255,6 +255,19 @@ class DomainTest < ActiveSupport::TestCase
255
255
  assert_match(/polymorphic interface FooBar does not exist/, output)
256
256
  end
257
257
 
258
+ test "relationships should not crash when exclude is present and a polymorphic association's interface does not exist" do
259
+ create_model "Foo" do
260
+ has_many :bars, :as => :foo
261
+ end
262
+ create_model "Bar", :foobar => :references do
263
+ belongs_to :foo_bar, :polymorphic => true
264
+ end
265
+ output = collect_stdout do
266
+ Domain.generate(:exclude => ["Qux"]).relationships
267
+ end
268
+ assert_match(/polymorphic interface FooBar does not exist/, output)
269
+ end
270
+
258
271
  test "relationships should not warn when a bad association is encountered if warnings are disabled" do
259
272
  create_model "Foo" do
260
273
  has_many :flabs
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-erd
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rolf Timmermans
@@ -178,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
178
  - !ruby/object:Gem::Version
179
179
  version: '0'
180
180
  requirements: []
181
- rubygems_version: 4.0.10
181
+ rubygems_version: 4.0.13
182
182
  specification_version: 4
183
183
  summary: Entity-relationship diagram for your Rails models.
184
184
  test_files: