superstore 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +11 -0
- data/CHANGELOG +0 -0
- data/Gemfile +19 -0
- data/LICENSE +13 -0
- data/MIT-LICENSE +20 -0
- data/README.md +100 -0
- data/Rakefile +12 -0
- data/lib/superstore/adapters/abstract_adapter.rb +49 -0
- data/lib/superstore/adapters/cassandra_adapter.rb +181 -0
- data/lib/superstore/adapters/hstore_adapter.rb +163 -0
- data/lib/superstore/attribute_methods/definition.rb +22 -0
- data/lib/superstore/attribute_methods/dirty.rb +36 -0
- data/lib/superstore/attribute_methods/primary_key.rb +25 -0
- data/lib/superstore/attribute_methods/typecasting.rb +59 -0
- data/lib/superstore/attribute_methods.rb +96 -0
- data/lib/superstore/base.rb +33 -0
- data/lib/superstore/belongs_to/association.rb +48 -0
- data/lib/superstore/belongs_to/builder.rb +40 -0
- data/lib/superstore/belongs_to/reflection.rb +30 -0
- data/lib/superstore/belongs_to.rb +63 -0
- data/lib/superstore/callbacks.rb +29 -0
- data/lib/superstore/cassandra_schema/statements.rb +52 -0
- data/lib/superstore/cassandra_schema/tasks.rb +47 -0
- data/lib/superstore/cassandra_schema.rb +9 -0
- data/lib/superstore/connection.rb +39 -0
- data/lib/superstore/core.rb +59 -0
- data/lib/superstore/errors.rb +10 -0
- data/lib/superstore/identity.rb +26 -0
- data/lib/superstore/inspect.rb +25 -0
- data/lib/superstore/log_subscriber.rb +44 -0
- data/lib/superstore/model.rb +37 -0
- data/lib/superstore/persistence.rb +153 -0
- data/lib/superstore/railtie.rb +30 -0
- data/lib/superstore/railties/controller_runtime.rb +45 -0
- data/lib/superstore/schema.rb +20 -0
- data/lib/superstore/scope/batches.rb +32 -0
- data/lib/superstore/scope/finder_methods.rb +48 -0
- data/lib/superstore/scope/query_methods.rb +49 -0
- data/lib/superstore/scope.rb +49 -0
- data/lib/superstore/scoping.rb +27 -0
- data/lib/superstore/tasks/ks.rake +54 -0
- data/lib/superstore/timestamps.rb +19 -0
- data/lib/superstore/type.rb +16 -0
- data/lib/superstore/types/array_type.rb +20 -0
- data/lib/superstore/types/base_type.rb +26 -0
- data/lib/superstore/types/boolean_type.rb +20 -0
- data/lib/superstore/types/date_type.rb +22 -0
- data/lib/superstore/types/float_type.rb +16 -0
- data/lib/superstore/types/integer_type.rb +20 -0
- data/lib/superstore/types/json_type.rb +13 -0
- data/lib/superstore/types/string_type.rb +19 -0
- data/lib/superstore/types/time_type.rb +16 -0
- data/lib/superstore/types.rb +8 -0
- data/lib/superstore/validations.rb +44 -0
- data/lib/superstore.rb +69 -0
- data/superstore.gemspec +23 -0
- data/test/support/cassandra.rb +44 -0
- data/test/support/hstore.rb +40 -0
- data/test/support/issue.rb +10 -0
- data/test/test_helper.rb +42 -0
- data/test/unit/active_model_test.rb +18 -0
- data/test/unit/adapters/adapter_test.rb +6 -0
- data/test/unit/attribute_methods/definition_test.rb +13 -0
- data/test/unit/attribute_methods/dirty_test.rb +72 -0
- data/test/unit/attribute_methods/primary_key_test.rb +26 -0
- data/test/unit/attribute_methods/typecasting_test.rb +118 -0
- data/test/unit/attribute_methods_test.rb +51 -0
- data/test/unit/base_test.rb +20 -0
- data/test/unit/belongs_to/reflection_test.rb +12 -0
- data/test/unit/belongs_to_test.rb +62 -0
- data/test/unit/callbacks_test.rb +46 -0
- data/test/unit/cassandra_schema/statements_test.rb +47 -0
- data/test/unit/cassandra_schema/tasks_test.rb +31 -0
- data/test/unit/connection_test.rb +10 -0
- data/test/unit/core_test.rb +55 -0
- data/test/unit/identity_test.rb +26 -0
- data/test/unit/inspect_test.rb +26 -0
- data/test/unit/log_subscriber_test.rb +26 -0
- data/test/unit/persistence_test.rb +213 -0
- data/test/unit/railties/controller_runtime_test.rb +48 -0
- data/test/unit/schema_test.rb +27 -0
- data/test/unit/scope/batches_test.rb +30 -0
- data/test/unit/scope/finder_methods_test.rb +51 -0
- data/test/unit/scope/query_methods_test.rb +27 -0
- data/test/unit/scoping_test.rb +7 -0
- data/test/unit/serialization_test.rb +10 -0
- data/test/unit/timestamps_test.rb +27 -0
- data/test/unit/types/array_type_test.rb +21 -0
- data/test/unit/types/base_type_test.rb +19 -0
- data/test/unit/types/boolean_type_test.rb +24 -0
- data/test/unit/types/date_type_test.rb +15 -0
- data/test/unit/types/float_type_test.rb +17 -0
- data/test/unit/types/integer_type_test.rb +19 -0
- data/test/unit/types/json_type_test.rb +23 -0
- data/test/unit/types/string_type_test.rb +30 -0
- data/test/unit/types/time_type_test.rb +19 -0
- data/test/unit/validations_test.rb +27 -0
- metadata +170 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Types
|
|
3
|
+
class DateType < BaseType
|
|
4
|
+
FORMAT = '%Y-%m-%d'
|
|
5
|
+
REGEX = /\A\d{4}-\d{2}-\d{2}\Z/
|
|
6
|
+
|
|
7
|
+
def encode(value)
|
|
8
|
+
raise ArgumentError.new("#{value.inspect} is not a Date") unless value.kind_of?(Date)
|
|
9
|
+
value.strftime(FORMAT)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def decode(str)
|
|
13
|
+
return nil if str.empty?
|
|
14
|
+
Date.parse(str)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def typecast(value)
|
|
18
|
+
value.to_date
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Types
|
|
3
|
+
class FloatType < BaseType
|
|
4
|
+
REGEX = /\A[-+]?\d+(\.\d+)?\Z/
|
|
5
|
+
def encode(float)
|
|
6
|
+
raise ArgumentError.new("#{float.inspect} is not a Float") unless float.kind_of?(Float)
|
|
7
|
+
float.to_s
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def decode(str)
|
|
11
|
+
return nil if str.empty?
|
|
12
|
+
str.to_f
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Types
|
|
3
|
+
class IntegerType < BaseType
|
|
4
|
+
REGEX = /\A[-+]?\d+\Z/
|
|
5
|
+
def encode(int)
|
|
6
|
+
raise ArgumentError.new("#{int.inspect} is not an Integer.") unless int.kind_of?(Integer)
|
|
7
|
+
int.to_s
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def decode(str)
|
|
11
|
+
return nil if str.empty?
|
|
12
|
+
str.to_i
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def typecast(value)
|
|
16
|
+
value.to_i
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Types
|
|
3
|
+
class StringType < BaseType
|
|
4
|
+
def encode(str)
|
|
5
|
+
raise ArgumentError.new("#{str.inspect} is not a String") unless str.kind_of?(String)
|
|
6
|
+
|
|
7
|
+
unless str.encoding == Encoding::UTF_8
|
|
8
|
+
(str.frozen? ? str.dup : str).force_encoding('UTF-8')
|
|
9
|
+
else
|
|
10
|
+
str
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def typecast(value)
|
|
15
|
+
value.to_s
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Types
|
|
3
|
+
class TimeType < BaseType
|
|
4
|
+
def encode(time)
|
|
5
|
+
raise ArgumentError.new("#{time.inspect} is not a Time") unless time.kind_of?(Time)
|
|
6
|
+
time.utc.xmlschema(6)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def decode(str)
|
|
10
|
+
Time.parse(str).in_time_zone if str
|
|
11
|
+
rescue
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Superstore::Type.register(:array, Superstore::Types::ArrayType)
|
|
2
|
+
Superstore::Type.register(:boolean, Superstore::Types::BooleanType)
|
|
3
|
+
Superstore::Type.register(:date, Superstore::Types::DateType)
|
|
4
|
+
Superstore::Type.register(:float, Superstore::Types::FloatType)
|
|
5
|
+
Superstore::Type.register(:integer, Superstore::Types::IntegerType)
|
|
6
|
+
Superstore::Type.register(:json, Superstore::Types::JsonType)
|
|
7
|
+
Superstore::Type.register(:time, Superstore::Types::TimeType)
|
|
8
|
+
Superstore::Type.register(:string, Superstore::Types::StringType)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
class RecordInvalid < StandardError
|
|
3
|
+
attr_reader :record
|
|
4
|
+
def initialize(record)
|
|
5
|
+
@record = record
|
|
6
|
+
super("Invalid record: #{@record.errors.full_messages.to_sentence}")
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module Validations
|
|
11
|
+
extend ActiveSupport::Concern
|
|
12
|
+
include ActiveModel::Validations
|
|
13
|
+
|
|
14
|
+
included do
|
|
15
|
+
define_callbacks :validate, scope: :name
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module ClassMethods
|
|
19
|
+
def create!(attributes = {})
|
|
20
|
+
new(attributes).tap do |object|
|
|
21
|
+
object.save!
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def save(options={})
|
|
27
|
+
if perform_validations(options)
|
|
28
|
+
super
|
|
29
|
+
true
|
|
30
|
+
else
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def save!
|
|
36
|
+
save || raise(RecordInvalid.new(self))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
protected
|
|
40
|
+
def perform_validations(options={})
|
|
41
|
+
(options[:validate] != false) ? valid? : true
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/superstore.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'active_support/all'
|
|
2
|
+
require 'active_model'
|
|
3
|
+
require 'superstore/errors'
|
|
4
|
+
|
|
5
|
+
module Superstore
|
|
6
|
+
extend ActiveSupport::Autoload
|
|
7
|
+
|
|
8
|
+
autoload :AttributeMethods
|
|
9
|
+
autoload :Base
|
|
10
|
+
autoload :BelongsTo
|
|
11
|
+
autoload :Callbacks
|
|
12
|
+
autoload :Connection
|
|
13
|
+
autoload :Core
|
|
14
|
+
autoload :Identity
|
|
15
|
+
autoload :Inspect
|
|
16
|
+
autoload :Model
|
|
17
|
+
autoload :Persistence
|
|
18
|
+
autoload :Schema
|
|
19
|
+
autoload :CassandraSchema
|
|
20
|
+
autoload :Scope
|
|
21
|
+
autoload :Scoping
|
|
22
|
+
autoload :Serialization
|
|
23
|
+
autoload :Timestamps
|
|
24
|
+
autoload :Type
|
|
25
|
+
autoload :Validations
|
|
26
|
+
|
|
27
|
+
module AttributeMethods
|
|
28
|
+
extend ActiveSupport::Autoload
|
|
29
|
+
|
|
30
|
+
eager_autoload do
|
|
31
|
+
autoload :Definition
|
|
32
|
+
autoload :Dirty
|
|
33
|
+
autoload :PrimaryKey
|
|
34
|
+
autoload :Typecasting
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module Adapters
|
|
39
|
+
extend ActiveSupport::Autoload
|
|
40
|
+
|
|
41
|
+
autoload :AbstractAdapter
|
|
42
|
+
autoload :CassandraAdapter
|
|
43
|
+
autoload :HstoreAdapter
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
module BelongsTo
|
|
47
|
+
extend ActiveSupport::Autoload
|
|
48
|
+
|
|
49
|
+
autoload :Association
|
|
50
|
+
autoload :Builder
|
|
51
|
+
autoload :Reflection
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
module Types
|
|
55
|
+
extend ActiveSupport::Autoload
|
|
56
|
+
|
|
57
|
+
autoload :BaseType
|
|
58
|
+
autoload :ArrayType
|
|
59
|
+
autoload :BooleanType
|
|
60
|
+
autoload :DateType
|
|
61
|
+
autoload :FloatType
|
|
62
|
+
autoload :IntegerType
|
|
63
|
+
autoload :JsonType
|
|
64
|
+
autoload :StringType
|
|
65
|
+
autoload :TimeType
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
require 'superstore/railtie' if defined?(Rails)
|
data/superstore.gemspec
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = 'superstore'
|
|
5
|
+
s.version = '1.0.0'
|
|
6
|
+
s.description = 'ActiveModel for many attributes'
|
|
7
|
+
s.summary = 'Cassandra ActiveModel'
|
|
8
|
+
s.authors = ["Michael Koziarski", "gotime"]
|
|
9
|
+
s.email = 'developer@matthewhiggins.com'
|
|
10
|
+
s.homepage = 'http://github.com/data-axle/superstore'
|
|
11
|
+
|
|
12
|
+
s.required_ruby_version = '>= 2.0.0'
|
|
13
|
+
s.required_rubygems_version = '>= 1.3.5'
|
|
14
|
+
|
|
15
|
+
s.extra_rdoc_files = ["README.md"]
|
|
16
|
+
s.files = `git ls-files`.split("\n")
|
|
17
|
+
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
|
18
|
+
s.require_paths = ['lib']
|
|
19
|
+
|
|
20
|
+
s.add_runtime_dependency('activemodel', '>= 3.0')
|
|
21
|
+
|
|
22
|
+
s.add_development_dependency('bundler')
|
|
23
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Bundler.require :cassandra
|
|
2
|
+
|
|
3
|
+
Superstore::Base.config = {
|
|
4
|
+
keyspace: 'superstore_test',
|
|
5
|
+
servers: '127.0.0.1:9160',
|
|
6
|
+
consistency: 'QUORUM',
|
|
7
|
+
thrift: {
|
|
8
|
+
timeout: 5
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
begin
|
|
13
|
+
Superstore::CassandraSchema.drop_keyspace 'superstore_test'
|
|
14
|
+
rescue Exception => e
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
sleep 1
|
|
18
|
+
Superstore::CassandraSchema.create_keyspace 'superstore_test'
|
|
19
|
+
Superstore::CassandraSchema.create_column_family 'Issues'
|
|
20
|
+
|
|
21
|
+
Superstore::Base.class_eval do
|
|
22
|
+
class_attribute :created_records
|
|
23
|
+
self.created_records = []
|
|
24
|
+
|
|
25
|
+
after_create do
|
|
26
|
+
created_records << self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.delete_after_test
|
|
30
|
+
# created_records.reject(&:destroyed?).each(&:destroy)
|
|
31
|
+
Issue.delete_all
|
|
32
|
+
created_records.clear
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module ActiveSupport
|
|
37
|
+
class TestCase
|
|
38
|
+
teardown do
|
|
39
|
+
if Superstore::Base.created_records.any?
|
|
40
|
+
Superstore::Base.delete_after_test
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Bundler.require :hstore
|
|
2
|
+
require 'active_record'
|
|
3
|
+
|
|
4
|
+
Superstore::Base.config = {'adapter' => 'hstore'}
|
|
5
|
+
|
|
6
|
+
class PGInitializer
|
|
7
|
+
def self.initialize!
|
|
8
|
+
config = {
|
|
9
|
+
'adapter' => 'postgresql',
|
|
10
|
+
'encoding' => 'unicode',
|
|
11
|
+
'database' => 'superstore_test',
|
|
12
|
+
'pool' => 5,
|
|
13
|
+
'username' => 'postgres'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
ActiveRecord::Base.configurations = { test: config }
|
|
17
|
+
|
|
18
|
+
ActiveRecord::Tasks::DatabaseTasks.drop config
|
|
19
|
+
ActiveRecord::Tasks::DatabaseTasks.create config
|
|
20
|
+
ActiveRecord::Base.establish_connection config
|
|
21
|
+
|
|
22
|
+
Superstore::Base.adapter.create_table('issues')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.table_names
|
|
26
|
+
%w(issues)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
PGInitializer.initialize!
|
|
31
|
+
|
|
32
|
+
module ActiveSupport
|
|
33
|
+
class TestCase
|
|
34
|
+
teardown do
|
|
35
|
+
PGInitializer.table_names.each do |table_name|
|
|
36
|
+
ActiveRecord::Base.connection.execute "TRUNCATE #{table_name}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'bundler/setup'
|
|
2
|
+
Bundler.require(:default, :test)
|
|
3
|
+
|
|
4
|
+
I18n.config.enforce_available_locales = false
|
|
5
|
+
|
|
6
|
+
require 'rails/test_help'
|
|
7
|
+
require 'mocha/setup'
|
|
8
|
+
|
|
9
|
+
# require 'support/hstore'
|
|
10
|
+
require 'support/cassandra'
|
|
11
|
+
require 'support/issue'
|
|
12
|
+
|
|
13
|
+
def MiniTest.filter_backtrace(bt)
|
|
14
|
+
bt
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module Superstore
|
|
18
|
+
class TestCase < ActiveSupport::TestCase
|
|
19
|
+
def temp_object(&block)
|
|
20
|
+
Class.new(Superstore::Base) do
|
|
21
|
+
self.column_family = 'Issues'
|
|
22
|
+
string :force_save
|
|
23
|
+
before_save { self.force_save = 'junk' }
|
|
24
|
+
|
|
25
|
+
def self.name
|
|
26
|
+
'Issue'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
instance_eval(&block) if block_given?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module Types
|
|
35
|
+
class TestCase < Superstore::TestCase
|
|
36
|
+
attr_accessor :coder
|
|
37
|
+
setup do
|
|
38
|
+
@coder = self.class.name.sub(/Test$/, '').constantize.new
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class ActiveModelTest < Superstore::TestCase
|
|
4
|
+
|
|
5
|
+
include ActiveModel::Lint::Tests
|
|
6
|
+
|
|
7
|
+
# overrides ActiveModel::Lint::Tests#test_to_param
|
|
8
|
+
def test_to_param
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# overrides ActiveModel::Lint::Tests#test_to_key
|
|
12
|
+
def test_to_key
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def setup
|
|
16
|
+
@model = Issue.new
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class Superstore::AttributeMethods::DefinitionTest < Superstore::TestCase
|
|
4
|
+
class TestType < Superstore::Types::BaseType
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
test 'typecast' do
|
|
8
|
+
definition = Superstore::AttributeMethods::Definition.new(:foo, TestType, {a: :b})
|
|
9
|
+
|
|
10
|
+
assert_equal 'foo', definition.name
|
|
11
|
+
assert_kind_of TestType, definition.coder
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class Superstore::AttributeMethods::DirtyTest < Superstore::TestCase
|
|
4
|
+
test 'save clears dirty' do
|
|
5
|
+
record = temp_object do
|
|
6
|
+
string :name
|
|
7
|
+
end.new name: 'foo'
|
|
8
|
+
|
|
9
|
+
assert record.changed?
|
|
10
|
+
|
|
11
|
+
record.save!
|
|
12
|
+
|
|
13
|
+
assert_equal [nil, 'foo'], record.previous_changes['name']
|
|
14
|
+
assert !record.changed?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
test 'reload clears dirty' do
|
|
18
|
+
record = temp_object do
|
|
19
|
+
string :name
|
|
20
|
+
end.create! name: 'foo'
|
|
21
|
+
|
|
22
|
+
record.name = 'bar'
|
|
23
|
+
assert record.changed?
|
|
24
|
+
|
|
25
|
+
record.reload
|
|
26
|
+
|
|
27
|
+
assert !record.changed?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
test 'typecast float before dirty check' do
|
|
31
|
+
record = temp_object do
|
|
32
|
+
float :price
|
|
33
|
+
end.create(price: 5.01)
|
|
34
|
+
|
|
35
|
+
record.price = '5.01'
|
|
36
|
+
assert !record.changed?
|
|
37
|
+
|
|
38
|
+
record.price = '7.12'
|
|
39
|
+
assert record.changed?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
test 'typecast boolean before dirty check' do
|
|
43
|
+
record = temp_object do
|
|
44
|
+
boolean :awesome
|
|
45
|
+
end.create(awesome: false)
|
|
46
|
+
|
|
47
|
+
record.awesome = false
|
|
48
|
+
assert !record.changed?
|
|
49
|
+
|
|
50
|
+
record.awesome = true
|
|
51
|
+
assert record.changed?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
test 'write_attribute' do
|
|
55
|
+
object = temp_object do
|
|
56
|
+
string :name
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
expected = {"name"=>[nil, "foo"]}
|
|
60
|
+
|
|
61
|
+
object.new.tap do |record|
|
|
62
|
+
record.name = 'foo'
|
|
63
|
+
assert_equal expected, record.changes
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
object.new.tap do |record|
|
|
67
|
+
record[:name] = 'foo'
|
|
68
|
+
# record.write_attribute(:name, 'foo')
|
|
69
|
+
assert_equal expected, record.changes
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class Superstore::AttributeMethods::PrimaryKeyTest < Superstore::TestCase
|
|
4
|
+
test 'get id' do
|
|
5
|
+
model = temp_object do
|
|
6
|
+
key do
|
|
7
|
+
"foo"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
record = model.new
|
|
11
|
+
|
|
12
|
+
assert_equal 'foo', record.id
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
test 'set id' do
|
|
16
|
+
issue = Issue.new id: 'foo'
|
|
17
|
+
|
|
18
|
+
assert_equal 'foo', issue.id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
test 'attributes' do
|
|
22
|
+
issue = Issue.new
|
|
23
|
+
|
|
24
|
+
assert_not_nil issue.attributes['id']
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class Superstore::AttributeMethods::TypecastingTest < Superstore::TestCase
|
|
4
|
+
class CustomType
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class CustomCoder < Superstore::Types::BaseType
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class TestIssue < Superstore::Base
|
|
11
|
+
self.column_family = 'Issues'
|
|
12
|
+
|
|
13
|
+
attribute :custom_column, type: CustomType, coder: CustomCoder
|
|
14
|
+
boolean :enabled
|
|
15
|
+
float :rating
|
|
16
|
+
integer :price
|
|
17
|
+
json :orders
|
|
18
|
+
string :name
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class TestChildIssue < TestIssue
|
|
22
|
+
string :description
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
test 'attributes not shared' do
|
|
26
|
+
assert_nothing_raised { Issue.new.description }
|
|
27
|
+
assert_raise(NoMethodError) { TestIssue.new.description }
|
|
28
|
+
assert_nothing_raised { TestChildIssue.new.description }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
test 'custom attribute definer' do
|
|
32
|
+
model_attribute = TestIssue.attribute_definitions['custom_column']
|
|
33
|
+
|
|
34
|
+
assert_kind_of CustomCoder, model_attribute.coder
|
|
35
|
+
assert_equal 'custom_column', model_attribute.name
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
test 'typecast_attribute' do
|
|
39
|
+
assert_equal 1, TestIssue.typecast_attribute(TestIssue.new, 'price', 1)
|
|
40
|
+
assert_equal 1, TestIssue.typecast_attribute(TestIssue.new, :price, 1)
|
|
41
|
+
|
|
42
|
+
assert_raise NoMethodError do
|
|
43
|
+
TestIssue.typecast_attribute(TestIssue.new, 'wtf', 1)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
test 'boolean attribute' do
|
|
48
|
+
issue = TestIssue.create! enabled: '1'
|
|
49
|
+
assert_equal true, issue.enabled
|
|
50
|
+
|
|
51
|
+
issue = TestIssue.find issue.id
|
|
52
|
+
assert_equal true, issue.enabled
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
test 'float attribute' do
|
|
56
|
+
issue = TestIssue.create! rating: '4.5'
|
|
57
|
+
assert_equal 4.5, issue.rating
|
|
58
|
+
|
|
59
|
+
issue = TestIssue.find issue.id
|
|
60
|
+
assert_equal(4.5, issue.rating)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
test 'integer attribute' do
|
|
64
|
+
issue = TestIssue.create! price: '101'
|
|
65
|
+
assert_equal 101, issue.price
|
|
66
|
+
|
|
67
|
+
issue = TestIssue.find issue.id
|
|
68
|
+
assert_equal(101, issue.price)
|
|
69
|
+
|
|
70
|
+
issue = TestIssue.new price: ''
|
|
71
|
+
assert_nil issue.price
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
test 'json attribute' do
|
|
75
|
+
issue = TestIssue.create! orders: {'a' => 'b'}
|
|
76
|
+
assert_equal({'a' => 'b'}, issue.orders)
|
|
77
|
+
|
|
78
|
+
issue = TestIssue.find issue.id
|
|
79
|
+
assert_equal({'a' => 'b'}, issue.orders)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
test 'string attribute' do
|
|
83
|
+
issue = TestIssue.create! name: 'hola'
|
|
84
|
+
assert_equal('hola', issue.name)
|
|
85
|
+
|
|
86
|
+
issue = TestIssue.find issue.id
|
|
87
|
+
assert_equal('hola', issue.name)
|
|
88
|
+
|
|
89
|
+
issue = TestIssue.create! name: 42
|
|
90
|
+
assert_equal '42', issue.name
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
test 'multiple attributes definition' do
|
|
94
|
+
class MultipleAttributesIssue < Superstore::Base
|
|
95
|
+
self.column_family = 'Issues'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
assert_nothing_raised {
|
|
99
|
+
MultipleAttributesIssue.string :hello, :greetings, :bye
|
|
100
|
+
}
|
|
101
|
+
issue = MultipleAttributesIssue.new :hello => 'hey', :greetings => 'how r u', :bye => 'see ya'
|
|
102
|
+
|
|
103
|
+
assert_equal 'how r u', issue.greetings
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
test 'multiple attributes with options' do
|
|
107
|
+
class MultipleAttributesIssue < Superstore::Base
|
|
108
|
+
self.column_family = 'Issues'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
MultipleAttributesIssue.expects(:attribute).with(:hello, { :unique => :true, :type => :string })
|
|
112
|
+
MultipleAttributesIssue.expects(:attribute).with(:world, { :unique => :true, :type => :string })
|
|
113
|
+
|
|
114
|
+
class MultipleAttributesIssue < Superstore::Base
|
|
115
|
+
string :hello, :world, :unique => :true
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|