pg_objects 1.4.7 → 1.5.0

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.evilution.yml +74 -0
  3. data/.github/workflows/ci.yml +1 -1
  4. data/.gitignore +3 -0
  5. data/.rubocop.yml +13 -2
  6. data/CHANGELOG.md +78 -0
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +96 -78
  9. data/README.md +174 -3
  10. data/Rakefile +2 -0
  11. data/bin/benchmark +42 -7
  12. data/bin/console +1 -0
  13. data/lib/generators/pg_objects/install/install_generator.rb +2 -0
  14. data/lib/pg_objects/config.rb +53 -2
  15. data/lib/pg_objects/db_object.rb +6 -1
  16. data/lib/pg_objects/db_object_factory.rb +11 -3
  17. data/lib/pg_objects/logger.rb +34 -3
  18. data/lib/pg_objects/manager.rb +66 -15
  19. data/lib/pg_objects/parsed_object/aggregate.rb +9 -1
  20. data/lib/pg_objects/parsed_object/base.rb +38 -0
  21. data/lib/pg_objects/parsed_object/base_type.rb +17 -0
  22. data/lib/pg_objects/parsed_object/conversion.rb +9 -1
  23. data/lib/pg_objects/parsed_object/domain.rb +16 -0
  24. data/lib/pg_objects/parsed_object/enum_type.rb +16 -0
  25. data/lib/pg_objects/parsed_object/event_trigger.rb +3 -1
  26. data/lib/pg_objects/parsed_object/extension.rb +10 -0
  27. data/lib/pg_objects/parsed_object/function.rb +9 -1
  28. data/lib/pg_objects/parsed_object/index.rb +17 -0
  29. data/lib/pg_objects/parsed_object/materialized_view.rb +9 -1
  30. data/lib/pg_objects/parsed_object/operator.rb +9 -1
  31. data/lib/pg_objects/parsed_object/operator_class.rb +9 -1
  32. data/lib/pg_objects/parsed_object/policy.rb +10 -0
  33. data/lib/pg_objects/parsed_object/range_type.rb +16 -0
  34. data/lib/pg_objects/parsed_object/rule.rb +10 -0
  35. data/lib/pg_objects/parsed_object/sequence.rb +16 -0
  36. data/lib/pg_objects/parsed_object/table.rb +9 -1
  37. data/lib/pg_objects/parsed_object/text_search_parser.rb +9 -1
  38. data/lib/pg_objects/parsed_object/text_search_template.rb +9 -1
  39. data/lib/pg_objects/parsed_object/trigger.rb +3 -1
  40. data/lib/pg_objects/parsed_object/type.rb +9 -1
  41. data/lib/pg_objects/parsed_object/view.rb +9 -1
  42. data/lib/pg_objects/parsed_object.rb +11 -0
  43. data/lib/pg_objects/parsed_object_factory.rb +52 -90
  44. data/lib/pg_objects/parser.rb +28 -12
  45. data/lib/pg_objects/railtie.rb +2 -0
  46. data/lib/pg_objects/version.rb +3 -1
  47. data/lib/pg_objects/yaml_configurable.rb +25 -7
  48. data/lib/pg_objects.rb +54 -4
  49. data/lib/tasks/pg_objects_tasks.rake +33 -8
  50. data/pg_objects.gemspec +5 -3
  51. metadata +17 -20
  52. data/.github/copilot-instructions.md +0 -131
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Base class for parsed objects
3
5
  #
@@ -10,7 +12,43 @@ class PgObjects::ParsedObject::Base
10
12
  raise NotImplementedError
11
13
  end
12
14
 
15
+ # Schema-qualified name (+schema.name+) when a schema is present, otherwise
16
+ # the same as +name+. Used to disambiguate same-named objects across schemas.
17
+ def qualified_name
18
+ schema_name = schema
19
+ schema_name && !schema_name.empty? ? "#{schema_name}.#{name}" : name
20
+ end
21
+
13
22
  private
14
23
 
15
24
  attr_reader :stmt
