spare 0.0.4 → 0.0.5

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
  SHA1:
3
- metadata.gz: 95b4a7463baf2b1727fa3a4d7cef198dbab3eb62
4
- data.tar.gz: aa448ecb6a763756efdaa0285f7ac897b1b838c8
3
+ metadata.gz: eebcabc4cfa5cc1016e4792fcb33d1d3ad3f8ecc
4
+ data.tar.gz: dd194a9dcb89d2b274f3bbe511d0545ff25320c9
5
5
  SHA512:
6
- metadata.gz: 253b3557cbf75fd7679dc9a0e37f90e549cba3e96812ddd771db46b57ac937052c6118da53224f84704a19c465cd3a49b21ed3a28f79b9b3b2fcd07b1974479f
7
- data.tar.gz: 1a1ebe0650e871f944e1b103ad5ec459d84d9a976d837143d8ea57121f7c71de288db778b975ca6a6818094814b161fbb701830735e255a9fbe3d0f9b8871080
6
+ metadata.gz: 01199811e2ceb6df16f47ee095bc9786a7d02a65172ff4f68fbbb3ed299467f1050a2a6a9976e3a153112b9de11fd2208d32a46dfa01d117995ed0f7a27643b5
7
+ data.tar.gz: 33888abe01749bd5b32c951f8e66eb7b1d5b63477417509ae41eedc0d7e643eb6bceedb1979caf4457344e57868d8d867de74c99f32c464330fc678eeed61275
@@ -1,4 +1,5 @@
1
1
  require 'active_record'
2
2
  module ActiveRecord
3
3
  class StoredProcedureNotFound < StandardError; end
4
+ class StoredProcedureNotExecuted < RecordNotSaved;end
4
5
  end
@@ -0,0 +1,62 @@
1
+ module Spare
2
+ module Execution
3
+ module ClassMethods
4
+ # Build an object (or multiple objects) and executes, if validations pass.
5
+ # The resulting object is returned whether the object was executed successfully to the database or not.
6
+ #
7
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
8
+ # attributes on the objects that are to be created.
9
+ def execute(attributes = nil)
10
+ if attributes.is_a?(Array)
11
+ attributes.map { |attr| excute(attr) }
12
+ else
13
+ object = new(attributes)
14
+ object.execute
15
+ object
16
+ end
17
+ end
18
+ alias :call :execute
19
+
20
+ # Build an object (or multiple objects) and executes,
21
+ # if validations pass. Raises a RecordInvalid error if validations fail,
22
+ # unlike Base#create.
23
+ #
24
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes.
25
+ # These describe which attributes to be created on the object, or
26
+ # multiple objects when given an Array of Hashes.
27
+ def execute!(attributes = nil)
28
+ if attributes.is_a?(Array)
29
+ attributes.collect { |attr| create!(attr) }
30
+ else
31
+ object = new(attributes)
32
+ object.execute!
33
+ object
34
+ end
35
+ end
36
+ alias :call! :execute!
37
+ end
38
+
39
+ attr_accessor :call_results
40
+
41
+ def execute
42
+ if valid?
43
+ self.class.connection_pool.with_connection do |conn|
44
+ call_results = conn.execute_stored_procedure(self)
45
+ end
46
+ end
47
+ valid?
48
+ end
49
+ alias :call :execute
50
+
51
+ def execute!
52
+ unless valid?
53
+ raise(ActiveRecord::StoredProcedureNotExecuted.new("Failed to execute the stored procedure", self))
54
+ end
55
+ end
56
+ alias :call! :execute!
57
+
58
+ def self.included(base)
59
+ base.extend(Spare::Execution::ClassMethods)
60
+ end
61
+ end
62
+ end
@@ -44,12 +44,79 @@ module ActiveRecord
44
44
  else
45
45
  column = new_column(field_name, nil, sql_type, false, collation)
46
46
  end
47
-
47
+
48
48
  column.param_type = param_type
49
49
  params << column
50
50
  end
