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.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +11 -0
  4. data/CHANGELOG +0 -0
  5. data/Gemfile +19 -0
  6. data/LICENSE +13 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +100 -0
  9. data/Rakefile +12 -0
  10. data/lib/superstore/adapters/abstract_adapter.rb +49 -0
  11. data/lib/superstore/adapters/cassandra_adapter.rb +181 -0
  12. data/lib/superstore/adapters/hstore_adapter.rb +163 -0
  13. data/lib/superstore/attribute_methods/definition.rb +22 -0
  14. data/lib/superstore/attribute_methods/dirty.rb +36 -0
  15. data/lib/superstore/attribute_methods/primary_key.rb +25 -0
  16. data/lib/superstore/attribute_methods/typecasting.rb +59 -0
  17. data/lib/superstore/attribute_methods.rb +96 -0
  18. data/lib/superstore/base.rb +33 -0
  19. data/lib/superstore/belongs_to/association.rb +48 -0
  20. data/lib/superstore/belongs_to/builder.rb +40 -0
  21. data/lib/superstore/belongs_to/reflection.rb +30 -0
  22. data/lib/superstore/belongs_to.rb +63 -0
  23. data/lib/superstore/callbacks.rb +29 -0
  24. data/lib/superstore/cassandra_schema/statements.rb +52 -0
  25. data/lib/superstore/cassandra_schema/tasks.rb +47 -0
  26. data/lib/superstore/cassandra_schema.rb +9 -0
  27. data/lib/superstore/connection.rb +39 -0
  28. data/lib/superstore/core.rb +59 -0
  29. data/lib/superstore/errors.rb +10 -0
  30. data/lib/superstore/identity.rb +26 -0
  31. data/lib/superstore/inspect.rb +25 -0
  32. data/lib/superstore/log_subscriber.rb +44 -0
  33. data/lib/superstore/model.rb +37 -0
  34. data/lib/superstore/persistence.rb +153 -0
  35. data/lib/superstore/railtie.rb +30 -0
  36. data/lib/superstore/railties/controller_runtime.rb +45 -0
  37. data/lib/superstore/schema.rb +20 -0
  38. data/lib/superstore/scope/batches.rb +32 -0
  39. data/lib/superstore/scope/finder_methods.rb +48 -0
  40. data/lib/superstore/scope/query_methods.rb +49 -0
  41. data/lib/superstore/scope.rb +49 -0
  42. data/lib/superstore/scoping.rb +27 -0
  43. data/lib/superstore/tasks/ks.rake +54 -0
  44. data/lib/superstore/timestamps.rb +19 -0
  45. data/lib/superstore/type.rb +16 -0
  46. data/lib/superstore/types/array_type.rb +20 -0
  47. data/lib/superstore/types/base_type.rb +26 -0
  48. data/lib/superstore/types/boolean_type.rb +20 -0
  49. data/lib/superstore/types/date_type.rb +22 -0
  50. data/lib/superstore/types/float_type.rb +16 -0
  51. data/lib/superstore/types/integer_type.rb +20 -0
  52. data/lib/superstore/types/json_type.rb +13 -0
  53. data/lib/superstore/types/string_type.rb +19 -0
  54. data/lib/superstore/types/time_type.rb +16 -0
  55. data/lib/superstore/types.rb +8 -0
  56. data/lib/superstore/validations.rb +44 -0
  57. data/lib/superstore.rb +69 -0
  58. data/superstore.gemspec +23 -0
  59. data/test/support/cassandra.rb +44 -0
  60. data/test/support/hstore.rb +40 -0
  61. data/test/support/issue.rb +10 -0
  62. data/test/test_helper.rb +42 -0
  63. data/test/unit/active_model_test.rb +18 -0
  64. data/test/unit/adapters/adapter_test.rb +6 -0
  65. data/test/unit/attribute_methods/definition_test.rb +13 -0
  66. data/test/unit/attribute_methods/dirty_test.rb +72 -0
  67. data/test/unit/attribute_methods/primary_key_test.rb +26 -0
  68. data/test/unit/attribute_methods/typecasting_test.rb +118 -0
  69. data/test/unit/attribute_methods_test.rb +51 -0
  70. data/test/unit/base_test.rb +20 -0
  71. data/test/unit/belongs_to/reflection_test.rb +12 -0
  72. data/test/unit/belongs_to_test.rb +62 -0
  73. data/test/unit/callbacks_test.rb +46 -0
  74. data/test/unit/cassandra_schema/statements_test.rb +47 -0
  75. data/test/unit/cassandra_schema/tasks_test.rb +31 -0
  76. data/test/unit/connection_test.rb +10 -0
  77. data/test/unit/core_test.rb +55 -0
  78. data/test/unit/identity_test.rb +26 -0
  79. data/test/unit/inspect_test.rb +26 -0
  80. data/test/unit/log_subscriber_test.rb +26 -0
  81. data/test/unit/persistence_test.rb +213 -0
  82. data/test/unit/railties/controller_runtime_test.rb +48 -0
  83. data/test/unit/schema_test.rb +27 -0
  84. data/test/unit/scope/batches_test.rb +30 -0
  85. data/test/unit/scope/finder_methods_test.rb +51 -0
  86. data/test/unit/scope/query_methods_test.rb +27 -0
  87. data/test/unit/scoping_test.rb +7 -0
  88. data/test/unit/serialization_test.rb +10 -0
  89. data/test/unit/timestamps_test.rb +27 -0
  90. data/test/unit/types/array_type_test.rb +21 -0
  91. data/test/unit/types/base_type_test.rb +19 -0
  92. data/test/unit/types/boolean_type_test.rb +24 -0
  93. data/test/unit/types/date_type_test.rb +15 -0
  94. data/test/unit/types/float_type_test.rb +17 -0
  95. data/test/unit/types/integer_type_test.rb +19 -0
  96. data/test/unit/types/json_type_test.rb +23 -0
  97. data/test/unit/types/string_type_test.rb +30 -0
  98. data/test/unit/types/time_type_test.rb +19 -0
  99. data/test/unit/validations_test.rb +27 -0
  100. metadata +170 -0
