Class: Dynamini::Base

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Validations
Defined in:
lib/dynamini/base.rb

Overview

Core db interface class.

Constant Summary collapse

BATCH_SIZE =
25
GETTER_PROCS =
{
    integer:  proc { |v| v.to_i },
    date:     proc { |v| v.is_a?(Date) ? v : Time.at(v).to_date },
    time:     proc { |v| Time.at(v.to_f) },
    float:    proc { |v| v.to_f },
    symbol:   proc { |v| v.to_sym },
    string:   proc { |v| v },
    boolean:  proc { |v| v }
}
SETTER_PROCS =
{
    integer:  proc { |v| v.to_i },
    time:     proc { |v| v.to_f },
    float:    proc { |v| v.to_f },
    symbol:   proc { |v| v.to_s },
    string:   proc { |v| v },
    boolean:  proc { |v| v },
    date:     proc { |v| v.to_time.to_f }
}

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = {}, new_record = true) ⇒ Base

Instance Methods



142
143
144
145
146
147
148
149
150
# File 'lib/dynamini/base.rb', line 142

def initialize(attributes = {}, new_record = true)
  @changed = Set.new
  @new_record = new_record
  @attributes = {}

  attributes.each do |k, v|
    write_attribute(k, v, new_record)
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object (private)



350
351
352
353
354
355
356
357
358
359
360
# File 'lib/dynamini/base.rb', line 350

def method_missing(name, *args, &block)
  if write_method?(name)
    attribute = name[0..-2].to_sym
    new_value = args.first
    write_attribute(attribute, new_value)
  elsif read_method?(name)
    read_attribute(name)
  else
    super
  end
end

Class Attribute Details

.batch_write_queueObject



61
62
63
# File 'lib/dynamini/base.rb', line 61

def batch_write_queue
  @batch_write_queue ||= []
end

.in_memoryObject



57
58
59
# File 'lib/dynamini/base.rb', line 57

def in_memory
  @in_memory || false
end

.range_keyObject (readonly)

Returns the value of attribute range_key.



30
31
32
# File 'lib/dynamini/base.rb', line 30

def range_key
  @range_key
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



5
6
7
# File 'lib/dynamini/base.rb', line 5

def attributes
  @attributes
end

Class Method Details

.batch_find(ids = []) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
# File 'lib/dynamini/base.rb', line 109

def batch_find(ids = [])
  return [] if ids.length < 1
  objects = []
  fail StandardError, 'Batch is limited to 100 items' if ids.length > 100
  key_structure = ids.map { |i| { hash_key => i.to_s } }
  response = dynamo_batch_get(key_structure)
  response.responses[table_name].each do |item|
    objects << new(item.symbolize_keys, false)
  end
  objects
end

.clientObject



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/dynamini/base.rb', line 65

def client
  if in_memory
    @client ||= Dynamini::TestClient.new(hash_key)
  else
    @client ||= Aws::DynamoDB::Client.new(
        region: Dynamini.configuration.region,
        access_key_id: Dynamini.configuration.access_key_id,
        secret_access_key: Dynamini.configuration.secret_access_key
    )
  end
end

.create(attributes, options = {}) ⇒ Object



77
78
79
80
# File 'lib/dynamini/base.rb', line 77

def create(attributes, options = {})
  model = new(attributes, true)
  model if model.save(options)
end

.create!(attributes, options = {}) ⇒ Object



82
83
84
85
# File 'lib/dynamini/base.rb', line 82

def create!(attributes, options = {})
  model = new(attributes, true)
  model if model.save!(options)
end

.enqueue_for_save(attributes, options = {}) ⇒ Object



121
122
123
124
125
126
127
128
129
130
# File 'lib/dynamini/base.rb', line 121

def enqueue_for_save(attributes, options = {})
  model = new(attributes, true)
  model.generate_timestamps! unless options[:skip_timestamps]
  if model.valid?
    batch_write_queue << model
    flush_queue! if batch_write_queue.length == BATCH_SIZE
    return true
  end
  false
end

.exists?(key) ⇒ Boolean

Returns:

  • (Boolean)


94
95
96
97
# File 'lib/dynamini/base.rb', line 94

def exists?(key)
  r = client.get_item(table_name: table_name, key: { hash_key => key.to_s })
  r.item.present?
end

.find(hash_value, range_value = nil) ⇒ Object



87
88
89
90
91
92
# File 'lib/dynamini/base.rb', line 87

def find(hash_value, range_value = nil)
  fail 'Range key cannot be blank.' if range_key && range_value.nil?
  response = client.get_item(table_name: table_name, key: create_key_hash(hash_value, range_value))
  raise 'Item not found.' unless response.item
  new(response.item.symbolize_keys, false)
