cramp 0.11 → 0.12
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.
- 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
|