25
+
26
+ # Schema of the object, or nil when unqualified. Subclasses that carry a
27
+ # schema override this; the rest inherit the unqualified default.
28
+ def schema
29
+ nil
30
+ end
31
+
32
+ # Returns the schema element of a pg_query name list (e.g. funcname,
33
+ # defnames) when the name is qualified (+schema.name+), otherwise nil.
34
+ def qualifier(list)
35
+ list[-2].string.sval if list.size > 1
36
+ end
37
+
38
+ # Wraps name extraction so a malformed statement (nil field, empty array)
39
+ # surfaces an explicit error with context. Only NoMethodErrors from
40
+ # dereferencing nil are wrapped; genuine programming errors propagate.
41
+ def extract_name
42
+ yield
43
+ rescue NoMethodError => e
44
+ raise unless nil_dereference?(e)
45
+
46
+ raise PgObjects::MalformedStatementError, "malformed statement for #{self.class}: #{e.message}"
47
+ end
48
+
49
+ def nil_dereference?(error)
50
+ error.receiver.nil?
51
+ rescue ArgumentError
52
+ false
53
+ end
16
54
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Base (scalar) TYPE object representation (CREATE TYPE with I/O functions or
5
+ # a shell CREATE TYPE without attributes) — parsed as a define_stmt.
6
+ #
7
+ class PgObjects::ParsedObject::BaseType < PgObjects::ParsedObject::Base
8
+ def name
9
+ extract_name { stmt.define_stmt.defnames.last.string.sval }
10
+ end
11
+
12
+ private
13
+
14
+ def schema
15
+ extract_name { qualifier(stmt.define_stmt.defnames) }
16
+ end
17
+ end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # CONVERSION object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::Conversion < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.create_conversion_stmt.conversion_name[0].string.sval
8
+ extract_name { stmt.create_conversion_stmt.conversion_name.last.string.sval }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { qualifier(stmt.create_conversion_stmt.conversion_name) }
7
15
  end
8
16
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # DOMAIN object representation (CREATE DOMAIN)
5
+ #
6
+ class PgObjects::ParsedObject::Domain < PgObjects::ParsedObject::Base
7
+ def name
8
+ extract_name { stmt.create_domain_stmt.domainname.last.string.sval }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { qualifier(stmt.create_domain_stmt.domainname) }
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # ENUM TYPE object representation (CREATE TYPE ... AS ENUM)
5
+ #
6
+ class PgObjects::ParsedObject::EnumType < PgObjects::ParsedObject::Base
7
+ def name
8
+ extract_name { stmt.create_enum_stmt.type_name.last.string.sval }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { qualifier(stmt.create_enum_stmt.type_name) }
15
+ end
16
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # EVENT TRIGGER object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::EventTrigger < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.create_event_trig_stmt.trigname
8
+ extract_name { stmt.create_event_trig_stmt.trigname }
7
9
  end
8
10
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # EXTENSION object representation
5
+ #
6
+ class PgObjects::ParsedObject::Extension < PgObjects::ParsedObject::Base
7
+ def name
8
+ extract_name { stmt.create_extension_stmt.extname }
9
+ end
10
+ end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # FUNCTION object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::Function < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.create_function_stmt.funcname[0].string.sval
8
+ extract_name { stmt.create_function_stmt.funcname.last.string.sval }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { qualifier(stmt.create_function_stmt.funcname) }
7
15
  end
8
16
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # INDEX object representation
5
+ #
6
+ class PgObjects::ParsedObject::Index < PgObjects::ParsedObject::Base
7
+ def name
8
+ extract_name { stmt.index_stmt.idxname }
9
+ end
10
+
11
+ private
12
+
13
+ # An index lives in the schema of the table it indexes.
14
+ def schema
15
+ extract_name { stmt.index_stmt.relation.schemaname }
16
+ end
17
+ end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # MATERIALIZED VIEW object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::MaterializedView < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.create_table_as_stmt.into.rel.relname
8
+ extract_name { stmt.create_table_as_stmt.into.rel.relname }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { stmt.create_table_as_stmt.into.rel.schemaname }
7
15
  end
8
16
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # OPERATOR object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::Operator < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.define_stmt.defnames[0].string.sval
8
+ extract_name { stmt.define_stmt.defnames.last.string.sval }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { qualifier(stmt.define_stmt.defnames) }
7
15
  end
8
16
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # OPERATOR CLASS object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::OperatorClass < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.create_op_class_stmt.opclassname[0].string.sval
8
+ extract_name { stmt.create_op_class_stmt.opclassname.last.string.sval }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { qualifier(stmt.create_op_class_stmt.opclassname) }
7
15
  end
8
16
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # POLICY object representation
5
+ #
6
+ class PgObjects::ParsedObject::Policy < PgObjects::ParsedObject::Base
7
+ def name
8
+ extract_name { stmt.create_policy_stmt.policy_name }
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # RANGE TYPE object representation (CREATE TYPE ... AS RANGE)
5
+ #
6
+ class PgObjects::ParsedObject::RangeType < PgObjects::ParsedObject::Base
7
+ def name
8
+ extract_name { stmt.create_range_stmt.type_name.last.string.sval }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { qualifier(stmt.create_range_stmt.type_name) }
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # RULE object representation
5
+ #
6
+ class PgObjects::ParsedObject::Rule < PgObjects::ParsedObject::Base
7
+ def name
8
+ extract_name { stmt.rule_stmt.rulename }
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # SEQUENCE object representation
5
+ #
6
+ class PgObjects::ParsedObject::Sequence < PgObjects::ParsedObject::Base
7
+ def name
8
+ extract_name { stmt.create_seq_stmt.sequence.relname }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { stmt.create_seq_stmt.sequence.schemaname }
15
+ end
16
+ end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # TABLE object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::Table < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.create_stmt.relation.relname
8
+ extract_name { stmt.create_stmt.relation.relname }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { stmt.create_stmt.relation.schemaname }
7
15
  end
8
16
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # TEXT SEARCH PARSER object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::TextSearchParser < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.define_stmt.defnames[0].string.sval
8
+ extract_name { stmt.define_stmt.defnames.last.string.sval }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { qualifier(stmt.define_stmt.defnames) }
7
15
  end
8
16
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # TEXT SEARCH TEMPLATE object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::TextSearchTemplate < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.define_stmt.defnames[0].string.sval
8
+ extract_name { stmt.define_stmt.defnames.last.string.sval }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { qualifier(stmt.define_stmt.defnames) }
7
15
  end
8
16
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # TRIGGER object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::Trigger < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.create_trig_stmt.trigname
8
+ extract_name { stmt.create_trig_stmt.trigname }
7
9
  end
8
10
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # TYPE object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::Type < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.composite_type_stmt.typevar.relname
8
+ extract_name { stmt.composite_type_stmt.typevar.relname }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { stmt.composite_type_stmt.typevar.schemaname }
7
15
  end
8
16
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # VIEW object representation
3
5
  #
4
6
  class PgObjects::ParsedObject::View < PgObjects::ParsedObject::Base
5
7
  def name
6
- stmt.view_stmt.view.relname
8
+ extract_name { stmt.view_stmt.view.relname }
9
+ end
10
+
11
+ private
12
+
13
+ def schema
14
+ extract_name { stmt.view_stmt.view.schemaname }
7
15
  end
8
16
  end
@@ -1,14 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PgObjects::ParsedObject # rubocop: disable Style/Documentation
2
4
  end
3
5
 
4
6
  require 'pg_objects/parsed_object/base'
5
7
  require 'pg_objects/parsed_object/aggregate'
8
+ require 'pg_objects/parsed_object/base_type'
6
9
  require 'pg_objects/parsed_object/conversion'
10
+ require 'pg_objects/parsed_object/domain'
11
+ require 'pg_objects/parsed_object/enum_type'
7
12
  require 'pg_objects/parsed_object/event_trigger'
13
+ require 'pg_objects/parsed_object/extension'
8
14
  require 'pg_objects/parsed_object/function'
15
+ require 'pg_objects/parsed_object/index'
9
16
  require 'pg_objects/parsed_object/materialized_view'
10
17
  require 'pg_objects/parsed_object/operator'
11
18
  require 'pg_objects/parsed_object/operator_class'
19
+ require 'pg_objects/parsed_object/policy'
20
+ require 'pg_objects/parsed_object/range_type'
21
+ require 'pg_objects/parsed_object/rule'
22
+ require 'pg_objects/parsed_object/sequence'
12
23
  require 'pg_objects/parsed_object/table'
13
24
  require 'pg_objects/parsed_object/text_search_parser'
14
25
  require 'pg_objects/parsed_object/text_search_template'
@@ -1,103 +1,65 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Returns an object of the respective class based on the provided parsed query
3
5
  #
4
6
  class PgObjects::ParsedObjectFactory
5
- class << self
6
- include Dry::Monads[:try, :result]
7
-
8
- SUPPORTED_TYPES = %i[
9
- aggregate
10
- conversion
11
- event_trigger
12
- function
13
- materialized_view
14
- operator
15
- operator_class
16
- table
17
- text_search_parser
18
- text_search_template
19
- trigger
20
- type
21
- view
22
- ].freeze
23
-
24
- def create_object(input_data)
25
- @input_data = input_data
26
- @stmt = input_data.tree.stmts[0].stmt
27
-
28
- determine_class.new(stmt)
29
- end
30
-
31
- private
32
-
33
- attr_reader :stmt, :input_data
34
-
35
- def determine_class
36
- SUPPORTED_TYPES.each do |type|
37
- return class_for(type) if send("#{type}?")
38
- end
39
- end
40
-
41
- def class_for(type)
42
- "PgObjects::ParsedObject::#{type.to_s.classify}".constantize
43
- end
44
-
45
- def aggregate?
46
- try_check_get_result { stmt.define_stmt.kind == :OBJECT_AGGREGATE }
47
- end
48
-
49
- def conversion?
50
- try_check_get_result { stmt.create_conversion_stmt.conversion_name.present? }
51
- end
52
-
53
- def event_trigger?
54
- try_check_get_result { stmt.create_event_trig_stmt.trigname.present? }
55
- end
56
-
57
- def function?
58
- try_check_get_result { stmt.create_function_stmt.funcname.present? }
59
- end
60
-
61
- def materialized_view?
62
- try_check_get_result { stmt.create_table_as_stmt.objtype == :OBJECT_MATVIEW }
63
- end
64
-
65
- def operator_class?
66
- try_check_get_result { stmt.create_op_class_stmt.opclassname.present? }
67
- end
68
-
69
- def operator?
70
- try_check_get_result { stmt.define_stmt.kind == :OBJECT_OPERATOR }
71
- end
72
-
73
- def table?
74
- try_check_get_result { stmt.create_stmt.table_elts.present? }
75
- end
76
-
77
- def text_search_parser?
78
- try_check_get_result { stmt.define_stmt.kind == :OBJECT_TSPARSER }
79
- end
7
+ DISPATCH = {
8
+ composite_type_stmt: PgObjects::ParsedObject::Type,
9
+ create_conversion_stmt: PgObjects::ParsedObject::Conversion,
10
+ create_domain_stmt: PgObjects::ParsedObject::Domain,
11
+ create_enum_stmt: PgObjects::ParsedObject::EnumType,
12
+ create_event_trig_stmt: PgObjects::ParsedObject::EventTrigger,
13
+ create_extension_stmt: PgObjects::ParsedObject::Extension,
14
+ create_function_stmt: PgObjects::ParsedObject::Function,
15
+ create_op_class_stmt: PgObjects::ParsedObject::OperatorClass,
16
+ create_policy_stmt: PgObjects::ParsedObject::Policy,
17
+ create_range_stmt: PgObjects::ParsedObject::RangeType,
18
+ create_seq_stmt: PgObjects::ParsedObject::Sequence,
19
+ create_stmt: PgObjects::ParsedObject::Table,
20
+ create_trig_stmt: PgObjects::ParsedObject::Trigger,
21
+ index_stmt: PgObjects::ParsedObject::Index,
22
+ rule_stmt: PgObjects::ParsedObject::Rule,
23
+ view_stmt: PgObjects::ParsedObject::View
24
+ }.freeze
25
+
26
+ DEFINE_STMT_DISPATCH = {
27
+ OBJECT_AGGREGATE: PgObjects::ParsedObject::Aggregate,
28
+ OBJECT_OPERATOR: PgObjects::ParsedObject::Operator,
29
+ OBJECT_TSPARSER: PgObjects::ParsedObject::TextSearchParser,
30
+ OBJECT_TSTEMPLATE: PgObjects::ParsedObject::TextSearchTemplate,
31
+ OBJECT_TYPE: PgObjects::ParsedObject::BaseType
32
+ }.freeze
33
+
34
+ def self.create_object(input_data)
35
+ new(input_data).create_object
36
+ end
80
37
 
81
- def text_search_template?
82
- try_check_get_result { stmt.define_stmt.kind == :OBJECT_TSTEMPLATE }
83
- end
38
+ def initialize(input_data)
39
+ @input_data = input_data
40
+ @stmt = input_data.tree.stmts[0].stmt
41
+ end
84
42
 
85
- def trigger?
86
- try_check_get_result { stmt.create_trig_stmt.trigname.present? }
87
- end
43
+ def create_object
44
+ determine_class.new(stmt)
45
+ end
88
46
 
89
- def type?
90
- try_check_get_result { stmt.composite_type_stmt.typevar.present? }
91
- end
47
+ private
92
48
 
93
- def view?
94
- try_check_get_result { stmt.view_stmt.view.present? }
95
- end
49
+ attr_reader :stmt, :input_data
96
50
 
97
- def try_check_get_result(&)
98
- result = Try(&).to_result
51
+ def determine_class
52
+ dispatch_class || raise(PgObjects::UnknownObjectTypeError, "unknown object type: #{stmt.node}")
53
+ end
99
54
 
100
- result.success? && result.value!
55
+ def dispatch_class
56
+ case stmt.node
57
+ when :define_stmt
58
+ DEFINE_STMT_DISPATCH[stmt.define_stmt.kind]
59
+ when :create_table_as_stmt
60
+ PgObjects::ParsedObject::MaterializedView if stmt.create_table_as_stmt.objtype == :OBJECT_MATVIEW
61
+ else
62
+ DISPATCH[stmt.node]
101
63
  end
102
64
  end
103
65
  end
@@ -1,20 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pg_query'
2
4
 
3
5
  ##
4
6
  # Reads directives from SQL-comments
5
7
  #
6
- # --!depends_on [name_of_dependency]
8
+ # --!depends_on name_a
9
+ # --!depends_on name_a, name_b, name_c
10
+ # #!depends_on name_a
11
+ #
12
+ # The directive must start the line (no leading whitespace). Dependencies may
13
+ # be listed on one line separated by commas and/or whitespace, or across
14
+ # several directive lines.
7
15
  #
8
16
  # name_of_dependency: short or full name of object as well as object_name
9
17
  #
10
18
  class PgObjects::Parser
11
19
  include Import['parsed_object_factory']
12
- include Memery
13
20
 
14
- PG_ENTITIES = %i[operator_class trigger define_statement conversion event_trigger type function table].freeze
21
+ DEPENDS_ON_DIRECTIVE = /\A(?:--|#)!depends_on\s+(.+)/
15
22
 
16
23
  def load(source)
17
24
  @source = source
25
+ @parsed = nil
26
+ @parsed_object = nil
18
27
  self
19
28
  end
20
29
 
@@ -25,26 +34,33 @@ class PgObjects::Parser
25
34
  end
26
35
 
27
36
  def fetch_object_name
28
- parse_query
29
37
  parsed_object.name
30
- rescue PgQuery::ParseError, NoMethodError
38
+ rescue PgQuery::ParseError, PgObjects::UnknownObjectTypeError
31
39
  nil
32
40
  end
33
41
 
34
- private
42
+ def fetch_qualified_object_name
43
+ parsed_object.qualified_name
44
+ rescue PgQuery::ParseError, PgObjects::UnknownObjectTypeError
45
+ nil
46
+ end
35
47
 
36
- attr_reader :parsed
48
+ private
37
49
 
38
- def parse_query
39
- @parsed = PgQuery.parse(@source)
50
+ # Parse once per loaded source; both fetch_* methods reuse the result and
51
+ # +load+ clears it, so a new source is always reparsed (no stale cache).
52
+ def parsed
53
+ @parsed ||= PgQuery.parse(@source)
40
54
  end
41
55
 
42
- memoize
43
56
  def parsed_object
44
- parsed_object_factory.create_object(parsed)
57
+ @parsed_object ||= parsed_object_factory.create_object(parsed)
45
58
  end
46
59
 
47
60
  def fetch_dependencies
48
- @source.split("\n").grep(/^(--|#)!/).map { |ln| ln.split[1] if ln =~ /!depends_on/ }.compact
61
+ @source.each_line.flat_map do |line|
62
+ match = DEPENDS_ON_DIRECTIVE.match(line)
63
+ match ? match[1].split(/[\s,]+/).reject(&:empty?) : []
64
+ end
49
65
  end
50
66
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  ##
2
4
  # Brings rake tasks to rails app
3
5
  class PgObjects::Railtie < Rails::Railtie
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PgObjects
2
- VERSION = '1.4.7'.freeze
4
+ VERSION = '1.5.0'
3
5
  end