sdb_dal 0.0.1

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.
@@ -0,0 +1,188 @@
1
+ module SdbDal
2
+
3
+ module Geo
4
+
5
+ class Location
6
+
7
+
8
+ attr_accessor :latitude
9
+ attr_accessor :longitude
10
+
11
+ def initialize(lat,lon)
12
+ self.latitude=lat
13
+ self.longitude=lon
14
+ end
15
+
16
+ def to_address
17
+ if latitude==nil || longitude==nil
18
+ return nil
19
+ end
20
+ address='t'
21
+ right=0
22
+ top=0
23
+ width=180.0
24
+ height=90.0
25
+ 16.times do
26
+
27
+ if latitude>top
28
+ top=top+height/2.0
29
+ if longitude<right
30
+ right=right-width/2.0
31
+ address<<'q'
32
+ else
33
+ right=right+width/2.0
34
+ address << 'r'
35
+ end
36
+ else
37
+
38
+ top=top-height/2.0
39
+ if longitude<right
40
+ right=right-width/2.0
41
+ address<<'t'
42
+ else
43
+ right=right+width/2.0
44
+ address << 's'
45
+ end
46
+ end
47
+ width=width/2.0
48
+ height=height/2.0
49
+ # puts "#{address} w=#{width} h=#{height} r=#{right} t=#{top}"
50
+ end
51
+ return address
52
+ end
53
+ def get_nearby_addresses(zoom_level)
54
+ address=self.to_address
55
+ if address.length>(zoom_level+1)
56
+ address=address.slice(0,zoom_level+1)
57
+ end
58
+
59
+ result=[address]
60
+
61
+ tileWidth=360.0/(2**zoom_level)
62
+ tileHeight=180.0/(2**zoom_level)
63
+
64
+ other=Geo::Location.new(self.latitude+tileHeight,self.longitude+tileWidth)
65
+ result << other.to_address.slice(0,zoom_level+1)
66
+
67
+ other=Geo::Location.new(self.latitude+tileHeight,self.longitude)
68
+ result << other.to_address.slice(0,zoom_level+1)
69
+
70
+ other=Geo::Location.new(self.latitude+tileHeight,self.longitude-tileWidth)
71
+ result << other.to_address.slice(0,zoom_level+1)
72
+
73
+ other=Geo::Location.new(self.latitude-tileHeight,self.longitude+tileWidth)
74
+ result << other.to_address.slice(0,zoom_level+1)
75
+
76
+ other=Geo::Location.new(self.latitude-tileHeight,self.longitude)
77
+ result << other.to_address.slice(0,zoom_level+1)
78
+
79
+ other=Geo::Location.new(self.latitude-tileHeight,self.longitude-tileWidth)
80
+ result << other.to_address.slice(0,zoom_level+1)
81
+
82
+
83
+ other=Geo::Location.new(self.latitude,self.longitude+tileWidth)
84
+ result << other.to_address.slice(0,zoom_level+1)
85
+
86
+ other=Geo::Location.new(self.latitude,self.longitude-tileWidth)
87
+ result << other.to_address.slice(0,zoom_level+1)
88
+
89
+
90
+ end
91
+ def Location.to_location(address)
92
+ width=90.0
93
+ height=45.0
94
+
95
+ index=0
96
+ x=0
97
+ y=0
98
+ length=address.length-1
99
+ length.times do
100
+ index+=1
101
+
102
+ puts address[index].chr
103
+ case address[index].chr
104
+
105
+ when 'r'
106
+ x+=width
107
+ y+=height
108
+ when 'q'
109
+ x-=width
110
+ y+=height
111
+
112
+ when 't'
113
+ x-=width
114
+ y-=height
115
+ when 's'
116
+ x+=width
117
+ y-=height
118
+ end
119
+ width/=2
120
+ height/=2
121
+ end
122
+ loc=Location.new
123
+ loc.latitude=y
124
+ loc.longitude=x
125
+ return loc
126
+ end
127
+
128
+ def self.deg2rad(deg)
129
+ (deg * Math::PI / 180)
130
+ end
131
+
132
+ def self.rad2deg(rad)
133
+ (rad * 180 / Math::PI)
134
+ end
135
+
136
+ def self.acos(rad)
137
+ Math.atan2(Math.sqrt(1 - rad**2), rad)
138
+ end
139
+
140
+ def distance_in_miles( loc2)
141
+ lat2 = loc2.latitude
142
+ lon2 = loc2.longitude
143
+ theta = self.longitude - lon2
144
+
145
+ dist = Math.sin(self.deg2rad(self.latitude)) * Math.sin(deg2rad(lat2)) + Math.cos(self.deg2rad(self.latitude)) * Math.cos(self.deg2rad(lat2)) * Math.cos(deg2rad(theta))
146
+
147
+ dist = self.rad2deg(self.acos(dist))
148
+
149
+ (dist * 60 * 1.1515).round #distance in miles
150
+ end
151
+
152
+ end
153
+
154
+ #mixin
155
+ #locatable object must have latitude, longitude and address attributes
156
+ module Locatable
157
+ def location
158
+ return Geo::Location.new(latitude,longitude)
159
+ end
160
+ def get_nearby(zoom_level=7,order=nil)
161
+ if latitude==nil or longitude==nil
162
+ return []
163
+ end
164
+ result= self.class.find_near(self.location,zoom_level,order)
165
+ result.delete_if{|item|item.id==id}
166
+ return result
167
+ end
168
+
169
+ def find_near(loc,zoom_level=7,order_by=nil,order=:ascending)
170
+ nearby= loc.get_nearby_addresses(zoom_level)
171
+
172
+ address_params=[]
173
+ for address in nearby
174
+
175
+ address_params<<StartsWithCondition.new(self.AttributeDescription(:address),address)
176
+
177
+ end
178
+ orCondition=OrCondition.new(address_params)
179
+
180
+ return find(:all,:limit => 24,:order_by => order_by,:order => order,:conditions=>[orCondition])
181
+
182
+ end
183
+ end
184
+ module Xxx
185
+
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,29 @@
1
+ module SdbDal
2
+
3
+ class IndexDescription < DomainAttributeDescription
4
+ include SdbFormatter
5
+ attr_accessor :columns
6
+
7
+
8
+ def initialize(name,columns)
9
+ self.name=name
10
+ self.columns=columns
11
+ self.value_type =:string
12
+ self.is_primary_key=false
13
+ end
14
+
15
+ def format_index_entry(attribute_descriptions,attribute_values)
16
+ result=""
17
+ columns.each do |column|
18
+ if column.respond_to?(:transform)
19
+ result << column.transform(attribute_values[column.source_column] )
20
+ else
21
+ result<<attribute_descriptions[column].format_for_sdb(attribute_values[column]).to_s
22
+ end
23
+ result<<"&"
24
+ end
25
+ result
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ module SdbDal
2
+
3
+ class IsNullTransform
4
+ attr_accessor :name
5
+ attr_accessor :source_column
6
+ def initialize(source_column )
7
+ self.source_column =source_column
8
+ self.name = "#{name}_is_null".to_sym
9
+ end
10
+ def transform(value)
11
+ value.nil?.to_s
12
+ end
13
+
14
+
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ module SdbDal
2
+
3
+
4
+ class LazyLoadingText
5
+ attr_reader :is_dirty
6
+ attr_reader :has_loaded
7
+ def initialize(get_clob_proc)
8
+ @get_clob_proc=get_clob_proc
9
+ @has_loaded=false
10
+ @is_dirty=false
11
+ end
12
+
13
+ def value=(v)
14
+ @is_dirty=true
15
+ @has_loaded=true
16
+ @value=v
17
+ end
18
+ def value
19
+ if !@did_load
20
+ @value= @get_clob_proc.call
21
+ @has_loaded=true
22
+ end
23
+ return @value
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,168 @@
1
+ module SdbDal
2
+
3
+ require File.dirname(__FILE__) +"/memory_storage.rb"
4
+
5
+ class MemoryRepository
6
+ attr_accessor :use_cache #this here just so interface matches sdb repo
7
+ attr_accessor :storage
8
+
9
+ def initialize(
10
+ sdb_domain= nil,
11
+ clob_bucket= nil,
12
+ aws_key_id= nil,
13
+ aws_secret_key= nil,
14
+ memcache_servers = nil ,
15
+ dummy=nil
16
+ )
17
+ @records={}
18
+ @storage=MemoryStorage.new
19
+ end
20
+ def clear_session_cache
21
+
22
+ end
23
+ def clear
24
+ @records={}
25
+ @storage=MemoryStorage.new
26
+ end
27
+ def save(table_name, primary_key, attributes)
28
+ key=make_cache_key(table_name,primary_key);
29
+ record=@records[key]
30
+ if !record
31
+ record={}
32
+ @records[key]=record
33
+
34
+ end
35
+
36
+ attributes.each do |description,value|
37
+ if description.is_clob
38
+ @storage.put("",make_clob_key(table_name,primary_key,description.name),value)
39
+ else
40
+ record[description.name]=value
41
+ end
42
+ end
43
+ record["metadata%table_name"]=table_name
44
+ record["metadata%primary_key"]=primary_key
45
+ end
46
+ def query_ids(table_name,attribute_descriptions,options)
47
+ primary_key=nil
48
+ attribute_descriptions.each do |name,desc|
49
+ if desc.is_primary_key
50
+ primary_key= name.to_sym
51
+ break
52
+ end
53
+
54
+ end
55
+ objects=query(table_name,attribute_descriptions,options)
56
+ result=[]
57
+ objects.each do | o|
58
+ result<<o[primary_key]
59
+ end
60
+ result
61
+ end
62
+
63
+ def query(table_name,attribute_descriptions,options)
64
+
65
+ if options[:query]
66
+ raise 'query not supported yet'
67
+ end
68
+
69
+ result=[]
70
+ @records.each do |record_key,record|
71
+ ok=true
72
+ if record["metadata%table_name"]!=table_name
73
+ ok=false
74
+ else
75
+ if options.has_key?(:params)
76
+
77
+ options[:params].each do |param_key,param_value|
78
+
79
+ if param_value==:NOT_NULL
80
+ if record[param_key]==nil
81
+ ok=false
82
+ break
83
+ end
84
+ elsif param_value==:NULL
85
+ if record[param_key]!=nil
86
+ ok=false
87
+ break
88
+ end
89
+ elsif param_value.respond_to?(:matches?)
90
+
91
+ if !param_value.matches?(record[param_key])
92
+ ok=false
93
+ break
94
+ end
95
+ elsif !record[param_key] && !param_value
96
+ #ok
97
+ break
98
+ elsif record[param_key]!=param_value
99
+ ok=false
100
+ break
101
+ end
102
+ end
103
+ end
104
+ if options.has_key?(:conditions)
105
+
106
+ options[:conditions].each do |condition|
107
+
108
+ if !condition.matches?(record)
109
+ ok=false
110
+ break
111
+ end
112
+ end
113
+ end
114
+ end
115
+ if ok
116
+ result<<record
117
+ end
118
+
119
+ end
120
+
121
+ if options and options[:order_by]
122
+ result.sort! do |a,b|
123
+ a_value=a[options[:order_by]]
124
+ b_value=b[options[:order_by]]
125
+ if options[:order] && options[:order]!=:ascending
126
+ if b_value
127
+ b_value <=> a_value
128
+ else
129
+ -1
130
+ end
131
+ else
132
+ if a_value
133
+ a_value <=> b_value
134
+ else
135
+ -1
136
+ end
137
+ end
138
+ end
139
+ end
140
+ if options[:limit] and result.length>options[:limit]
141
+ result=result[0..options[:limit]-1]
142
+ end
143
+ return result
144
+ end
145
+ def find_one(table_name, primary_key,attribute_descriptions)#, non_clob_attribute_names, clob_attribute_names)
146
+ return @records[make_cache_key(table_name,primary_key)]
147
+ end
148
+ def get_clob(table_name,primary_key,clob_name)
149
+ return @storage.get("",make_clob_key(table_name,primary_key,clob_name))
150
+
151
+ end
152
+ def destroy(table_name, primary_key)
153
+ key=make_cache_key(table_name,primary_key);
154
+
155
+ if @records.has_key?(key)
156
+ @records.delete(key)
157
+ end
158
+ end
159
+
160
+ private
161
+ def make_cache_key(table_name,primary_key)
162
+ return "cached_objects/#{table_name}/#{CGI.escape(primary_key.to_s)}"
163
+ end
164
+ def make_clob_key(table_name,primary_key,clob_name)
165
+ return "clobs/#{table_name}/#{CGI.escape(primary_key)}/#{clob_name}"
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,29 @@
1
+ module SdbDal
2
+
3
+
4
+ class MemoryStorage
5
+
6
+ def initialize
7
+ @stuff={}
8
+ @attributes={}
9
+ end
10
+ def get(bucket,key)
11
+ return @stuff[bucket+"sdsdw555"+key]
12
+
13
+ end
14
+ def delete(bucket,key)
15
+ @attributes.delete(bucket+"sdsdw555"+key)
16
+ return @stuff.delete(bucket+"sdsdw555"+key)
17
+
18
+ end
19
+ def put(bucket,key,object,attributes=nil)
20
+ @stuff[bucket+"sdsdw555"+key]=object
21
+ @attributes[bucket+"sdsdw555"+key]=attributes if attributes
22
+ end
23
+ def get_content_type(bucket,key)
24
+ return @attributes[bucket+"sdsdw555"+key]['Content-Type'] if @attributes.has_key?(bucket+"sdsdw555"+key)
25
+ return nil
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,53 @@
1
+ module SdbDal
2
+
3
+
4
+ class OrCondition
5
+ attr_accessor :child_conditions
6
+
7
+ def initialize(child_conditions=[])
8
+ self.child_conditions=child_conditions
9
+ end
10
+
11
+ def add(child_condition)
12
+ self.child_conditions<<child_condition
13
+ end
14
+
15
+ def matches?(domain_object)
16
+
17
+ self.child_conditions.each do | condition|
18
+ if condition.respond_to?(:matches?)
19
+ if condition.matches?(domain_object)
20
+ return true
21
+ end
22
+
23
+ end
24
+ end
25
+ return false
26
+ end
27
+ def to_sdb_query
28
+ first =true
29
+ query=""
30
+ count=0
31
+ self.child_conditions.each do | condition|
32
+ count=count+1
33
+ if ! first
34
+ if count>4
35
+ query<< " ] union [ "
36
+ count=0
37
+ else
38
+ query<< " or "
39
+ end
40
+ end
41
+ first=false
42
+
43
+ if condition.respond_to?(:to_sdb_query)
44
+
45
+ query << condition.to_sdb_query
46
+ else
47
+ query << condition.to_s
48
+ end
49
+ end
50
+ return query
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,166 @@
1
+ require 'base64'
2
+ require 'cgi'
3
+ require 'openssl'
4
+ require 'digest/sha1'
5
+ require 'time'
6
+ module SdbDal
7
+ module S3
8
+ # This interface mirrors the AWSAuthConnection class, but instead
9
+ # of performing the operations, this class simply returns a url that can
10
+ # be used to perform the operation with the query string authentication
11
+ # parameters set.
12
+ # PLEASE NOTE - For security reasons, it is HIGHLY RECOMMENDED to avoid
13
+ # using product tokens in signed urls. Please see the README for
14
+ # further details.
15
+ class QueryStringAuthGenerator
16
+ attr_accessor :calling_format
17
+ attr_accessor :expires
18
+ attr_accessor :expires_in
19
+ attr_reader :server
20
+ attr_reader :port
21
+
22
+ # by default, expire in 1 minute
23
+ DEFAULT_EXPIRES_IN = 60
24
+
25
+ def initialize(aws_access_key_id, aws_secret_access_key, tokens=Array.new,
26
+ is_secure=true,
27
+ server=DEFAULT_HOST, port=PORTS_BY_SECURITY[is_secure],
28
+ format=CallingFormat::REGULAR)
29
+ @aws_access_key_id = aws_access_key_id
30
+ @aws_secret_access_key = aws_secret_access_key
31
+ @protocol = is_secure ? 'https' : 'http'
32
+ @server = server
33
+ @port = port
34
+ @calling_format = format
35
+ @tokens = tokens
36
+ # by default expire
37
+ @expires_in = DEFAULT_EXPIRES_IN
38
+
39
+
40
+ end
41
+ def tokens
42
+ @tokens
43
+ end
44
+
45
+ # set the expires value to be a fixed time. the argument can
46
+ # be either a Time object or else seconds since epoch.
47
+ def expires=(value)
48
+ @expires = value
49
+ @expires_in = nil
50
+ end
51
+
52
+ # set the expires value to expire at some point in the future
53
+ # relative to when the url is generated. value is in seconds.
54
+ def expires_in=(value)
55
+ @expires_in = value
56
+ @expires = nil
57
+ end
58
+
59
+ def create_bucket(bucket, headers={})
60
+ return generate_url('PUT', bucket, '', {}, headers)
61
+ end
62
+
63
+ # takes options :prefix, :marker, :max_keys, and :delimiter
64
+ def list_bucket(bucket, options={}, headers={})
65
+ path_args = {}
66
+ options.each { |k, v|
67
+ path_args[k] = v.to_s
68
+ }
69
+ return generate_url('GET', bucket, '', path_args, headers)
70
+ end
71
+
72
+ def delete_bucket(bucket, headers={})
73
+ return generate_url('DELETE', bucket, '', {}, headers)
74
+ end
75
+
76
+ # don't really care what object data is. it's just for conformance with the
77
+ # other interface. If this doesn't work, check tcpdump to see if the client is
78
+ # putting a Content-Type header on the wire.
79
+ def put(bucket, key, object=nil, headers={})
80
+ object = S3Object.new(object) if not object.instance_of? S3Object
81
+ return generate_url('PUT', bucket, CGI::escape(key), {}, merge_meta(headers, object))
82
+ end
83
+
84
+ def get(bucket, key, headers={})
85
+ return generate_url('GET', bucket, CGI::escape(key), {}, headers)
86
+ end
87
+
88
+ def delete(bucket, key, headers={})
89
+ return generate_url('DELETE', bucket, CGI::escape(key), {}, headers)
90
+ end
91
+
92
+ def get_bucket_logging(bucket, headers={})
93
+ return generate_url('GET', bucket, '', {'logging' => nil}, headers)
94
+ end
95
+
96
+ def put_bucket_logging(bucket, logging_xml_doc, headers={})
97
+ return generate_url('PUT', bucket, '', {'logging' => nil}, headers)
98
+ end
99
+
100
+ def get_acl(bucket, key='', headers={})
101
+ return generate_url('GET', bucket, CGI::escape(key), {'acl' => nil}, headers)
102
+ end
103
+
104
+ def get_bucket_acl(bucket, headers={})
105
+ return get_acl(bucket, '', headers)
106
+ end
107
+
108
+ # don't really care what acl_xml_doc is.
109
+ # again, check the wire for Content-Type if this fails.
110
+ def put_acl(bucket, key, acl_xml_doc, headers={})
111
+ return generate_url('PUT', bucket, CGI::escape(key), {'acl' => nil}, headers)
112
+ end
113
+
114
+ def put_bucket_acl(bucket, acl_xml_doc, headers={})
115
+ return put_acl(bucket, '', acl_xml_doc, headers)
116
+ end
117
+
118
+ def list_all_my_buckets(headers={})
119
+ return generate_url('GET', '', '', {}, headers)
120
+ end
121
+
122
+
123
+ private
124
+ # generate a url with the appropriate query string authentication
125
+ # parameters set.
126
+ def generate_url(method, bucket="", key="", path_args={}, headers={})
127
+ expires = 0
128
+ if not @expires_in.nil?
129
+ expires = Time.now.to_i + @expires_in
130
+ elsif not @expires.nil?
131
+ expires = @expires
132
+ else
133
+ raise "invalid expires state"
134
+ end
135
+ if @tokens && !@tokens.empty?
136
+ headers[AMAZON_TOKEN_HEADER_PREFIX]=@tokens.join(',')
137
+ path_args[AMAZON_TOKEN_HEADER_PREFIX]=@tokens.join(',')
138
+ end
139
+ canonical_string =
140
+ S3::canonical_string(method, bucket, key, path_args, headers, expires)
141
+ encoded_canonical =
142
+ S3::encode(@aws_secret_access_key, canonical_string)
143
+
144
+ url = CallingFormat.build_url_base(@protocol, @server, @port, bucket, @calling_format)
145
+
146
+ path_args["Signature"] = encoded_canonical.to_s
147
+ path_args["Expires"] = expires.to_s
148
+ path_args["AWSAccessKeyId"] = @aws_access_key_id.to_s
149
+
150
+ arg_string = S3.path_args_hash_to_string(path_args)
151
+
152
+ return "#{url}/#{key}?#{arg_string}"
153
+ end
154
+
155
+ def merge_meta(headers, object)
156
+ final_headers = headers.clone
157
+ if not object.nil? and not object.metadata.nil?
158
+ object.metadata.each do |k, v|
159
+ final_headers[METADATA_PREFIX + k] = v
160
+ end
161
+ end
162
+ return final_headers
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,28 @@
1
+ module SdbDal
2
+
3
+
4
+ class Reference
5
+ attr_accessor :target_class
6
+ attr_accessor :primary_key
7
+ attr_accessor :index
8
+ def initialize(options={})
9
+ if options.has_key?(:target)
10
+ self.target_class=options[:target].class.name.to_sym
11
+ self.primary_key=options[:target].primary_key
12
+ end
13
+ self.index=-1
14
+ if options.has_key?(:index)
15
+ self.index=options[:index]
16
+ end
17
+ end
18
+ def targets?(item)
19
+ return false unless item
20
+ self.target_class==item.class.name.to_sym and self.primary_key==item.primary_key
21
+ end
22
+ def get_target
23
+ the_class=Kernel.const_get(self.target_class)
24
+ the_class.find(self.primary_key)
25
+
26
+ end
27
+ end
28
+ end