Class: Dynamini::Base

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

Constant Summary collapse

BATCH_SIZE =
25
GETTER_PROCS =
{
    integer: Proc.new   { |v| v.to_i },
    datetime: Proc.new  { |v| Time.at(v.to_f) },
    float: Proc.new     { |v| v.to_f },
    symbol: Proc.new    { |v| v.to_sym },
    string: Proc.new    { |v| v },
    boolean: Proc.new   { |v| ['true', '1', '1.0'].include? v },
    array: Proc.new     { |v| v ? JSON.parse(v) : [] }
}
SETTER_PROCS =
{
    datetime: Proc.new  { |v| v.to_f },
    array: Proc.new     { |v| v if v.is_a? Array }
}

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



127
128
129
130
131
132
133
134
135
# File 'lib/dynamini/base.rb', line 127

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)



269
270
271
272
273
274
275
276
277
278
279
# File 'lib/dynamini/base.rb', line 269

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



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

def batch_write_queue
  @batch_write_queue ||= []
end

.in_memoryObject



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

def in_memory
  @in_memory || false
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



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

def attributes
  @attributes
end

Class Method Details

.batch_find(ids = []) ⇒ Object

Raises:

  • (StandardError)


94
95
96
97
98
99
100
101
102
103
104
# File 'lib/dynamini/base.rb', line 94

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

.clientObject



53
54
55
56
57
58
59
60
61
62
# File 'lib/dynamini/base.rb', line 53

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



64
65
66
67
# File 'lib/dynamini/base.rb', line 64

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

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



69
70
71
72
# File 'lib/dynamini/base.rb', line 69

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

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



106
107
108
109
110
111
112
113
114
115
# File 'lib/dynamini/base.rb', line 106

def enqueue_for_save(attributes, options = {})
  model = self.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)


80
81
82
83
# File 'lib/dynamini/base.rb', line 80

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

.find(key) ⇒ Object



74
75
76
77
78
# File 'lib/dynamini/base.rb', line 74

def find(key)
  response = client.get_item(table_name: table_name, key: {hash_key => key.to_s})
  raise 'Item not found.' unless response.item
  self.new(response.item.symbolize_keys, false)
end

.find_or_new(key) ⇒ Object



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

def find_or_new(key)
  response = client.get_item(table_name: table_name, key: {hash_key => key.to_s})
  if response.item
    self.new(response.item.symbolize_keys, false)
  else
    self.new(hash_key => key.to_s)
  end
end

.flush_queue!Object



117
118
119
120
121
# File 'lib/dynamini/base.rb', line 117

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

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



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

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

.hash_keyObject



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

def hash_key
  @hash_key || :id
end

.set_hash_key(key) ⇒ Object



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

def set_hash_key(key)
  @hash_key = key
end

.set_table_name(name) ⇒ Object



28
29
30
# File 'lib/dynamini/base.rb', line 28

def set_table_name(name)
  @table_name = name
end

.table_nameObject



24
25
26
# File 'lib/dynamini/base.rb', line 24

def table_name
  @table_name || name.demodulize.downcase.pluralize
end

Instance Method Details

#==(object) ⇒ Object



138
139
140
# File 'lib/dynamini/base.rb', line 138

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

#assign_attributes(attributes) ⇒ Object



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

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

#changedObject



194
195
196
# File 'lib/dynamini/base.rb', line 194

def changed
  @changed.to_a
end

#changesObject



190
191
192
# File 'lib/dynamini/base.rb', line 190

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

#deleteObject



185
186
187
188
# File 'lib/dynamini/base.rb', line 185

def delete
  delete_from_dynamo
  self
end

#new_record?Boolean

Returns:

  • (Boolean)


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

def new_record?
  @new_record
end

#save(options = {}) ⇒ Object



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

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

#save!(options = {}) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/dynamini/base.rb', line 163

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)


176
177
178
179
180
181
182
183
# File 'lib/dynamini/base.rb', line 176

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



149
150
151
152
# File 'lib/dynamini/base.rb', line 149

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

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



154
155
156
157
# File 'lib/dynamini/base.rb', line 154

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