51
51
  params
52
52
  end
53
+
54
+ def stored_procedure_to_sql(sp)
55
+ sql = []
56
+ sql << sp_inout_sql(sp)
57
+ sql << sp_call_sql(sp)
58
+ sql << sp_out_sql(sp)
59
+ sql.compact!
60
+ sql.join("\n")
61
+ end
62
+
63
+ def execute_stored_procedure(sp)
64
+
65
+ call_results = execute(stored_procedure_to_sql(sp))
66
+
67
+ if sp_out_params(sp).length != 0
68
+ clnt = instance_variable_get(:@connection)
69
+ while clnt.next_result
70
+ if result_array = clnt.store_result.to_a[0]
71
+ sp_out_params(sp).each_with_index do |param,i|
72
+ sp.__send__ "#{param.name}=", result_array[i]
73
+ end
74
+ end
75
+ end
76
+ nil
77
+ else
78
+ call_results
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def sp_in_params(sp)
85
+ prms = []
86
+ sp.class.stored_procedure[:param_list].each do |param|
87
+ if param.param_type == "IN"
88
+ prms << quote(sp.read_attribute(param.name))
89
+ else # OUT
90
+ prms << "@#{param.name}"
91
+ end
92
+ end
93
+ prms
94
+ end
95
+
96
+ def sp_out_params(sp)
97
+ sp.class.stored_procedure[:param_list].select { |param| param.param_type.to_s =~ /out/i }
98
+ end
99
+
100
+ def sp_inout_params(sp)
101
+ sp.class.stored_procedure[:param_list].select { |param| param.param_type.to_s =~ /inout/i }
102
+ end
103
+
104
+ def sp_out_sql(sp)
105
+ "SELECT #{sp_out_params(sp).collect{|param| "@#{param.name}"}.join(',')};"
106
+ end
107
+
108
+ # In MySQL even with multi-statements flag set, variables must be set 1 at a time, so return an array
109
+ def sp_inout_sql(sp)
110
+ sql = []
111
+ sp_inout_params(sp).each do |param|
112
+ sql << "SET @#{param.name} = #{quote(sp.send(param.name))};"
113
+ end
114
+ sql
115
+ end
116
+
117
+ def sp_call_sql(sp)
118
+ "CALL #{sp.class.stored_procedure[:db]}.#{sp.class.stored_procedure[:specific_name]}(#{sp_in_params(sp).join(',')});"
119
+ end
53
120
  end
54
121
  end
55
122
  end
@@ -2,89 +2,24 @@ require "active_record"
2
2
  module ActiveRecord
3
3
 
4
4
  # TODO - Refactor using only those modules necessary for things to work, should
5
- # be a lot easier when updated to support only Rails 4. Also, move any methods here
5
+ # be a lot easier when updated to support only Rails 4. Also, move any methods here
6
6
  # to their own module
7
7
  class StoredProcedure < Base
8
8
 
9
9
  extend Spare::Core
10
10
  extend Spare::ModelSchema
11
11
  extend Spare::Attributes
12
+ include Spare::Execution
12
13
 
13
14
  self.pluralize_table_names = false # Stored procedure names are what they are.
14
15
  self.abstract_class = true
15
16
 
16
17
  attr_accessor :call_results
17
18
 
