modelscope 0.1.0 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 797e5f9be7613c91445fe7079db6889653f1807efb0c65fd571bdd4371e3dba9
4
- data.tar.gz: 12c425ccff813c02fcf3570cbb7da963da2b933397af1931460e27b51373771c
3
+ metadata.gz: 4e40c1dcb806b8b59a48b18a239a7160b6c816e61a173896bc1f5b233395d884
4
+ data.tar.gz: 3cb1b59b808eaa01eba216b5e53c4b7286a8aa145fdca08697835a8716c20621
5
5
  SHA512:
6
- metadata.gz: 78a6d989e9fe646b473b2e4518152ff77dd32b4d0b3adf2d29fe8bf6d86a395d068f8f4193d484ae748d86ecc7453fc0cb8d2376e52e3972c27791a0660a9030
7
- data.tar.gz: 307b3ee5b3fee4df6fcd06b7a52d4606cddf409f75faafabc1a5409bd64f892441f9742f4d22fc2009585acd5c5fd4645a2b3d45921d0c2dd19fcfbfed25bb24
6
+ metadata.gz: 37802c37e0d1cb44f696eaea22c8531f1433e543a15ae23a5a3e283c0bb31f5bb26b948f3558d25a37aa64a81ca9ebcb1129b18aec9a4147b04500e62812b12f
7
+ data.tar.gz: a1c81b68ae3d3b3dfe8ff9108216ce5ec554de75724d061b8d99d1f6b1af87956f04328bf020eb3a0a71723c82ee32e89853667b60865673a9b6311d4feca8f5
@@ -34,16 +34,7 @@ module ModelScope
34
34
  end
35
35
 
36
36
  def inherited?
37
- owner = callback_owner
38
- return false unless owner
39
-
40
- if validator?(@filter)
41
- @model != @defining_class
42
- else
43
- owner != @model &&
44
- !rails_module?(owner) &&
45
- owner != @defining_class
46
- end
37
+ @model != @defining_class
47
38
  end
48
39
 
49
40
  def conditional?
@@ -3,7 +3,8 @@
3
3
  module ModelScope
4
4
  class Callback
5
5
  attr_reader :model, :method_name, :conditional, :origin, :inherited, :kind,
6
- :association_generated, :attribute_generated
6
+ :association_generated, :attribute_generated, :callback, :defining_class,
7
+ :fingerprint
7
8
 
8
9
  def initialize(model:, rails_callback:, name:, defining_class:)
9
10
  @model = model
@@ -20,6 +21,11 @@ module ModelScope
20
21
  @inherited = analyzer.inherited?
21
22
  @association_generated = analyzer.association_generated?
22
23
  @attribute_generated = analyzer.attribute_generated?
24
+ # fingerprint allows us to de-duplicate callbacks/validations;
25
+ # in most cases, it's just an object_id, but for named validations/callbacks,
26
+ # it's a combination of the name, kind and the method_name.
27
+ # The "0" and "1" prefixes define how to handle duplicates (1 means last write wins, 0 means first write wins)
28
+ @fingerprint = (@method_name.is_a?(Symbol) && @origin != :rails) ? ["1", @name, @kind, @method_name].join("-") : "0-#{@callback.object_id}"
23
29
  end
24
30
 
25
31
  def callback_group
@@ -34,5 +40,17 @@ module ModelScope
34
40
  def human_method_name
35
41
  Analyzers::ValidationAnalyzer.human_method_name(@callback.filter)
36
42
  end
43
+
44
+ def to_s
45
+ [
46
+ "#{model.name}: #{human_method_name}",
47
+ "kind=#{kind}_#{callback_group}",
48
+ "origin=#{origin}",
49
+ inherited ? "inherited=true" : nil,
50
+ conditional ? "conditional=true" : nil,
51
+ association_generated ? "association=true" : nil,
52
+ attribute_generated ? "attribute=true" : nil
53
+ ].compact.join(" ")
54
+ end
37
55
  end
38
56
  end
@@ -14,8 +14,8 @@ module ModelScope
14
14
  @models = Set.new(models ? [*models] : ApplicationRecord.descendants)
15
15
  end
16
16
 
17
- def collect
18
- models.flat_map { |model| collect_for_model(model) }
17
+ def collect(select_models = models)
18
+ select_models.flat_map { |model| collect_for_model(model) }
19
19
  end
20
20
 
21
21
  private
@@ -27,7 +27,25 @@ module ModelScope
27
27
 
28
28
  def collect_for_model(model)
29
29
  model.ancestors.select { |ancestor| ancestor < ActiveRecord::Base }
