cramp 0.11 → 0.12
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cramp.rb +12 -1
- data/lib/cramp/abstract.rb +72 -0
- data/lib/cramp/action.rb +12 -0
- data/lib/cramp/body.rb +48 -0
- data/lib/cramp/callbacks.rb +48 -0
- data/lib/cramp/keep_connection_alive.rb +19 -0
- data/lib/cramp/periodic_timer.rb +49 -0
- data/lib/cramp/rendering.rb +11 -0
- data/lib/cramp/test_case.rb +54 -0
- data/lib/cramp/websocket.rb +84 -0
- data/lib/cramp/{controller/websocket → websocket}/rainbows_backend.rb +1 -1
- data/lib/cramp/{controller/websocket → websocket}/thin_backend.rb +1 -1
- metadata +37 -86
- data/lib/cramp/controller.rb +0 -15
- data/lib/cramp/controller/abstract.rb +0 -71
- data/lib/cramp/controller/action.rb +0 -14
- data/lib/cramp/controller/body.rb +0 -50
- data/lib/cramp/controller/callbacks.rb +0 -50
- data/lib/cramp/controller/keep_connection_alive.rb +0 -21
- data/lib/cramp/controller/periodic_timer.rb +0 -51
- data/lib/cramp/controller/rendering.rb +0 -13
- data/lib/cramp/controller/test_case.rb +0 -57
- data/lib/cramp/controller/websocket.rb +0 -63
- data/lib/cramp/model.rb +0 -40
- data/lib/cramp/model/arel_monkey_patches.rb +0 -66
- data/lib/cramp/model/attribute.rb +0 -104
- data/lib/cramp/model/attribute_methods.rb +0 -83
- data/lib/cramp/model/base.rb +0 -119
- data/lib/cramp/model/callbacks.rb +0 -41
- data/lib/cramp/model/column.rb +0 -72
- data/lib/cramp/model/emysql_ext.rb +0 -21
- data/lib/cramp/model/engine.rb +0 -75
- data/lib/cramp/model/engine/connection.rb +0 -32
- data/lib/cramp/model/evented_mysql.rb +0 -298
- data/lib/cramp/model/finders.rb +0 -27
- data/lib/cramp/model/quoting.rb +0 -104
- data/lib/cramp/model/relation.rb +0 -62
- data/lib/cramp/model/status.rb +0 -18
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'active_support'
|
2
|
-
require 'active_support/test_case'
|
3
|
-
|
4
|
-
module Cramp
|
5
|
-
module Controller
|
6
|
-
class TestCase < ::ActiveSupport::TestCase
|
7
|
-
|
8
|
-
setup :create_request
|
9
|
-
|
10
|
-
def create_request
|
11
|
-
@request = Rack::MockRequest.new(app)
|
12
|
-
end
|
13
|
-
|
14
|
-
def get(path, options = {}, headers = {}, &block)
|
15
|
-
callback = options.delete(:callback) || block
|
16
|
-
headers = headers.merge('async.callback' => callback)
|
17
|
-
|
18
|
-
EM.run { @request.get(path, headers) }
|
19
|
-
end
|
20
|
-
|
21
|
-
def get_body(path, options = {}, headers = {}, &block)
|
22
|
-
callback = options.delete(:callback) || block
|
23
|
-
response_callback = proc {|response| response[-1].each {|chunk| callback.call(chunk) } }
|
24
|
-
headers = headers.merge('async.callback' => response_callback)
|
25
|
-
|
26
|
-
EM.run { @request.get(path, headers) }
|
27
|
-
end
|
28
|
-
|
29
|
-
def get_body_chunks(path, options = {}, headers = {}, &block)
|
30
|
-
callback = options.delete(:callback) || block
|
31
|
-
count = options.delete(:count) || 1
|
32
|
-
|
33
|
-
stopping = false
|
34
|
-
chunks = []
|
35
|
-
|
36
|
-
get_body(path, options, headers) do |body_chunk|
|
37
|
-
chunks << body_chunk unless stopping
|
38
|
-
|
39
|
-
if chunks.count >= count
|
40
|
-
stopping = true
|
41
|
-
callback.call(chunks) if callback
|
42
|
-
EM.next_tick { EM.stop }
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def app
|
48
|
-
raise "Please define a method called 'app' returning an async Rack Application"
|
49
|
-
end
|
50
|
-
|
51
|
-
def stop
|
52
|
-
EM.stop
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
module Cramp
|
2
|
-
module Controller
|
3
|
-
module WebsocketExtension
|
4
|
-
WEBSOCKET_RECEIVE_CALLBACK = 'websocket.receive_callback'.freeze
|
5
|
-
|
6
|
-
def websocket?
|
7
|
-
@env['HTTP_CONNECTION'] == 'Upgrade' && @env['HTTP_UPGRADE'] == 'WebSocket'
|
8
|
-
end
|
9
|
-
|
10
|
-
def websocket_upgrade_data
|
11
|
-
location = "ws://#{@env['HTTP_HOST']}#{@env['REQUEST_PATH']}"
|
12
|
-
|
13
|
-
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
14
|
-
upgrade << "Upgrade: WebSocket\r\n"
|
15
|
-
upgrade << "Connection: Upgrade\r\n"
|
16
|
-
upgrade << "WebSocket-Origin: #{@env['HTTP_ORIGIN']}\r\n"
|
17
|
-
upgrade << "WebSocket-Location: #{location}\r\n\r\n"
|
18
|
-
|
19
|
-
upgrade
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class Websocket < Abstract
|
24
|
-
include PeriodicTimer
|
25
|
-
|
26
|
-
# TODO : Websockets shouldn't need this in an ideal world
|
27
|
-
include KeepConnectionAlive
|
28
|
-
|
29
|
-
class_inheritable_accessor :on_data_callbacks, :instance_reader => false
|
30
|
-
self.on_data_callbacks = []
|
31
|
-
|
32
|
-
class << self
|
33
|
-
def backend=(backend)
|
34
|
-
raise "Websocket backend #{backend} is unknown" unless [:thin, :rainbows].include?(backend.to_sym)
|
35
|
-
require "cramp/controller/websocket/#{backend}_backend.rb"
|
36
|
-
end
|
37
|
-
|
38
|
-
def on_data(*methods)
|
39
|
-
self.on_data_callbacks += methods
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def process
|
44
|
-
@env['websocket.receive_callback'] = method(:_on_data_receive)
|
45
|
-
super
|
46
|
-
end
|
47
|
-
|
48
|
-
def render(body)
|
49
|
-
@body.call("\x00#{body}\xff")
|
50
|
-
end
|
51
|
-
|
52
|
-
def _on_data_receive(data)
|
53
|
-
data = data.split(/\000([^\377]*)\377/).select{|d| !d.empty? }.collect{|d| d.gsub(/^\x00|\xff$/, '') }
|
54
|
-
self.class.on_data_callbacks.each do |callback|
|
55
|
-
data.each do |message|
|
56
|
-
EM.next_tick { send(callback, message) }
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
data/lib/cramp/model.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
require 'cramp'
|
2
|
-
require 'cramp/model/evented_mysql'
|
3
|
-
require 'cramp/model/emysql_ext'
|
4
|
-
|
5
|
-
require 'mysqlplus'
|
6
|
-
|
7
|
-
require 'arel'
|
8
|
-
require 'cramp/model/arel_monkey_patches'
|
9
|
-
|
10
|
-
require 'active_model'
|
11
|
-
|
12
|
-
module Cramp
|
13
|
-
module Model
|
14
|
-
autoload :Quoting, "cramp/model/quoting"
|
15
|
-
autoload :Engine, "cramp/model/engine"
|
16
|
-
autoload :Column, "cramp/model/column"
|
17
|
-
autoload :Relation, "cramp/model/relation"
|
18
|
-
|
19
|
-
autoload :Base, "cramp/model/base"
|
20
|
-
autoload :Finders, "cramp/model/finders"
|
21
|
-
autoload :Attribute, "cramp/model/attribute"
|
22
|
-
autoload :AttributeMethods, "cramp/model/attribute_methods"
|
23
|
-
autoload :Status, "cramp/model/status"
|
24
|
-
autoload :Callbacks, "cramp/model/callbacks"
|
25
|
-
|
26
|
-
def self.init(settings)
|
27
|
-
Arel::Table.engine = Cramp::Model::Engine.new(settings)
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.select(query, callback = nil, &block)
|
31
|
-
callback ||= block
|
32
|
-
|
33
|
-
EventedMysql.select(query) do |rows|
|
34
|
-
callback.arity == 1 ? callback.call(rows) : callback.call if callback
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
@@ -1,66 +0,0 @@
|
|
1
|
-
class Arel::Session
|
2
|
-
def create(insert, &block)
|
3
|
-
insert.call(&block)
|
4
|
-
end
|
5
|
-
|
6
|
-
def read(select, &block)
|
7
|
-
select.call(&block)
|
8
|
-
end
|
9
|
-
|
10
|
-
def update(update, &block)
|
11
|
-
update.call(&block)
|
12
|
-
end
|
13
|
-
|
14
|
-
def delete(delete, &block)
|
15
|
-
delete.call(&block)
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
module Arel::Relation
|
21
|
-
def call(&block)
|
22
|
-
engine.read(self, &block)
|
23
|
-
end
|
24
|
-
|
25
|
-
def all(&block)
|
26
|
-
session.read(self) {|rows| block.call(rows) }
|
27
|
-
end
|
28
|
-
|
29
|
-
def first(&block)
|
30
|
-
session.read(self) {|rows| block.call(rows[0]) }
|
31
|
-
end
|
32
|
-
|
33
|
-
def each(&block)
|
34
|
-
session.read(self) {|rows| rows.each {|r| block.call(r) } }
|
35
|
-
end
|
36
|
-
|
37
|
-
def insert(record, &block)
|
38
|
-
session.create(Arel::Insert.new(self, record), &block)
|
39
|
-
end
|
40
|
-
|
41
|
-
def update(assignments, &block)
|
42
|
-
session.update(Arel::Update.new(self, assignments), &block)
|
43
|
-
end
|
44
|
-
|
45
|
-
def delete(&block)
|
46
|
-
session.delete(Arel::Deletion.new(self), &block)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
class Arel::Deletion
|
51
|
-
def call(&block)
|
52
|
-
engine.delete(self, &block)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
class Arel::Insert
|
57
|
-
def call(&block)
|
58
|
-
engine.create(self, &block)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
class Arel::Update
|
63
|
-
def call(&block)
|
64
|
-
engine.update(self, &block)
|
65
|
-
end
|
66
|
-
end
|
@@ -1,104 +0,0 @@
|
|
1
|
-
# Copyright (c) 2009 Koziarski Software Ltd
|
2
|
-
#
|
3
|
-
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
-
# purpose with or without fee is hereby granted, provided that the above
|
5
|
-
# copyright notice and this permission notice appear in all copies.
|
6
|
-
#
|
7
|
-
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
-
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
-
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
-
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
-
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
-
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
-
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
-
|
15
|
-
module Cramp
|
16
|
-
module Model
|
17
|
-
class Attribute
|
18
|
-
|
19
|
-
FORMATS = {}
|
20
|
-
FORMATS[Date] = /^\d{4}\/\d{2}\/\d{2}$/
|
21
|
-
FORMATS[Integer] = /^-?\d+$/
|
22
|
-
FORMATS[Float] = /^-?\d*\.\d*$/
|
23
|
-
FORMATS[Time] = /\A\s*
|
24
|
-
-?\d+-\d\d-\d\d
|
25
|
-
T
|
26
|
-
\d\d:\d\d:\d\d
|
27
|
-
(\.\d*)?
|
28
|
-
(Z|[+-]\d\d:\d\d)?
|
29
|
-
\s*\z/ix # lifted from the implementation of Time.xmlschema
|
30
|
-
|
31
|
-
CONVERTERS = {}
|
32
|
-
CONVERTERS[Date] = Proc.new do |str|
|
33
|
-
Date.strptime(str, "%Y/%m/%d")
|
34
|
-
end
|
35
|
-
|
36
|
-
CONVERTERS[Integer] = Proc.new do |str|
|
37
|
-
Integer(str)
|
38
|
-
end
|
39
|
-
|
40
|
-
CONVERTERS[Float] = Proc.new do |str|
|
41
|
-
Float(str)
|
42
|
-
end
|
43
|
-
|
44
|
-
CONVERTERS[Time] = Proc.new do |str|
|
45
|
-
Time.xmlschema(str)
|
46
|
-
end
|
47
|
-
|
48
|
-
attr_reader :name
|
49
|
-
def initialize(name, owner_class, options)
|
50
|
-
@name = name.to_s
|
51
|
-
@owner_class = owner_class
|
52
|
-
@options = options
|
53
|
-
|
54
|
-
# append_validations!
|
55
|
-
define_methods!
|
56
|
-
end
|
57
|
-
|
58
|
-
# I think this should live somewhere in Amo
|
59
|
-
def check_value!(value)
|
60
|
-
# Allow nil and Strings to fall back on the validations for typecasting
|
61
|
-
# Everything else gets checked with is_a?
|
62
|
-
if value.nil?
|
63
|
-
nil
|
64
|
-
elsif value.is_a?(String)
|
65
|
-
value
|
66
|
-
elsif value.is_a?(expected_type)
|
67
|
-
value
|
68
|
-
else
|
69
|
-
raise TypeError, "Expected #{expected_type.inspect} but got #{value.inspect}"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def expected_type
|
74
|
-
@options[:type] || String
|
75
|
-
end
|
76
|
-
|
77
|
-
def type_cast(value)
|
78
|
-
if value.is_a?(expected_type)
|
79
|
-
value
|
80
|
-
elsif (converter = CONVERTERS[expected_type]) && (value =~ FORMATS[expected_type])
|
81
|
-
converter.call(value)
|
82
|
-
else
|
83
|
-
value
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def append_validations!
|
88
|
-
if f = FORMATS[expected_type]
|
89
|
-
@owner_class.validates_format_of @name, :with => f, :unless => lambda {|obj| obj.send(name).is_a? expected_type }, :allow_nil => @options[:allow_nil]
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def define_methods!
|
94
|
-
@owner_class.define_attribute_methods(true)
|
95
|
-
end
|
96
|
-
|
97
|
-
def primary_key?
|
98
|
-
@options[:primary_key]
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|
104
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
# Copyright (c) 2009 Koziarski Software Ltd
|
2
|
-
#
|
3
|
-
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
-
# purpose with or without fee is hereby granted, provided that the above
|
5
|
-
# copyright notice and this permission notice appear in all copies.
|
6
|
-
#
|
7
|
-
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
-
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
-
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
-
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
-
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
-
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
-
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
-
|
15
|
-
module Cramp
|
16
|
-
module Model
|
17
|
-
module AttributeMethods
|
18
|
-
|
19
|
-
extend ActiveSupport::Concern
|
20
|
-
include ActiveModel::AttributeMethods
|
21
|
-
|
22
|
-
module ClassMethods
|
23
|
-
def attribute(name, options = {})
|
24
|
-
write_inheritable_hash(:model_attributes, {name => Attribute.new(name, self, options)})
|
25
|
-
end
|
26
|
-
|
27
|
-
def define_attribute_methods(force = false)
|
28
|
-
return unless model_attributes
|
29
|
-
undefine_attribute_methods if force
|
30
|
-
super(model_attributes.keys)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
included do
|
35
|
-
class_inheritable_hash :model_attributes
|
36
|
-
undef_method(:id) if method_defined?(:id)
|
37
|
-
|
38
|
-
attribute_method_suffix("", "=")
|
39
|
-
end
|
40
|
-
|
41
|
-
def write_attribute(name, value)
|
42
|
-
if ma = self.class.model_attributes[name.to_sym]
|
43
|
-
value = ma.check_value!(value)
|
44
|
-
end
|
45
|
-
if(@attributes[name] != value)
|
46
|
-
send "#{name}_will_change!".to_sym
|
47
|
-
@attributes[name] = value
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def read_attribute(name)
|
52
|
-
if ma = self.class.model_attributes[name]
|
53
|
-
ma.type_cast(@attributes[name])
|
54
|
-
else
|
55
|
-
@attributes[name]
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def attributes=(attributes)
|
60
|
-
attributes.each do |(name, value)|
|
61
|
-
send("#{name}=", value)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
protected
|
66
|
-
|
67
|
-
def attribute_method?(name)
|
68
|
-
@attributes.include?(name.to_sym) || model_attributes[name.to_sym]
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
def attribute(name)
|
74
|
-
read_attribute(name.to_sym)
|
75
|
-
end
|
76
|
-
|
77
|
-
def attribute=(name, value)
|
78
|
-
write_attribute(name.to_sym, value)
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
data/lib/cramp/model/base.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
module Cramp
|
2
|
-
module Model
|
3
|
-
class Base
|
4
|
-
|
5
|
-
extend Finders
|
6
|
-
include AttributeMethods
|
7
|
-
include ActiveModel::Validations
|
8
|
-
include ActiveModel::Dirty
|
9
|
-
include Callbacks
|
10
|
-
|
11
|
-
class << self
|
12
|
-
def columns
|
13
|
-
@columns ||= arel_table.columns
|
14
|
-
end
|
15
|
-
|
16
|
-
def column_names
|
17
|
-
columns.map(&:name)
|
18
|
-
end
|
19
|
-
|
20
|
-
def primary_key
|
21
|
-
@primary_key ||= model_attributes.detect {|k, v| v.primary_key? }[0]
|
22
|
-
end
|
23
|
-
|
24
|
-
def instantiate(record)
|
25
|
-
object = allocate
|
26
|
-
object.instance_variable_set("@attributes", record.with_indifferent_access)
|
27
|
-
object
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
attr_reader :attributes
|
32
|
-
|
33
|
-
def initialize(attributes = {})
|
34
|
-
@new_record = true
|
35
|
-
@attributes = {}.with_indifferent_access
|
36
|
-
self.attributes = attributes
|
37
|
-
end
|
38
|
-
|
39
|
-
def new_record?
|
40
|
-
@new_record
|
41
|
-
end
|
42
|
-
|
43
|
-
def save(callback = nil, &block)
|
44
|
-
callback ||= block
|
45
|
-
|
46
|
-
if valid?
|
47
|
-
new_record? ? create_record(callback) : update_record(callback)
|
48
|
-
else
|
49
|
-
callback.arity == 1 ? callback.call(Status.new(self, false)) : callback.call if callback
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def destroy(callback = nil, &block)
|
54
|
-
callback ||= block
|
55
|
-
|
56
|
-
relation.delete do
|
57
|
-
status = Status.new(self, true)
|
58
|
-
after_destroy_callbacks status
|
59
|
-
callback.arity == 1 ? callback.call(status) : callback.call if callback
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def create_record(callback = nil, &block)
|
66
|
-
callback ||= block
|
67
|
-
|
68
|
-
self.class.arel_table.insert(arel_attributes(true)) do |new_id|
|
69
|
-
if new_id.present?
|
70
|
-
self.id = new_id
|
71
|
-
saved = true
|
72
|
-
@new_record = false
|
73
|
-
else
|
74
|
-
saved = false
|
75
|
-
end
|
76
|
-
|
77
|
-
status = Status.new(self, saved)
|
78
|
-
after_save status
|
79
|
-
callback.arity == 1 ? callback.call(status) : callback.call if callback
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def update_record(callback = nil, &block)
|
84
|
-
callback ||= block
|
85
|
-
|
86
|
-
relation.update(arel_attributes) do |updated_rows|
|
87
|
-
status = Status.new(self, true)
|
88
|
-
after_save status
|
89
|
-
callback.arity == 1 ? callback.call(status) : callback.call if callback
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def relation
|
94
|
-
self.class.arel_table.where(self.class[self.class.primary_key].eq(send(self.class.primary_key)))
|
95
|
-
end
|
96
|
-
|
97
|
-
def after_save(status)
|
98
|
-
if status.success?
|
99
|
-
@previously_changed = changes
|
100
|
-
changed_attributes.clear
|
101
|
-
end
|
102
|
-
|
103
|
-
after_save_callbacks status
|
104
|
-
end
|
105
|
-
|
106
|
-
def arel_attributes(exclude_primary_key = true, attribute_names = @attributes.keys)
|
107
|
-
attrs = {}
|
108
|
-
|
109
|
-
attribute_names.each do |name|
|
110
|
-
value = read_attribute(name)
|
111
|
-
attrs[self.class.arel_table[name]] = value
|
112
|
-
end
|
113
|
-
|
114
|
-
attrs
|
115
|
-
end
|
116
|
-
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|