18
- def in_params
19
- @in_params ||= in_fetch_params
20
- end
21
-
22
- def in_fetch_params
23
- prms = []
24
- self.class.stored_procedure[:param_list].each do |param|
25
- if param.param_type == "IN"
26
- prms << self.class.connection.quote(self.send(param.name.to_sym))
27
- else # OUT
28
- prms << "@#{param.name}"
29
- end
30
- end
31
- prms
32
- end
33
-
34
- def out_params
35
- @out_params ||= self.class.stored_procedure[:param_list].select{|param| param.param_type.to_s =~ /out/i}
36
- end
37
-
38
- def inout_params
39
- @inout_params ||= self.class.stored_procedure[:param_list].select{|param| param.param_type.to_s =~ /inout/i}
40
- end
41
-
42
- def out_sql
43
- "SELECT #{out_params.collect{|param| "@#{param.name}"}.join(',')};"
44
- end
45
-
46
- # In MySQL even with multi-statements flag set variables must be set 1 at a time, so return an array
47
- def inout_sql
48
- sql = []
49
- inout_params.each do |param|
50
- sql << "SET @#{param.name} = #{self.class.connection.quote(send(param.name))}"
51
- end
52
- sql
53
- end
54
-
55
- def call_sql
56
- "CALL #{self.class.stored_procedure[:db]}.#{self.class.stored_procedure[:specific_name]}(#{in_params.join(',')});"
57
- end
58
-
59
19
  def to_sql(skip_valid=false)
60
20
  if skip_valid || valid?
61
- # sql = (inout_sql.blank? ? "" : inout_sql)
62
- sql = call_sql
63
- sql << out_sql unless out_params.blank?
64
- sql
65
- end
66
- end
67
-
68
- def execute
69
- if valid?
70
- conn = self.class.connection
71
- unless inout_params.blank?
72
- self.inout_sql.each do |inout_to_set|
73
- conn.execute(inout_to_set)
74
- end
75
- end
76
- self.call_results = conn.execute(self.to_sql(true))
77
- if out_params.length != 0
78
- clnt = conn.instance_variable_get(:@connection)
79
- while clnt.next_result
80
- result_array = clnt.store_result.to_a[0]
81
- out_params.each_with_index do |param,i|
82
- send "#{param.name}=", result_array[i]
83
- end
84
- end
85
- end
21
+ self.class.connection.stored_procedure_to_sql(self)
86
22
  end
87
- valid?
88
23
  end
89
24
  end
90
25
  end
data/lib/spare/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spare
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
data/lib/spare.rb CHANGED
@@ -5,6 +5,7 @@ require "spare/attributes"
5
5
  require "spare/mysql_abstract_adapter"
6
6
  require "spare/model_schema"
7
7
  require "spare/schema_cache"
8
+ require "spare/execution"
8
9
  require "spare/stored_procedure"
9
10
 
10
11
  module Spare
@@ -21,13 +21,13 @@ describe ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter do
21
21
  END})
22
22
  end
23
23
  context "called with the stored procedure's name" do
24
- let (:sp) {ActiveRecord::Base.connection.stored_procedure("sp_test_adapter")}
24
+ let (:sp) { ActiveRecord::Base.connection.stored_procedure("sp_test_adapter") }
25
25
  it {expect(sp).to be_a(Hash)}
26
26
  it {expect(sp[:param_list]).to be_a(Array)}
27
27
  end
28
28
 
29
29
  context "called the stored procedure and database name" do
30
- let (:sp) {ActiveRecord::Base.connection.stored_procedure("sp_test.sp_test_adapter")}
30
+ let (:sp) { ActiveRecord::Base.connection.stored_procedure("sp_test.sp_test_adapter") }
31
31
  it {expect(sp).to be_a(Hash)}
32
32
  it {expect(sp[:param_list]).to be_a(Array)}
33
33
  end
@@ -35,12 +35,13 @@ describe ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter do
35
35
  end
36
36
 
37
37
  describe '#stored_procedure_params' do
38
- let (:sp_params) { %q{IN p_name VARCHAR(255) ,
39
- IN p_bar DECIMAL(10,2) ,
40
- IN p_other DATE ,
41
- OUT results INT(11)}
42
- }
43
- let (:parsed_params) {ActiveRecord::Base.connection.stored_procedure_params(sp_params, 'utf8_general_ci')}
38
+ let (:sp_params) do
39
+ %q{IN p_name VARCHAR(255) ,
40
+ IN p_bar DECIMAL(10,2) ,
41
+ IN p_other DATE ,
42
+ OUT results INT(11)}
43
+ end
44
+ let (:parsed_params) { ActiveRecord::Base.connection.stored_procedure_params(sp_params, 'utf8_general_ci') }
44
45
 