30
+ # collect from parent to child to correctly handle inheritance
31
+ .reverse
30
32
  .flat_map { |ancestor| collect_callbacks_for_class(model, ancestor) }
33
+ .group_by(&:fingerprint)
34
+ # merge groups
35
+ .transform_values do |callbacks|
36
+ probe = callbacks.first
37
+ if probe.fingerprint.start_with?("1")
38
+ # we must keep the last non-matching callback (i.e., if all callbacks are the same,
39
+ # we must keep the first one)
40
+ callbacks.each do |clbk|
41
+ if clbk.callback != probe.callback
42
+ probe = clbk
43
+ end
44
+ end
45
+ end
46
+ probe
47
+ end
48
+ .values
31
49
  end
32
50
 
33
51
  def collect_callbacks_for_class(model, klass)
@@ -5,9 +5,9 @@ module ModelScope
5
5
  class Base
6
6
  attr_reader :callbacks
7
7
 
8
- def initialize(callbacks)
8
+ def initialize(callbacks, **opts)
9
9
  @callbacks = callbacks
10
- @stats = Stats.new(callbacks)
10
+ @stats = Stats.new(callbacks, **opts)
11
11
  end
12
12
 
13
13
  def generate
@@ -4,15 +4,17 @@ module ModelScope
4
4
  class Runner
5
5
  DEFAULT_FORMAT = :table
6
6
 
7
- def self.run(format: DEFAULT_FORMAT, model: nil, paths: nil, kind: :callbacks)
8
- new(format: format, model: model, paths: paths, kind: kind).run
7
+ def self.run(format: DEFAULT_FORMAT, model: nil, paths: nil, kind: :callbacks, **opts)
8
+ new(format: format, model: model, paths: paths, kind: kind, **opts).run
9
9
  end
10
10
 
11
- def initialize(format:, model:, paths:, kind: :callbacks)
11
+ def initialize(format:, model:, paths:, kind: :callbacks, sort_by: :size, sort_order: :desc)
12
12
  @format = (format || DEFAULT_FORMAT).to_sym
13
13
  @model_name = model
14
14
  @paths = paths
15
15
  @kind = kind
16
+ @sort_by = sort_by
17
+ @sort_order = sort_order
16
18
  end
17
19
 
18
20
  def run
@@ -32,7 +34,7 @@ module ModelScope
32
34
  end
33
35
 
34
36
  def generate_report(callbacks, ckind)
35
- find_reporter_class(ckind).new(callbacks).generate
37
+ find_reporter_class(ckind).new(callbacks, sort_by: @sort_by, sort_order: @sort_order).generate
36
38
  end
37
39
 
38
40
  def find_reporter_class(ckind)
@@ -7,15 +7,31 @@ module ModelScope
7
7
  association_generated attribute_generated
8
8
  ].freeze
9
9
 
10
+ SORT = %i[size name].freeze
11
+ SORT_ORDER = %i[desc asc].freeze
12
+
10
13
  attr_reader :callbacks
14
+ private attr_reader :sort_by, :sort_order
11
15
 
12
- def initialize(callbacks)
16
+ def initialize(callbacks, sort_by: :size, sort_order: :desc)
13
17
  @callbacks = callbacks
14
18
  @stats_cache = {}
19
+ @sort_by = sort_by
20
+ raise ArgumentError, "Invalid sort_by: #{@sort_by}. Available: #{SORT.join(", ")}" unless SORT.include?(@sort_by)
21
+ @sort_order = sort_order
22
+ raise ArgumentError, "Invalid sort_order: #{@sort_order}. Available: #{SORT_ORDER.join(", ")}" unless SORT_ORDER.include?(@sort_order)
15
23
  end
16
24
 
17
25
  def by_model
18
- @by_model ||= callbacks.group_by { |cb| cb.model.name }
26
+ @by_model ||= callbacks.group_by { |cb| cb.model.name }.sort_by do |name, callbacks|
27
+ if sort_by == :size
28
+ callbacks.size
29
+ elsif sort_by == :name
30
+ name
31
+ end
32
+ end.tap do |sorted|
33
+ sorted.reverse! if sort_order == :desc
34
+ end.to_h
19
35
  end
20
36
 
21
37
  def stats_for(model_name)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ModelScope
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
data/lib/modelscope.rb CHANGED
@@ -12,6 +12,16 @@ module ModelScope
12
12
  loader.setup
13
13
  end
14
14
  end
15
+
16
+ def self.collect_callbacks(*models)
17
+ collector = Collector.new
18
+ collector.collect(models)
19
+ end
20
+
21
+ def self.collect_validations(*models)
22
+ collector = Collector.new(kind: :validations)
23
+ collector.collect(models)
24
+ end
15
25
  end
