Class: DataMapper::Adapters::SimpleDBAdapter

Inherits:
AbstractAdapter
  • Object
show all
Includes:
DmAdapterSimpledb::Utils
Defined in:
lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from DmAdapterSimpledb::Utils

#transform_hash

Constructor Details

#initialize(name, normalised_options) ⇒ SimpleDBAdapter

Returns a new instance of SimpleDBAdapter.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 14

def initialize(name, normalised_options)
  super
  @sdb_options = {}
  @sdb_options[:access_key] = options.fetch(:access_key) { 
    options[:user] 
  }
  @sdb_options[:secret_key] = options.fetch(:secret_key) { 
    options[:password] 
  }
  @logger = options.fetch(:logger) { DataMapper.logger }
  @sdb_options[:logger] = @logger
  @sdb_options[:server] = options.fetch(:host) { 'sdb.amazonaws.com' }
  @sdb_options[:port]   = options[:port] || 443 # port may be set but nil
  @sdb_options[:domain] = options.fetch(:domain) { 
    options[:path].to_s.gsub(%r{(^/+)|(/+$)},"") # remove slashes
  }
  @sdb_options[:create_domain] = options.fetch(:create_domain) { false }
  # We do not expect to be saving any nils in future, because now we
  # represent null values by removing the attributes. The representation
  # here is chosen on the basis of it being unlikely to match any strings
  # found in real-world records, as well as being eye-catching in case any
  # nils DO manage to sneak in. It would be preferable if we could disable
  # AWS's nil-token replacement altogether, but that does not appear
  # to be an option.
  @sdb_options[:nil_representation] = "<[<[<NIL>]>]>"
  @sdb_options[:connection_mode] = options.fetch(:connection_mode) {
    :per_thread
  }
  @null_mode   = options.fetch(:null) { false }
  @batch_limit = options.fetch(:batch_limit) {
    SDBTools::Selection::DEFAULT_RESULT_LIMIT
  }.to_i

  if @null_mode
    logger.info "SimpleDB adapter for domain #{domain_name} is in null mode"
  end

  @consistency_policy = 
    normalised_options.fetch(:wait_for_consistency) { false }
  @sdb = options.fetch(:sdb_interface) { nil }
  if @sdb_options[:create_domain] && !domains.include?(domain_name)
    @sdb_options[:logger].info "Creating domain #{domain_name}"
    database.create_domain(domain_name)
  end
end

Instance Attribute Details

#batch_limitObject (readonly)

Returns the value of attribute batch_limit.



7
8
9
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 7

def batch_limit
  @batch_limit
end

#consistency_policyObject

For testing purposes ONLY. Seriously, don’t enable this for production code.



12
13
14
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 12

def consistency_policy
  @consistency_policy
end

#loggerObject

Returns the value of attribute logger.



8
9
10
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 8

def logger
  @logger
end

#sdb_optionsObject (readonly)

Returns the value of attribute sdb_options.



6
7
8
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 6

def sdb_options
  @sdb_options
end

Instance Method Details

#aggregate(query) ⇒ Object

Raises:

  • (NotImplementedError)


148
149
150
151
152
153
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 148

def aggregate(query)
  raise NotImplementedError, "Only count is supported" unless (query.fields.first.operator == :count)
  transaction("AGGREGATE") do |t|
    [selection_from_query(query).count]
  end
end

#create(resources) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 60

def create(resources)
  created = 0
  transaction("CREATE #{resources.size} objects") do
    resources.each do |resource|
      uuid = UUIDTools::UUID.timestamp_create
      initialize_serial(resource, uuid.to_i)

      record     = DmAdapterSimpledb::Record.from_resource(resource)
      attributes = record.writable_attributes
      item_name  = record.item_name
      domain.put(item_name, attributes, :replace => true)
      created += 1
    end
  end
  modified!
  created
end

#delete(collection) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 78

def delete(collection)
  deleted = 0
  transaction("DELETE #{collection.query.conditions}") do
    collection.each do |resource|
      record = DmAdapterSimpledb::Record.from_resource(resource)
      item_name = record.item_name
      domain.delete(item_name)
      deleted += 1
    end

    # TODO no reason we can't select a bunch of item names with an
    # arbitrary query and then delete them.
    raise NotImplementedError.new('Only :eql on delete at the moment') if not_eql_query?(collection.query)
  end
  modified!
  deleted
end

#domainsObject



165
166
167
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 165

def domains
  database.domains
end

#read(query) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 96

def read(query)
  maybe_wait_for_consistency
  transaction("READ #{query.model.name} #{query.conditions}") do |t|
    query = query.dup

    selection = selection_from_query(query)

    records = selection.map{|name, attributes| 
      DmAdapterSimpledb::Record.from_simpledb_hash(name => attributes)
    }

    proto_resources = records.map{|record|
      record.to_resource_hash(query.fields)
    }
    
    # This used to be a simple call to Query#filter_records(), but that
    # caused the result limit to be re-imposed on an already limited result
    # set, with the upshot that too few records were returned. So here we do
    # everything filter_records() does EXCEPT limiting.
    records = proto_resources
    records = records.uniq if query.unique?
    records = query.match_records(records)
    records = query.sort_records(records)

    records
  end
end

#sdb_interfaceObject



169
170
171
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 169

def sdb_interface
  database.sdb
end

#update(attributes, collection) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 124

def update(attributes, collection)
  updated = 0
  transaction("UPDATE #{collection.query} with #{attributes.inspect}") do
    collection.each do |resource|
      updated_resource = resource.dup
      updated_resource.attributes = attributes
      record = DmAdapterSimpledb::Record.from_resource(updated_resource)
      attrs_to_update = record.writable_attributes
      attrs_to_delete = record.deletable_attributes
      item_name       = record.item_name
      unless attrs_to_update.empty?
        domain.put(item_name, attrs_to_update, :replace => true)
      end
      unless attrs_to_delete.empty?
        domain.delete(item_name, attrs_to_delete)
      end
      updated += 1
    end
    raise NotImplementedError.new('Only :eql on delete at the moment') if not_eql_query?(collection.query)
  end
  modified!
  updated
end

#wait_for_consistencyObject

For testing purposes only.



156
157
158
159
160
161
162
163
# File 'lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb', line 156

def wait_for_consistency
  return unless @current_consistency_token
  token = :none
  begin
    results = domain.get('__dm_consistency_token', '__dm_consistency_token')
    tokens  = Array(results[:attributes]['__dm_consistency_token'])
  end until tokens.include?(@current_consistency_token)
end