@@ -0,0 +1,59 @@
1
+ module Superstore
2
+ module AttributeMethods
3
+ module Typecasting
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :attribute_definitions
8
+ self.attribute_definitions = {}
9
+
10
+ %w(array boolean date float integer json string time).each do |type|
11
+ instance_eval <<-EOV, __FILE__, __LINE__ + 1
12
+ def #{type}(*args)
13
+ options = args.extract_options!
14
+ args.each do |name|
15
+ attribute(name, options.merge(:type => :#{type}))
16
+ end
17
+ end
18
+ EOV
19
+ end
20
+ end
21
+
22
+ module ClassMethods
23
+ def inherited(child)
24
+ super
25
+ child.attribute_definitions = attribute_definitions.dup
26
+ end
27
+
28
+ #
29
+ # attribute :name, type: :string
30
+ # attribute :ammo, type: Ammo, coder: AmmoCodec
31
+ #
32
+ def attribute(name, options)
33
+ type = options[:type]
34
+ coder = options[:coder]
35
+
36
+ if type.is_a?(Symbol)
37
+ coder = Superstore::Type.get_coder(type) || (raise "Unknown type #{type}")
38
+ elsif coder.nil?
39
+ raise "Must supply a :coder for #{name}"
40
+ end
41
+
42
+ attribute_definitions[name.to_s] = AttributeMethods::Definition.new(name, coder, options)
43
+ end
44
+
45
+ def typecast_attribute(record, name, value)
46
+ if attribute_definition = attribute_definitions[name.to_s]
47
+ attribute_definition.instantiate(record, value)
48
+ else
49
+ raise NoMethodError, "Unknown attribute #{name.inspect}"
50
+ end
51
+ end
52
+
53
+ def coder_for(attribute)
54
+ attribute_definitions[attribute.to_s].coder
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,96 @@
1
+ module Superstore
2
+ module AttributeMethods
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::AttributeMethods
5
+
6
+ included do
7
+ if ActiveModel::VERSION::STRING < '3.2'
8
+ attribute_method_suffix("", "=")
9
+ else
10
+ attribute_method_suffix("=")
11
+ end
12
+
13
+ # (Alias for the protected read_attribute method).
14
+ def [](attr_name)
15
+ read_attribute(attr_name)
16
+ end
17
+
18
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
19
+ # (Alias for the protected write_attribute method).
20
+ def []=(attr_name, value)
21
+ write_attribute(attr_name, value)
22
+ end
23
+ end
24
+
25
+ module ClassMethods
26
+ def inherited(child_class)
27
+ child_class.define_attribute_methods
28
+ super
29
+ end
30
+
31
+ def define_attribute_methods
32
+ return if attribute_methods_generated?
33
+ super(attribute_definitions.keys)
34
+ @attribute_methods_generated = true
35
+ end
36
+
37
+ def attribute_methods_generated?
38
+ @attribute_methods_generated ||= false
39
+ end
40
+ end
41
+
42
+ def write_attribute(name, value)
43
+ @attributes[name.to_s] = self.class.typecast_attribute(self, name, value)
44
+ end
45
+
46
+ def read_attribute(name)
47
+ @attributes[name.to_s]
48
+ end
49
+
50
+ def attribute_exists?(name)
51
+ @attributes.key?(name.to_s)
52
+ end
53
+
54
+ def attributes
55
+ results = {}
56
+ @attributes.each_key do |key|
57
+ results[key] = read_attribute(key)
58
+ end
59
+ results
60
+ end
61
+
62
+ def attributes=(attributes)
63
+ attributes.each do |(name, value)|
64
+ send("#{name}=", value)
65
+ end
66
+ end
67
+
68
+ def method_missing(method_id, *args, &block)
69
+ if !self.class.attribute_methods_generated?
70
+ self.class.define_attribute_methods
71
+ send(method_id, *args, &block)
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ def respond_to?(*args)
78
+ self.class.define_attribute_methods unless self.class.attribute_methods_generated?
79
+ super
80
+ end
81
+
82
+ protected
83
+ def attribute_method?(name)
84
+ !!attribute_definitions[name.to_s]
85
+ end
86
+
87
+ private
88
+ def attribute(name)
89
+ read_attribute(name)
90
+ end
91
+
92
+ def attribute=(name, value)
93
+ write_attribute(name, value)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,33 @@
1
+ require 'set'
2
+
3
+ require 'superstore/log_subscriber'
4
+ require 'superstore/types'
5
+
6
+ module Superstore
7
+ class Base
8
+
9
+ extend ActiveModel::Naming
10
+ include ActiveModel::Conversion
11
+ extend ActiveSupport::DescendantsTracker
12
+ include ActiveModel::Serializers::JSON
13
+
14
+ include Connection
15
+ include Identity
16
+ include Inspect
17
+ include Persistence
18
+ include AttributeMethods
19
+ include Validations
20
+ include AttributeMethods::Dirty
21
+ include AttributeMethods::PrimaryKey
22
+ include AttributeMethods::Typecasting
23
+ include BelongsTo
24
+ include Callbacks
25
+ include Timestamps
26
+ include Scoping
27
+ include Core
28
+ extend Model
29
+
30
+ end
31
+ end
32
+
33
+ ActiveSupport.run_load_hooks(:superstore, Superstore::Base)
@@ -0,0 +1,48 @@
1
+ module Superstore
2
+ module BelongsTo
3
+ class Association
4
+ attr_reader :owner, :reflection
5
+ attr_accessor :record_variable
6
+ delegate :options, to: :reflection
7
+
8
+ def initialize(owner, reflection)
9
+ @owner = owner
10
+ @reflection = reflection
11
+ end
12
+
13
+ def reader
14
+ unless loaded?
15
+ if record_id = owner.send(reflection.foreign_key).presence
16
+ self.record_variable = association_class.find_by_id(record_id)
17
+ else
18
+ self.record_variable = nil
19
+ end
20
+ @loaded = true
21
+ end
22
+
23
+ record_variable
24
+ end
25
+
26
+ def writer(record)
27
+ self.record_variable = record
28
+ @loaded = true
29
+ owner.send("#{reflection.foreign_key}=", record.try(:id))
30
+ if reflection.polymorphic?
31
+ owner.send("#{reflection.polymorphic_column}=", record.class.name)
32
+ end
33
+ end
34
+
35
+ def association_class
36
+ association_class_name.constantize
37
+ end
38
+
39
+ def association_class_name
40
+ reflection.polymorphic? ? owner.send(reflection.polymorphic_column) : reflection.class_name
41
+ end
42
+
43
+ def loaded?
44
+ @loaded
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ module Superstore
2
+ module BelongsTo
3
+ class Builder
4
+ def self.build(model, name, options)
5
+ new(model, name, options).build
6
+ end
7
+
8
+ attr_reader :model, :name, :options
9
+ def initialize(model, name, options)
10
+ @model, @name, @options = model, name, options
11
+ end
12
+
13
+ def build
14
+ define_writer
15
+ define_reader
16
+
17
+ reflection = Superstore::BelongsTo::Reflection.new(model, name, options)
18
+ model.belongs_to_reflections = model.belongs_to_reflections.merge(name => reflection)
19
+ end
20
+
21
+ def mixin
22
+ model.generated_belongs_to_methods
23
+ end
24
+
25
+ def define_writer
26
+ name = self.name
27
+ mixin.redefine_method("#{name}=") do |records|
28
+ belongs_to_association(name).writer(records)
29
+ end
30
+ end
31
+
32
+ def define_reader
33
+ name = self.name
34
+ mixin.redefine_method(name) do
35
+ belongs_to_association(name).reader
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ module Superstore
2
+ module BelongsTo
3
+ class Reflection
4
+ attr_reader :model, :name, :options
5
+ def initialize(model, name, options)
6
+ @model, @name, @options = model, name, options
7
+ end
8
+
9
+ def instance_variable_name
10
+ "@#{name}"
11
+ end
12
+
13
+ def foreign_key
14
+ "#{name}_id"
15
+ end
16
+
17
+ def polymorphic_column
18
+ "#{name}_type"
19
+ end
20
+
21
+ def polymorphic?
22
+ options[:polymorphic]
23
+ end
24
+
25
+ def class_name
26
+ options[:class_name] || name.to_s.camelize
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,63 @@
1
+ module Superstore
2
+ module BelongsTo
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :belongs_to_reflections
7
+ self.belongs_to_reflections = {}
8
+ end
9
+
10
+ module ClassMethods
11
+ # === Options
12
+ # [:class_name]
13
+ # Use if the class cannot be inferred from the association
14
+ # [:polymorphic]
15
+ # Specify if the association is polymorphic
16
+ # Example:
17
+ # class Driver < Superstore::Base
18
+ # end
19
+ # class Truck < Superstore::Base
20
+ # end
21
+ def belongs_to(name, options = {})
22
+ Superstore::BelongsTo::Builder.build(self, name, options)
23
+ end
24
+
25
+ def generated_belongs_to_methods
26
+ @generated_belongs_to_methods ||= begin
27
+ mod = const_set(:GeneratedBelongsToMethods, Module.new)
28
+ include mod
29
+ mod
30
+ end
31
+ end
32
+ end
33
+
34
+ # Returns the belongs_to instance for the given name, instantiating it if it doesn't already exist
35
+ def belongs_to_association(name)
36
+ association = belongs_to_instance_get(name)
37
+
38
+ if association.nil?
39
+ association = Superstore::BelongsTo::Association.new(self, belongs_to_reflections[name])
40
+ belongs_to_instance_set(name, association)
41
+ end
42
+
43
+ association
44
+ end
45
+
46
+ private
47
+ def clear_belongs_to_cache
48
+ belongs_to_cache.clear if persisted?
49
+ end
50
+
51
+ def belongs_to_cache
52
+ @belongs_to_cache ||= {}
53
+ end
54
+
55
+ def belongs_to_instance_get(name)
56
+ belongs_to_cache[name.to_sym]
57
+ end
58
+
59
+ def belongs_to_instance_set(name, association)
60
+ belongs_to_cache[name.to_sym] = association
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,29 @@
1
+ module Superstore
2
+ module Callbacks
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extend ActiveModel::Callbacks
7
+ include ActiveModel::Validations::Callbacks
8
+
9
+ define_model_callbacks :save, :create, :update, :destroy
10
+ end
11
+
12
+ def destroy #:nodoc:
13
+ run_callbacks(:destroy) { super }
14
+ end
15
+
16
+ private
17
+ def write(*args) #:nodoc:
18
+ run_callbacks(:save) { super }
19
+ end
20
+
21
+ def create #:nodoc:
22
+ run_callbacks(:create) { super }
23
+ end
24
+
25
+ def update(*) #:nodoc:
26
+ run_callbacks(:update) { super }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,52 @@
1
+ module Superstore
2
+ module Statements
3
+ DEFAULT_CREATE_KEYSPACE = {
4
+ 'strategy_class' => 'SimpleStrategy',
5
+ 'strategy_options:replication_factor' => 1
6
+ }
7
+
8
+ def create_keyspace(keyspace, options = nil)
9
+ stmt = "CREATE KEYSPACE #{keyspace}"
10
+
11
+ options ||= DEFAULT_CREATE_KEYSPACE
12
+
13
+ system_execute adapter.statement_with_options(stmt, options)
14
+ end
15
+
16
+ def drop_keyspace(keyspace)
17
+ system_execute "DROP KEYSPACE #{keyspace}"
18
+ end
19
+
20
+ def create_column_family(column_family, options = {})
21
+ create_table column_family, options
22
+ end
23
+
24
+ def alter_column_family(column_family, instruction, options = {})
25
+ stmt = "ALTER TABLE #{column_family} #{instruction}"
26
+ keyspace_execute adapter.statement_with_options(stmt, options)
27
+ end
28
+
29
+ def drop_column_family(column_family)
30
+ drop_table column_family
31
+ end
32
+
33
+ def add_index(column_family, column, index_name = nil)
34
+ stmt = "CREATE INDEX #{index_name.nil? ? '' : index_name} ON #{column_family} (#{column})"
35
+ keyspace_execute stmt
36
+ end
37
+
38
+ # If the index was not given a name during creation, the index name is <columnfamily_name>_<column_name>_idx.
39
+ def drop_index(index_name)
40
+ keyspace_execute "DROP INDEX #{index_name}"
41
+ end
42
+
43
+
44
+ def keyspace_execute(cql)
45
+ adapter.schema_execute cql, Superstore::Base.config[:keyspace]
46
+ end
47
+
48
+ def system_execute(cql)
49
+ adapter.schema_execute cql, 'system'
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,47 @@
1
+ module Superstore
2
+ module Tasks
3
+ def dump(io)
4
+ table_names.each do |column_family|
5
+ io.puts run_command("DESCRIBE COLUMNFAMILY #{column_family}")
6
+ io.puts
7
+ end
8
+ end
9
+
10
+ def load(io)
11
+ current_cql = ''
12
+
13
+ io.each_line do |line|
14
+ next if line.blank?
15
+
16
+ current_cql << line.rstrip
17
+
18
+ if current_cql =~ /;$/
19
+ keyspace_execute current_cql
20
+ current_cql = ''
21
+ end
22
+ end
23
+ end
24
+
25
+ def table_names
26
+ run_command('DESCRIBE COLUMNFAMILIES').split.sort
27
+ end
28
+
29
+ private
30
+
31
+ def run_command(command)
32
+ `echo "#{command};" | #{cqlsh} -2 -k #{keyspace} #{server}`.sub(/^(.*)$/, '').strip
33
+ end
34
+
35
+ def cqlsh
36
+ ENV['CQLSH'] || 'cqlsh'
37
+ end
38
+
39
+ def keyspace
40
+ Superstore::Base.config[:keyspace]
41
+ end
42
+
43
+ def server
44
+ Superstore::Base.adapter.servers.first.gsub(/:.*/, '')
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ require 'superstore/cassandra_schema/statements'
2
+ require 'superstore/cassandra_schema/tasks'
3
+
4
+ module Superstore
5
+ class CassandraSchema < Schema
6
+ extend Superstore::Statements
7
+ extend Superstore::Tasks
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ module CassandraCQL
2
+ class Statement
3
+ def self.sanitize(statement, bind_vars=[])
4
+ return statement if bind_vars.empty?
5
+
6
+ bind_vars = bind_vars.dup
7
+ expected_bind_vars = statement.count("?")
8
+
9
+ raise Error::InvalidBindVariable, "Wrong number of bound variables (statement expected #{expected_bind_vars}, was #{bind_vars.size})" if expected_bind_vars != bind_vars.size
10
+
11
+ statement.gsub(/\?/) do
12
+ quote(cast_to_cql(bind_vars.shift))
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ module Superstore
19
+ module Connection
20
+ extend ActiveSupport::Concern
21
+
22
+ module ClassMethods
23
+ def adapter
24
+ @@adapter ||= adapter_class.new(config)
25
+ end
26
+
27
+ def adapter_class
28
+ case config[:adapter]
29
+ when 'hstore'
30
+ Superstore::Adapters::HstoreAdapter
31
+ when nil, 'cassandra'
32
+ Superstore::Adapters::CassandraAdapter
33
+ else
34
+ raise "Unknown adapter #{config[:adapter]}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,59 @@
1
+ module Superstore
2
+ module Core
3
+ extend ActiveSupport::Concern
4
+
5
+ def initialize(attributes=nil)
6
+ @new_record = true
7
+ @destroyed = false
8
+ @attributes = {}
9
+ self.attributes = attributes || {}
10
+ attribute_definitions.each_value do |definition|
11
+ unless definition.default.nil? || attribute_exists?(definition.name)
12
+ @attributes[definition.name] = definition.default
13
+ end
14
+ end
15
+
16
+ yield self if block_given?
17
+ end
18
+
19
+ def initialize_dup(other)
20
+ @attributes = other.attributes
21
+ @attributes['created_at'] = nil
22
+ @attributes['updated_at'] = nil
23
+ @attributes.delete(self.class.primary_key)
24
+ @id = nil
25
+ @new_record = true
26
+ @destroyed = false
27
+ super
28
+ end
29
+
30
+ def to_param
31
+ id
32
+ end
33
+
34
+ def hash
35
+ id.hash
36
+ end
37
+
38
+ module ClassMethods
39
+ def inspect
40
+ if self == Base
41
+ super
42
+ else
43
+ attr_list = @attributes.map do |col, definition| "#{col}: #{definition.type}" end * ', '
44
+ "#{super}(#{attr_list.truncate(140 * 1.7337)})"
45
+ end
46
+ end
47
+ end
48
+
49
+ def ==(comparison_object)
50
+ comparison_object.equal?(self) ||
51
+ (comparison_object.instance_of?(self.class) &&
52
+ comparison_object.id == id)
53
+ end
54
+
55
+ def eql?(comparison_object)
56
+ self == (comparison_object)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,10 @@
1
+ module Superstore
2
+ class CasssandraObjectError < StandardError
3
+ end
4
+
5
+ class RecordNotSaved < CasssandraObjectError
6
+ end
7
+
8
+ class RecordNotFound < CasssandraObjectError
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ require 'securerandom'
2
+
3
+ module Superstore
4
+ module Identity
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :key_generator
9
+
10
+ key do
11
+ SecureRandom.uuid.tr('-','')
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+ # Define a key generator. Default is UUID.
17
+ def key(&block)
18
+ self.key_generator = block
19
+ end
20
+
21
+ def _generate_key(object)
22
+ object.instance_eval(&key_generator)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ module Superstore
2
+ module Inspect
3
+ def inspect
4
+ inspection = ["#{self.class.primary_key}: #{id.inspect}"]
5
+
6
+ @attributes.each do |attribute, value|
7
+ if value.present?
8
+ inspection << "#{attribute}: #{attribute_for_inspect(value)}"
9
+ end
10
+ end
11
+
12
+ "#<#{self.class} #{inspection * ', '}>"
13
+ end
14
+
15
+ def attribute_for_inspect(value)
16
+ if value.is_a?(String) && value.length > 50
17
+ "#{value[0..50]}...".inspect
18
+ elsif value.is_a?(Date) || value.is_a?(Time)
19
+ %("#{value.to_s(:db)}")
20
+ else
21
+ value.inspect
22
+ end
23
+ end
24
+ end
25
+ end