16
26
 
17
27
  ModelScope.loader
@@ -13,6 +13,7 @@ namespace :modelscope do
13
13
  specified as constant
14
14
  name (MessageThread) or file path
15
15
  (message_thread, admin/message_thread)
16
+ sort=total|name Sort by score or name (default: score:desc)
16
17
  path=DIR1,DIR2 Additional model directories (comma-separated)
17
18
 
18
19
  Examples:
@@ -20,14 +21,24 @@ namespace :modelscope do
20
21
  rake modelscope:callbacks
21
22
  DESC
22
23
  task callbacks: :environment do
23
- paths = ENV["path"]&.split(",")&.map { |p| Rails.root.join(p) }
24
-
25
- puts ModelScope::Runner.run(
24
+ opts = {
25
+ kind: :callbacks,
26
26
  format: ENV["format"],
27
- model: ENV["model"],
28
- paths: paths,
29
- kind: :callbacks
30
- )
27
+ model: ENV["model"]
28
+ }.compact
29
+
30
+ if ENV["path"]
31
+ opts[:paths] = ENV["path"]&.split(",")&.map { |p| Rails.root.join(p) }
32
+ end
33
+
34
+ if ENV["sort"]
35
+ sort_by, sort_order = ENV["sort"].split(":")
36
+ opts[:sort_by] = sort_by.to_sym
37
+ sort_order ||= ((sort_by == "size") ? :desc : :asc)
38
+ opts[:sort_order] = sort_order.to_sym
39
+ end
40
+
41
+ puts ModelScope::Runner.run(**opts)
31
42
  end
32
43
 
33
44
  desc <<~DESC
@@ -39,6 +50,7 @@ namespace :modelscope do
39
50
  specified as constant
40
51
  name (MessageThread) or file path
41
52
  (message_thread, admin/message_thread)
53
+ sort=total|name Sort by score or name (default: score:desc)
42
54
  path=DIR1,DIR2 Additional model directories (comma-separated)
43
55
 
44
56
  Examples:
@@ -46,14 +58,24 @@ namespace :modelscope do
46
58
  rake modelscope:validations
47
59
  DESC
48
60
  task validations: :environment do
49
- paths = ENV["path"]&.split(",")&.map { |p| Rails.root.join(p) }
50
-
51
- puts ModelScope::Runner.run(
61
+ opts = {
62
+ kind: :validations,
52
63
  format: ENV["format"],
53
- model: ENV["model"],
54
- paths: paths,
55
- kind: :validations
56
- )
64
+ model: ENV["model"]
65
+ }.compact
66
+
67
+ if ENV["path"]
68
+ opts[:paths] = ENV["path"]&.split(",")&.map { |p| Rails.root.join(p) }
69
+ end
70
+
71
+ if ENV["sort"]
72
+ sort_by, sort_order = ENV["sort"].split(":")
73
+ opts[:sort_by] = sort_by.to_sym
74
+ sort_order ||= ((sort_by == "size") ? :desc : :asc)
75
+ opts[:sort_order] = sort_order.to_sym
76
+ end
77
+
78
+ puts ModelScope::Runner.run(**opts)
57
79
  end
58
80
 
59
81
  desc <<~DESC
@@ -65,16 +87,27 @@ namespace :modelscope do
65
87
  specified as constant
66
88
  name (MessageThread) or file path
67
89
  (message_thread, admin/message_thread)
90
+ sort=total|name Sort by score or name (default: score:desc)
68
91
  path=DIR1,DIR2 Additional model directories (comma-separated)
69
92
  DESC
70
93
  task report: :environment do
71
- paths = ENV["path"]&.split(",")&.map { |p| Rails.root.join(p) }
72
-
73
- puts ModelScope::Runner.run(
94
+ opts = {
95
+ kind: :report,
74
96
  format: ENV["format"],
75
- model: ENV["model"],
76
- paths: paths,
77
- kind: :report
78
- )
97
+ model: ENV["model"]
98
+ }.compact
99
+
100
+ if ENV["path"]
101
+ opts[:paths] = ENV["path"]&.split(",")&.map { |p| Rails.root.join(p) }
102
+ end
103
+
104
+ if ENV["sort"]
105
+ sort_by, sort_order = ENV["sort"].split(":")
106
+ opts[:sort_by] = sort_by.to_sym
107
+ sort_order ||= ((sort_by == "size") ? :desc : :asc)
108
+ opts[:sort_order] = sort_order.to_sym
109
+ end
110
+
111
+ puts ModelScope::Runner.run(**opts)
79
112
  end
80
113
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modelscope
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev