statesman-trigger 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f9ae2e9640d93d968e583f6ed5dcf8c80a4f85ff
4
- data.tar.gz: 5796a36556bad99d890174522ed94ca295161a99
3
+ metadata.gz: 4aa120ba58e0a9739d02b5ccc9838fa4c88cd6e8
4
+ data.tar.gz: bb1f4c91f67b04f963f316566fa27188a9fd022b
5
5
  SHA512:
6
- metadata.gz: 0fb83a8b14e76e37c475d7141efb317487472f2e02f296cd32ab81bb6c7cb25ab2d5e051ceb8d4ea4635ac4c836cc74320da8bf6dc2d9f9d63bfdf52d6752f97
7
- data.tar.gz: 550682de13db79df2da3b08fce45e810f9dd27f764c29bdb77ff03a71ada73ac45caaa95c9e7c8d6b5c0946b97d20282f2eb9f6f2541de7462d277148ac978e7
6
+ metadata.gz: ce618ee125c93a5150dcd151cbb10d1969240f472707d8c739829bf5fd7fe879a5b8c2afce5ef2234f857aac89befe955aea455e30f4ce6603d4b53fb99be531
7
+ data.tar.gz: d72cb76d9ead48c4cd7f0ee87dc1bbb5c443487a3e2cfd21d85779f8574269fd041b6bf2d8baa3cd71a33c1982a231300f598e112fbc139a56dc7999fcd728b7
@@ -0,0 +1,8 @@
1
+ --private
2
+ --protected
3
+ -m markdown
4
+ lib/**/*.rb
5
+ -
6
+ README.md
7
+ CODE_OF_CONDUCT.md
8
+ LICENSE.txt
@@ -0,0 +1,15 @@
1
+ # @markup markdown
2
+ # @title Contributor Code of Conduct
3
+ # Contributor Code of Conduct
4
+
5
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
6
+
7
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
8
+
9
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
10
+
11
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
12
+
13
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
14
+
15
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Alexa Grey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,12 +1,18 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
- begin
4
- require 'rspec/core/rake_task'
3
+ require 'yard'
4
+ require 'rspec/core/rake_task'
5
+ require 'yard/rake/yardoc_task'
5
6
 
6
- RSpec::Core::RakeTask.new(:spec) do |t|
7
- t.verbose = false
8
- end
9
- rescue LoadError
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.verbose = false
9
+ end
10
+
11
+
12
+ YARD::Rake::YardocTask.new do |t|
13
+ t.files = ['lib/**/*.rb', '-', 'README.md', 'LICENSE.txt', 'CODE_OF_CONDUCT.md']
14
+ t.options = ['--private', '--protected']
15
+ t.stats_options = ['--list-undoc']
10
16
  end
11
17
 
12
18
  task :default => :spec
@@ -5,19 +5,22 @@ require "pg"
5
5
  require "virtus"
6
6
  require "dedent"
7
7
 
8
+ # Namespace for [Statesman gem](https://github.com/gocardless/statesman).
8
9
  module Statesman
10
+ # Namespace for this gem.
9
11
  module Trigger
12
+ # Raised when a value can't be introspected
13
+ class IntrospectionError < NoMethodError; end
14
+
10
15
  require_relative './trigger/null_object'
11
16
  require_relative './trigger/parameters'
12
17
  require_relative './trigger/abstract_query'
13
18
  require_relative './trigger/trigger_query'
14
19
  require_relative './trigger/update_function_query'
15
20
  require_relative './trigger/integration'
16
- require_relative './trigger/migrations'
17
21
  end
18
22
  end
19
23
 
20
24
  ActiveSupport.on_load :active_record do
21
- ActiveRecord::ConnectionAdapters::AbstractAdapter.include Statesman::Trigger::Integration
22
- ActiveRecord::Migration::CommandRecorder.include Statesman::Trigger::Migrations
25
+ Statesman::Trigger::Integration.install!
23
26
  end
@@ -8,32 +8,47 @@ module Statesman
8
8
  # @return [Statesman::Trigger::Parameters]