45
46
  it {expect(parsed_params).to be_a(Array)}
46
47
  it {expect(parsed_params.length).to eql(4)}
@@ -1,6 +1,8 @@
1
1
  require 'spec_helper'
2
2
  require 'byebug'
3
- class SpInsert < ActiveRecord::StoredProcedure;end
3
+ class SpInsert < ActiveRecord::StoredProcedure
4
+ validates :p_name, presence: true
5
+ end
4
6
 
5
7
  class Foo < ActiveRecord::Base;end
6
8
 
@@ -14,20 +16,25 @@ describe ActiveRecord::StoredProcedure do
14
16
  end
15
17
 
16
18
  describe '#execute' do
17
- let (:sp_insert) {SpInsert.new(:p_name => "foo",:p_deci => 2.0, :p_date => Date.today, :in_out_add => 4)}
19
+ let (:sp_insert) { SpInsert.new(:p_name => "foo",:p_deci => 2.0, :p_date => Date.today, :in_out_add => 4) }
20
+
18
21
  it { expect(sp_insert).to be_valid}
22
+
19
23
  it { expect(sp_insert.execute).to eql(true) }
24
+
20
25
  it "should work" do
21
26
  expect {
22
27
  sp_insert.execute
23
28
  }.to change(Foo, :count).by(1)
24
29
  end
30
+
25
31
  context "out parameters" do
26
32
  it "should work" do
27
33
  sp_insert.execute
28
34
  expect(sp_insert.o_id).to be_a(Fixnum)
29
35
  end
30
36
  end
37
+
31
38
  context "inout parameters" do
32
39
  it "should work" do
33
40
  sp_insert.execute
@@ -35,4 +42,36 @@ describe ActiveRecord::StoredProcedure do
35
42
  end
36
43
  end
37
44
  end
45
+
46
+ context "class methods" do
47
+ context "#execute" do
48
+ it "should work" do
49
+ expect {
50
+ SpInsert.execute(:p_name => "foo",:p_deci => 2.0, :p_date => Date.today, :in_out_add => 4)
51
+ }.to change(Foo, :count).by(1)
52
+ end
53
+
54
+ context "out parameters" do
55
+ it "should work" do
56
+ sp = SpInsert.execute(:p_name => "foo",:p_deci => 2.0, :p_date => Date.today, :in_out_add => 4)
57
+ expect(sp.o_id).to be_a(Fixnum)
58
+ end
59
+ end
60
+
61
+ context "inout parameters" do
62
+ it "should work" do
63
+ sp = SpInsert.execute(:p_name => "foo",:p_deci => 2.0, :p_date => Date.today, :in_out_add => 4)
64
+ expect(sp.in_out_add).to eql(5)
65
+ end
66
+ end
67
+ end
68
+
69
+ context "#execute!" do
70
+ context "not valid" do
71
+ it "should work" do
72
+ expect { SpInsert.execute! }.to raise_error(ActiveRecord::StoredProcedureNotExecuted)
73
+ end
74
+ end
75
+ end
76
+ end
38
77
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spare
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Mckinney
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-11 00:00:00.000000000 Z
11
+ date: 2016-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -117,6 +117,7 @@ files:
117
117
  - lib/spare/attributes.rb
118
118
  - lib/spare/core.rb
119
119
  - lib/spare/exceptions.rb
120
+ - lib/spare/execution.rb
120
121
  - lib/spare/model_schema.rb
121
122
  - lib/spare/mysql_abstract_adapter.rb
122
123
  - lib/spare/schema_cache.rb
@@ -149,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
150
  version: '0'
150
151
  requirements: []
151
152
  rubyforge_project:
152
- rubygems_version: 2.5.1
153
+ rubygems_version: 2.6.4
153
154
  signing_key:
154
155
  specification_version: 4
155
156
  summary: StoredProcedure models for ActiveRecord.