end

.find_or_new(key) ⇒ Object



99
100
101
102
103
104
105
106
107
# File 'lib/dynamini/base.rb', line 99

def find_or_new(key)
  fail 'Key cannot be blank.' if (key.nil? || key == '')
  r = client.get_item(table_name: table_name, key: { hash_key => key.to_s })
  if r.item
    new(r.item.symbolize_keys, false)
  else
    new(hash_key => key.to_s)
  end
end

.flush_queue!Object



132
133
134
135
136
# File 'lib/dynamini/base.rb', line 132

def flush_queue!
  response = dynamo_batch_save(batch_write_queue)
  self.batch_write_queue = []
  response
end

.handle(column, format_class, options = {}) ⇒ Object



48
49
50
51
# File 'lib/dynamini/base.rb', line 48

def handle(column, format_class, options = {})
  define_handled_getter(column, format_class, options)
  define_handled_setter(column, format_class)
end

.hash_keyObject



53
54
55
# File 'lib/dynamini/base.rb', line 53

def hash_key
  @hash_key || :id
end

.set_hash_key(key) ⇒ Object



40
41
42
# File 'lib/dynamini/base.rb', line 40

def set_hash_key(key)
  @hash_key = key
end

.set_range_key(key) ⇒ Object



44
45
46
# File 'lib/dynamini/base.rb', line 44

def set_range_key(key)
  @range_key = key
end

.set_table_name(name) ⇒ Object



36
37
38
# File 'lib/dynamini/base.rb', line 36

def set_table_name(name)
  @table_name = name
end

.table_nameObject



32
33
34
# File 'lib/dynamini/base.rb', line 32

def table_name
  @table_name ||= name.demodulize.tableize
end

Instance Method Details

#==(other) ⇒ Object



152
153
154
# File 'lib/dynamini/base.rb', line 152

def ==(other)
  hash_key == other.hash_key if other.is_a?(self.class)
end

#assign_attributes(attributes) ⇒ Object



156
157
158
159
160
161
# File 'lib/dynamini/base.rb', line 156

def assign_attributes(attributes)
  attributes.each do |key, value|
    write_attribute(key, value)
  end
  nil
end

#changedObject



217
218
219
# File 'lib/dynamini/base.rb', line 217

def changed
  @changed.to_a
end

#changesObject



210
211
212
213
214
215
# File 'lib/dynamini/base.rb', line 210

def changes
  @attributes.select { |attribute| @changed.include?(attribute.to_s) &&
      attribute != self.class.hash_key &&
      attribute != self.class.range_key
  }
end

#deleteObject



205
206
207
208
# File 'lib/dynamini/base.rb', line 205

def delete
  delete_from_dynamo
  self
end

#increment!(attribute_increments, opts = {}) ⇒ Object



198
199
200
201
202
203
# File 'lib/dynamini/base.rb', line 198

def increment!(attribute_increments, opts = {})
  attribute_increments.each do |a, v|
    validate_incrementable_attribute(a, v)
  end
  increment_to_dynamo(attribute_increments, opts)
end

#new_record?Boolean

Returns:

  • (Boolean)


221
222
223
# File 'lib/dynamini/base.rb', line 221

def new_record?
  @new_record
end

#save(options = {}) ⇒ Object



173
174
175
# File 'lib/dynamini/base.rb', line 173

def save(options = {})
  @changed.empty? || (valid? && trigger_save(options))
end

#save!(options = {}) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
# File 'lib/dynamini/base.rb', line 177

def save!(options = {})
  options[:validate] = true if options[:validate].nil?

  unless @changed.empty?
    if (options[:validate] && valid?) || !options[:validate]
      trigger_save(options)
    else
      raise StandardError, errors.full_messages
    end
  end
end

#touch(options = {validate: true}) ⇒ Object

Raises:

  • (RuntimeError)


189
190
191
192
193
194
195
196
# File 'lib/dynamini/base.rb', line 189

def touch(options = {validate: true})
  raise RuntimeError, 'Cannot touch a new record.' if new_record?
  if (options[:validate] && valid?) || !options[:validate]
    trigger_touch
  else
    raise StandardError, errors.full_messages
  end
end

#update_attribute(key, value, options = {}) ⇒ Object



163
164
165
166
# File 'lib/dynamini/base.rb', line 163

def update_attribute(key, value, options = {})
  write_attribute(key, value)
  save!(options)
end

#update_attributes(attributes, options = {}) ⇒ Object



168
169
170
171
# File 'lib/dynamini/base.rb', line 168

def update_attributes(attributes, options = {})
  assign_attributes(attributes)
  save!(options)
end