9
9
  attr_reader :parameters
10
10
 
11
+ # @param [Statesman::Trigger::Parameters] parameters
11
12
  def initialize(parameters)
12
13
  @parameters = parameters
13
14
  end
14
15
 
16
+ # @return [String] the formatted {#up_query} based on {#parameters}.
15
17
  def up
16
18
  up_query % parameters.for_query
17
19
  end
18
20
 
21
+ # @return [String] the formatted {#down_query} based on {#parameters}.
19
22
  def down
20
23
  down_query % parameters.for_query
21
24
  end
22
25
 
23
26
  protected
27
+ # @!attribute [r] up_query
28
+ # @return [String]
24
29
  def up_query
25
30
  config.up_query.presence or raise 'Define up!'
26
31
  end
27
32
 
33
+ # @!attribute [r] down_query
34
+ # @return [String]
28
35
  def down_query
29
36
  config.down_query.presence or raise 'Define down!'
30
37
  end
31
38
 
32
39
  class << self
40
+ # Set the {#up_query} SQL for this class.
41
+ #
42
+ # @param [String] raw_sql
43
+ # @return [void]
33
44
  def up!(raw_sql)
34
45
  config.up_query = raw_sql.dedent
35
46
  end
36
47
 
48
+ # Set the {#down_query} SQL for this class.
49
+ #
50
+ # @param [String] raw_sql
51
+ # @return [void]
37
52
  def down!(raw_sql)
38
53
  config.down_query = raw_sql.dedent
39
54
  end
@@ -1,35 +1,24 @@
1
1
  module Statesman
2
2
  module Trigger
3
+ # Integration with ActiveRecord.
3
4
  module Integration
4
5
  extend ActiveSupport::Concern
5
6
 
6
- def create_statesman_trigger(options = {})
7
- statesman_trigger_requires_pg!
8
-
9
- params = Statesman::Trigger::Parameters.new options
10
-
11
- validation_query = params.build_validation_query
12
-
13
- raise "target column `#{params.sync_column}` not found on `#{params.model_table}`." unless select_value(validation_query)
14
-
15
- params.build_statements(direction: :up).map do |stmt|
16
- execute stmt
7
+ require_relative './integration/shared_methods'
8
+ require_relative './integration/adapter_methods'
9
+ require_relative './integration/command_recorder_methods'
10
+ require_relative './integration/migration_methods'
11
+
12
+ class << self
13
+ # Used in an `ActiveSupport.on_load :active_record` callback to install the ActiveRecord integrations.
14
+ #
15
+ # @return [void]
16
+ def install!
17
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.include Statesman::Trigger::Integration::AdapterMethods
18
+ ActiveRecord::Migration::CommandRecorder.include Statesman::Trigger::Integration::CommandRecorderMethods
19
+ ActiveRecord::Migration.include Statesman::Trigger::Integration::MigrationMethods
17
20
  end
18
21
  end
19
-
20
- def drop_statesman_trigger(options = {})
21
- statesman_trigger_requires_pg!
22
-
23
- params = Statesman::Trigger::Parameters.new options
24
-
25
- params.build_statements(direction: :down).map do |stmt|
26
- execute stmt
27
- end
28
- end
29
-
30
- def statesman_trigger_requires_pg!
31
- raise 'Requires postgres' unless adapter_name =~ /postg/i
32
- end
33
22
  end
34
23
  end
35
24
  end
@@ -0,0 +1,46 @@
1
+ module Statesman
2
+ module Trigger
3
+ module Integration
4
+ # Integration with `ActiveRecord::ConnectionAdapters::AbstractAdapter`
5
+ #
6
+ # @note only Postgres is currently supported.
7
+ module AdapterMethods
8
+ extend ActiveSupport::Concern
9
+ include Statesman::Trigger::Integration::SharedMethods
10
+
11
+ # @!macro migration_command
12
+ def create_statesman_trigger(*args)
13
+ statesman_trigger_requires_pg!
14
+
15
+ params = build_statesman_trigger_params args
16
+
17
+ validation_query = params.build_validation_query
18
+
19
+ raise "target column `#{params.sync_column}` not found on `#{params.model_table}`." unless select_value(validation_query)
20
+
21
+ params.build_statements(direction: :up).map do |stmt|
22
+ execute stmt
23
+ end
24
+ end
25
+
26
+ # @!macro migration_command
27
+ def drop_statesman_trigger(*args)
28
+ statesman_trigger_requires_pg!
29
+
30
+ params = build_statesman_trigger_params args
31
+
32
+ params.build_statements(direction: :down).map do |stmt|
33
+ execute stmt
34
+ end
35
+ end
36
+
37
+ # @api private
38
+ # @raise [RuntimeError] if adapter is not postgres
39
+ # @return [void]
40
+ def statesman_trigger_requires_pg!
41
+ raise 'Requires postgres' unless adapter_name =~ /postg/i
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,31 @@
1
+ module Statesman
2
+ module Trigger
3
+ module Integration
4
+ # Add support for statesman triggers to `ActiveRecord::Migration::CommandRecorder` in order to support reversible migrations.
5
+ module CommandRecorderMethods
6
+ extend ActiveSupport::Concern
7
+ include Statesman::Trigger::Integration::SharedMethods
8
+
9
+ # @!macro migration_command
10
+ def create_statesman_trigger(*args, &block)
11
+ record :create_statesman_trigger, build_statesman_trigger_options(args)
12
+ end
13
+
14
+ # @!macro migration_command
15
+ def drop_statesman_trigger(*args, &block)
16
+ record :drop_statesman_trigger, build_statesman_trigger_options(args)
17
+ end
18
+
19
+ # @return [(Symbol, Hash)]
20
+ def invert_create_statesman_trigger(args, &block)
21
+ [:drop_statesman_trigger, build_statesman_trigger_options(args)]
22
+ end
23
+
24
+ # @return [(Symbol, Hash)]
25
+ def invert_drop_statesman_trigger(args, &block)
26
+ [:create_statesman_trigger, build_statesman_trigger_options(args)]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,48 @@
1
+ module Statesman
2
+ module Trigger
3
+ module Integration
4
+ # Adds support for statesman triggers to `ActiveRecord::Migration`.
5
+ module MigrationMethods
6
+ extend ActiveSupport::Concern
7
+ include Statesman::Trigger::Integration::SharedMethods
8
+
9
+ # @!macro migration_command
10
+ def create_statesman_trigger(*args)
11
+ params = build_statesman_trigger_params(args)
12
+
13
+ say_with_time "Creating Statesman trigger" do
14
+ dump_statesman_params params
15
+
16
+ say "create_statesman_trigger_function :#{params.function_name}", true
17
+ say "create_statesman_trigger :#{params.trigger_name}", true
18
+
19
+ connection.create_statesman_trigger params
20
+ end
21
+ end
22
+
23
+ # @!macro migration_command
24
+ def drop_statesman_trigger(*args)
25
+ params = build_statesman_trigger_params(args)
26
+
27
+ say_with_time "Dropping Statesman trigger" do
28
+ dump_statesman_params params
29
+
30
+ say "drop_statesman_trigger :#{params.trigger_name}", true
31
+ say "drop_statesman_trigger_function :#{params.function_name}", true
32
+
33
+ connection.drop_statesman_trigger params
34
+ end
35
+ end
36
+
37
+ private
38
+ # @param [Statesman::Trigger::Parameters] params
39
+ # @return [void]
40
+ def dump_statesman_params(params)
41
+ params.for_inspect.each do |key, value|
42
+ say "#{key}: #{value.inspect}", true
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,96 @@
1
+ module Statesman
2
+ module Trigger
3
+ module Integration
4
+ # Used to match a single string for a {Parameters#state_name}
5
+ MATCH_STATE_NAME = proc do |args|
6
+ args.length == 1 && args.first.kind_of?(String)
7
+ end
8
+
9
+ # Used to match a tuple of classes for {Parameters#model_klass} and {Parameters#transition_klass}
10
+ MATCH_MODEL_AND_TRANSITION_CLASSES = proc do |args|
11
+ args.length == 2 && args.all? { |a| a.kind_of? Class }
12
+ end
13
+
14
+ # Used to match a tuple of strings for {Parameters#model_table} and {Parameters#transition_table}
15
+ MATCH_MODEL_AND_TRANSITION_TABLES = proc do |args|
16
+ args.length == 2 && args.all? { |a| a.kind_of?(String) || a.kind_of?(Symbol) }
17
+ end
18
+
19
+ # Combines {MATCH_STATE_NAME} with {MATCH_MODEL_AND_TRANSITION_CLASSES}
20
+ MATCH_STATE_NAME_AND_CLASSES = proc do |args|
21
+ MATCH_STATE_NAME[args.take(1)] && MATCH_MODEL_AND_TRANSITION_CLASSES[args.drop(1)]
22
+ end
23
+
24
+ # Combines {MATCH_STATE_NAME} with {MATCH_MODEL_AND_TRANSITION_TABLES}
25
+ MATCH_STATE_NAME_AND_TABLE_NAMES = proc do |args|
26
+ MATCH_STATE_NAME[args.take(1)] && MATCH_MODEL_AND_TRANSITION_TABLES[args.drop(1)]
27
+ end
28
+
29
+ # Methods used by migration and connection adapter integrations.
30
+ #
31
+ # @api private
32
+ module SharedMethods
33
+ extend ActiveSupport::Concern
34
+
35
+ # @!macro [new] migration_options
36
+ # @param [Hash] options
37
+ # @option options [String] :sync_column (see Parameters#sync_column)
38
+
39
+ # @!macro [new] migration_command
40
+ # @overload $0(state_name, options = {})
41
+ # @param [#to_s] state_name
42
+ # @!macro migration_options
43
+ # @overload $0(model_klass, transition_klass, options = {})
44
+ # @param [Class] model_klass
45
+ # @param [Class] transition_klass
46
+ # @!macro migration_options
47
+ # @overload $0(state_name, model_klass, transition_klass, options = {})
48
+ # @param [#to_s] state_name
49
+ # @param [Class] model_klass
50
+ # @param [Class] transition_klass
51
+ # @!macro migration_options
52
+ # @overload $0(model_table, transition_table, options = {})
53
+ # @param [#to_s] model_table
54
+ # @param [#to_s] transition_table
55
+ # @!macro migration_options
56
+ # @overload $0(state_name, model_table, transition_table, options = {})
57
+ # @param [#to_s] state_name
58
+ # @param [#to_s] model_table
59
+ # @param [#to_s] transition_table
60
+ # @!macro migration_options
61
+ #
62
+ # @return [void]
63
+
64
+ # @param [(Class, Class), (#to_s, #to_s, #to_s), (#to_s), (#to_s, Class, Class), (#to_s, #to_s)] args
65
+ # @return [(Hash)]
66
+ def build_statesman_trigger_options(args)
67
+ return [args.first] if args.first.kind_of?(Statesman::Trigger::Parameters)
68
+
69
+ args &&= args.dup
70
+ options = args.extract_options!
71
+
72
+ case args
73
+ when MATCH_STATE_NAME_AND_CLASSES
74
+ options[:state_name], options[:model_klass], options[:transition_klass] = args
75
+ when MATCH_STATE_NAME_AND_TABLE_NAMES
76
+ options[:state_name], options[:model_table], options[:transition_table] = args
77
+ when MATCH_STATE_NAME
78
+ options[:state_name] = args.first
79
+ when MATCH_MODEL_AND_TRANSITION_TABLES
80
+ options[:model_table], options[:transition_table] = args
81
+ when MATCH_MODEL_AND_TRANSITION_CLASSES
82
+ options[:model_klass], options[:transition_klass] = args
83
+ end
84
+
85
+ return [options]
86
+ end
87
+
88
+ # @param (see #build_statesman_trigger_options)
89
+ # @return [Statesman::Trigger::Parameters]
90
+ def build_statesman_trigger_params(args)
91
+ Statesman::Trigger::Parameters.new build_statesman_trigger_options(args).first
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,18 +1,29 @@
1
1
  module Statesman
2
2
  module Trigger
3
+ # Created by {Parameters#default_model_klass} or {Parameters#default_transition_klass}
4
+ # to raise errors if there is an attempt to introspect
5
+ # @api private
3
6
  class NullObject
7
+ # @!attribute [r] name
8
+ # Name of the parameter used by {Parameters}
9
+ # @return [String]
4
10
  attr_reader :name
5
11
 
12
+ # @param [String, Symbol] name
6
13
  def initialize(name)
7
14
  @name = name
8
15
  end
9
16
 
17
+ # @return [String]
10
18
  def inspect
11
19
  %[#{self.class}(:name => :#{name})]
12
20
  end
13
21
 
22
+ # @param [Symbol] method
23
+ # @raise [Statesman::Trigger::IntrospectionError] with the name of the method that should exist on {#name}.
24
+ # @return [void]
14
25
  def method_missing(method, *args, &block)
15
- raise NoMethodError, "You must provide :#{name} that responds to :#{method}"
26
+ raise Statesman::Trigger::IntrospectionError, "You must provide :#{name} that responds to :#{method}"
16
27
  end
17
28
  end
18
29
  end
@@ -1,31 +1,70 @@
1
1
  module Statesman
2
2
  module Trigger
3
+ # This generates `CREATE` / `DROP` `FUNCTION` / `TRIGGER` statements and can
4
+ # introspect proper values based on provided {#model_klass} and {#transition_klass}.
3
5
  class Parameters
4
- include Virtus.model
6
+ __send__ :include, Virtus.model
5
7
 
8
+ # @see #prefix
6
9
  PREFIX_FORMAT = "sync_%<state_name>s_state_for_%<model_table>s"
7
10
 
11
+ # @!attribute [r] state_name
12
+ # Unique identifier in the case of multiple states on the same table.
13
+ # @return [String]
8
14
  attribute :state_name, String, default: "statesman"
15
+
16
+ # @!attribute [r] sync_column
17
+ # The name of the column on the "parent" table to synchronize the last state's value.
18
+ # @return [String]
9
19
  attribute :sync_column, String, default: "current_state"
10
20
 
21
+ # @!attribute [r] model_klass
22
+ # The "parent" class that has the state machine on it.
23
+ # @return [Class]
11
24
  attribute :model_klass, Object, required: false, default: :default_model_klass
25
+
26
+ # @!attribute [r] transition_klass
27
+ # The transition class that stores the state machine history.
28
+ # @return [Class]
12
29
  attribute :transition_klass, Object, required: false, default: :default_transition_klass
13
30
 
31
+ # @!attribute [r] model_table
32
+ # The table for {#model_klass} (will be introspected from that if not provided, else error)
33
+ # @return [String]
14
34
  attribute :model_table, String, default: :default_model_table
35
+
36
+ # @!attribute [r] transition_table
37
+ # The table for {#transition_klass} (will be introspected from that if not provided, else error)
38
+ # @return [String]
15
39
  attribute :transition_table, String, default: :default_transition_table
40
+
41
+ # @!attribute [r] foreign_key_column
42
+ # The foreign key column on on {#transition_table} that syncs up with {#model_table}'s `id` column.
43
+ #
44
+ # Given `orders` and `order_transitions`, this would be `order_id` on the latter table.
45
+ # @return [String]
16
46
  attribute :foreign_key_column, String, default: :default_foreign_key_column
17
47
 
48
+ # @!attribute [r] prefix
49
+ # Used to namespace {#function_name} and {#trigger_name} in the database.
50
+ # @see PREFIX_FORMAT
51
+ # @return [String]
18
52
  attribute :prefix, String, default: :default_prefix
19
53
 
54
+ # @!attribute [r] function_name
55
+ # @return [String]
20
56
  attribute :function_name, String, default: :default_function_name
57
+
58
+ # @!attribute [r] trigger_name
59
+ # @return [String]
21
60
  attribute :trigger_name, String, default: :default_trigger_name
22
61
 
62
+ # Builds a query used to validate that {#sync_column} actually exists on {#model_table}.
23
63
  # @return [String]
24
64
  def build_validation_query
25
65
  %[SELECT TRUE from pg_attribute WHERE attrelid = '%<model_table>s'::regclass AND attname = '%<sync_column>s' AND NOT attisdropped] % { model_table: escape_string(model_table), sync_column: escape_string(sync_column) }
26
66
  end
27
67
 
28
- # @api private
29
68
  # @param [:up, :down] direction
30
69
  # @return [<String>]
31
70
  def build_statements(direction:)
@@ -43,10 +82,33 @@ module Statesman
43
82
  return stmts
44
83
  end
45
84
 
85
+ # @return [String]
86
+ def inspect
87
+ %[Statesman::Trigger(#{for_inspect.inspect})]
88
+ end
89
+
90
+ # @!attribute [r] quoted_function_name
91
+ # Returns the PG-safe {#function_name}
92
+ # @return [String]
93
+ def quoted_function_name
94
+ quote_ident function_name
95
+ end
96
+
97
+ # @!attribute [r] quoted_function_name
98
+ # Returns the PG-safe {#trigger_name}
99
+ # @return [String]
100
+ def quoted_trigger_name
101
+ quote_ident trigger_name
102
+ end
103
+
104
+ # @!attribute [r] update_function
105
+ # @return [Statesman::Trigger::UpdateFunctionQuery]
46
106
  def update_function
47
107
  @update_function ||= UpdateFunctionQuery.new self
48
108
  end
49
109
 
110
+ # @!attribute [r] trigger_statement
111
+ # @return [Statesman::Trigger::TriggerQuery]
50
112
  def trigger_statement
51
113
  @trigger_statement ||= TriggerQuery.new self
52
114
  end
@@ -56,51 +118,78 @@ module Statesman
56
118
  attributes.slice(:state_name, :sync_column,
57
119
  :model_table, :transition_table,
58
120
  :function_name, :trigger_name,
59
- :foreign_key_column)
121
+ :foreign_key_column).merge(
122
+ quoted_function_name: quoted_function_name,
123
+ quoted_trigger_name: quoted_trigger_name
124
+ )
125
+ end
126
+
127
+ # @return [Hash]
128
+ def for_inspect
129
+ attributes.slice(:state_name, :sync_column, :model_table, :transition_table)
60
130
  end
61
131
 
62
132
  private
133
+ # @return [NullObject]
63
134
  def default_model_klass
64
135
  NullObject.new :model_klass
65
136
  end
66
137
 
138
+ # @return [NullObject]
67
139
  def default_transition_klass
68
140
  NullObject.new :transition_klass
69
141
  end
70
142
 
143
+ # @return [String]
71
144
  def default_prefix
72
145
  sprintf(PREFIX_FORMAT, state_name: state_name, model_table: model_table)
73
146
  end
74
147
 
148
+ # @return [String]
75
149
  def default_model_table
76
150
  model_klass.table_name
77
151
  end
78
152
 
153
+ # @return [String]
79
154
  def default_transition_table
80
155
  transition_klass.table_name
81
156
  end
82
157
 
158
+ # @return [String]
83
159
  def default_foreign_key_column
84
160
  return "#{model_klass.model_name.singular}_id" unless model_klass.kind_of?(NullObject)
85
161
 
86
162
  return "#{model_table.to_s.singularize}_id"
87
163
  end
88
164
 
165
+ # @return [String]
89
166
  def default_function_name
90
- quote_ident "#{prefix}_fn"
167
+ "#{prefix}_fn"
91
168
  end
92
169
 
170
+ # @return [String]
93
171
  def default_trigger_name
94
- quote_ident "#{prefix}_trigger"
172
+ "#{prefix}_trigger"
95
173
  end
96
174
 
175
+ # @!group Quoting methods
176
+
177
+ # Shorthand for `PG::Connection.escape_string`
178
+ #
179
+ # @param [String] value
180
+ # @return [String]
97
181
  def escape_string(value)
98
182
  PG::Connection.escape_string value
99
183
  end
100
184
 
185
+ # Shorthand for `PG::Connection.quote_ident`
186
+ #
187
+ # @param [String] value
188
+ # @return [String]
101
189
  def quote_ident(value)
102
190
  PG::Connection.quote_ident value
103
191
  end
192
+ # @!endgroup
104
193
  end
105
194
  end
106
195
  end
@@ -1,14 +1,16 @@
1
1
  module Statesman
2
2
  module Trigger
3
+ # Generates `[ CREATE | DROP ] TRIGGER` statements.
4
+ #
3
5
  # @api private
4
6
  class TriggerQuery < AbstractQuery
5
7
  up! <<-SQL
6
- CREATE TRIGGER %<trigger_name>s AFTER INSERT ON %<transition_table>s
7
- FOR EACH ROW EXECUTE PROCEDURE %<function_name>s();
8
+ CREATE TRIGGER %<quoted_trigger_name>s AFTER INSERT ON %<transition_table>s
9
+ FOR EACH ROW EXECUTE PROCEDURE %<quoted_function_name>s();
8
10
  SQL
9
11
 
10
12
  down! <<-SQL
11
- DROP TRIGGER IF EXISTS %<trigger_name>s ON %<transition_table>s;
13
+ DROP TRIGGER IF EXISTS %<quoted_trigger_name>s ON %<transition_table>s;
12
14
  SQL
13
15
  end
14
16
  end
@@ -1,9 +1,11 @@
1
1
  module Statesman
2
2
  module Trigger
3
+ # Generates `[ CREATE | DROP ] FUNCTION` statements.
4
+ #
3
5
  # @api private
4
6
  class UpdateFunctionQuery < AbstractQuery
5
7
  up! <<-SQL
6
- CREATE OR REPLACE FUNCTION %<function_name>s() RETURNS TRIGGER AS $$
8
+ CREATE OR REPLACE FUNCTION %<quoted_function_name>s() RETURNS TRIGGER AS $$
7
9
  BEGIN
8
10
  UPDATE %<model_table>s SET %<sync_column>s = NEW.to_state WHERE id = NEW.%<foreign_key_column>s;
9
11
  RETURN NULL;
@@ -12,7 +14,7 @@ module Statesman
12
14
  SQL
13
15
 
14
16
  down! <<-SQL
15
- DROP FUNCTION IF EXISTS %<function_name>s()
17
+ DROP FUNCTION IF EXISTS %<quoted_function_name>s()
16
18
  SQL
17
19
  end
18
20
  end
@@ -1,5 +1,6 @@
1
1
  module Statesman
2
2
  module Trigger
3
- VERSION = "0.1.0"
3
+ # Gem version.
4
+ VERSION = "0.1.1"
4
5
  end
5
6
  end
@@ -31,4 +31,5 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency "pry"
32
32
  spec.add_development_dependency "simplecov"
33
33
  spec.add_development_dependency "database_cleaner"
34
+ spec.add_development_dependency "yard"
34
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statesman-trigger
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
  - Alexa Grey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-07-31 00:00:00.000000000 Z
11
+ date: 2015-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: yard
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
181
195
  description: Create a database trigger that keeps the most recent Statesman state
182
196
  in sync.
183
197
  email:
@@ -189,7 +203,10 @@ files:
189
203
  - ".gitignore"
190
204
  - ".rspec"
191
205
  - ".travis.yml"
206
+ - ".yardopts"
207
+ - CODE_OF_CONDUCT.md
192
208
  - Gemfile
209
+ - LICENSE.txt
193
210
  - README.md
194
211
  - Rakefile
195
212
  - bin/console
@@ -199,7 +216,10 @@ files:
199
216
  - lib/statesman/trigger.rb
200
217
  - lib/statesman/trigger/abstract_query.rb
201
218
  - lib/statesman/trigger/integration.rb
202
- - lib/statesman/trigger/migrations.rb
219
+ - lib/statesman/trigger/integration/adapter_methods.rb
220
+ - lib/statesman/trigger/integration/command_recorder_methods.rb
221
+ - lib/statesman/trigger/integration/migration_methods.rb
222
+ - lib/statesman/trigger/integration/shared_methods.rb
203
223
  - lib/statesman/trigger/null_object.rb
204
224
  - lib/statesman/trigger/parameters.rb
205
225
  - lib/statesman/trigger/trigger_query.rb
@@ -230,3 +250,4 @@ signing_key:
230
250
  specification_version: 4
231
251
  summary: Create a database trigger that keeps the most recent Statesman state in sync.
232
252
  test_files: []
253
+ has_rdoc:
@@ -1,71 +0,0 @@
1
- module Statesman
2
- module Trigger
3
- MATCH_STATE_NAME = proc do |args|
4
- args.length == 1 && args.first.kind_of?(String)
5
- end
6
-
7
- MATCH_MODEL_AND_TRANSITION_CLASSES = proc do |args|
8
- args.length == 2 && args.all? { |a| a.kind_of? Class }
9
- end
10
-
11
- MATCH_MODEL_AND_TRANSITION_TABLES = proc do |args|
12
- args.length == 2 && args.all? { |a| a.kind_of?(String) || a.kind_of?(Symbol) }
13
- end
14
-
15
- MATCH_STATE_NAME_AND_CLASSES = proc do |args|
16
- MATCH_STATE_NAME[args.take(1)] && MATCH_MODEL_AND_TRANSITION_CLASSES[args.drop(1)]
17
- end
18
-
19
- MATCH_STATE_NAME_AND_TABLE_NAMES = proc do |args|
20
- MATCH_STATE_NAME[args.take(1)] && MATCH_MODEL_AND_TRANSITION_TABLES[args.drop(1)]
21
- end
22
-
23
- # Integrates with CommandRecorder
24
- #
25
- # @api private
26
- module Migrations
27
- extend ActiveSupport::Concern
28
-
29
- def create_statesman_trigger(*args, &block)
30
- record :create_statesman_trigger, build_statesman_trigger_options(args)
31
- end
32
-
33
- def drop_statesman_trigger(*args, &block)
34
- record :drop_statesman_trigger, build_statesman_trigger_options(args)
35
- end
36
-
37
- def invert_create_statesman_trigger(args, &block)
38
- [:drop_statesman_trigger, build_statesman_trigger_options(args)]
39
- end
40
-
41
- def invert_drop_statesman_trigger(args, &block)
42
- [:create_statesman_trigger, build_statesman_trigger_options(args)]
43
- end
44
-
45
- def build_statesman_trigger_options(args)
46
- options = args.extract_options!
47
-
48
- case args
49
- when MATCH_STATE_NAME_AND_CLASSES
50
- options[:state_name] = args.shift
51
- options[:model_klass] = args.shift
52
- options[:transition_klass] = args.shift
53
- when MATCH_STATE_NAME_AND_TABLE_NAMES
54
- options[:state_name] = args.shift
55
- options[:model_table] = args.shift
56
- options[:transition_table] = args.shift
57
- when MATCH_STATE_NAME
58
- options[:state_name] = args.shift
59
- when MATCH_MODEL_AND_TRANSITION_TABLES
60
- options[:model_table] = args.shift
61
- options[:transition_table] = args.shift
62
- when MATCH_MODEL_AND_TRANSITION_CLASSES
63
- options[:model_klass] = args.shift
64
- options[:transition_klass] = args.shift
65
- end
66
-
67
- return [options]
68
- end
69
- end
70
- end
71
- end