friendly_postgres 0.4.3
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.
- data/.document +2 -0
- data/.gitignore +26 -0
- data/APACHE-LICENSE +202 -0
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTORS.md +7 -0
- data/LICENSE +20 -0
- data/README.md +265 -0
- data/Rakefile +68 -0
- data/TODO.md +5 -0
- data/VERSION +1 -0
- data/examples/friendly.yml +7 -0
- data/friendly.gemspec +232 -0
- data/lib/friendly.rb +54 -0
- data/lib/friendly/associations.rb +7 -0
- data/lib/friendly/associations/association.rb +34 -0
- data/lib/friendly/associations/set.rb +37 -0
- data/lib/friendly/attribute.rb +91 -0
- data/lib/friendly/boolean.rb +10 -0
- data/lib/friendly/cache.rb +24 -0
- data/lib/friendly/cache/by_id.rb +33 -0
- data/lib/friendly/config.rb +5 -0
- data/lib/friendly/data_store.rb +72 -0
- data/lib/friendly/document.rb +257 -0
- data/lib/friendly/document_table.rb +56 -0
- data/lib/friendly/index.rb +73 -0
- data/lib/friendly/memcached.rb +48 -0
- data/lib/friendly/named_scope.rb +17 -0
- data/lib/friendly/newrelic.rb +6 -0
- data/lib/friendly/query.rb +42 -0
- data/lib/friendly/scope.rb +100 -0
- data/lib/friendly/scope_proxy.rb +45 -0
- data/lib/friendly/sequel_monkey_patches.rb +34 -0
- data/lib/friendly/storage.rb +31 -0
- data/lib/friendly/storage_factory.rb +24 -0
- data/lib/friendly/storage_proxy.rb +103 -0
- data/lib/friendly/table.rb +15 -0
- data/lib/friendly/table_creator.rb +48 -0
- data/lib/friendly/time.rb +14 -0
- data/lib/friendly/translator.rb +32 -0
- data/lib/friendly/uuid.rb +148 -0
- data/rails/init.rb +3 -0
- data/spec/config.yml.example +7 -0
- data/spec/fakes/data_store_fake.rb +29 -0
- data/spec/fakes/database_fake.rb +12 -0
- data/spec/fakes/dataset_fake.rb +28 -0
- data/spec/fakes/document.rb +18 -0
- data/spec/fakes/serializer_fake.rb +12 -0
- data/spec/fakes/time_fake.rb +12 -0
- data/spec/integration/ad_hoc_scopes_spec.rb +42 -0
- data/spec/integration/basic_object_lifecycle_spec.rb +114 -0
- data/spec/integration/batch_insertion_spec.rb +29 -0
- data/spec/integration/convenience_api_spec.rb +25 -0
- data/spec/integration/count_spec.rb +12 -0
- data/spec/integration/default_value_spec.rb +15 -0
- data/spec/integration/find_via_cache_spec.rb +101 -0
- data/spec/integration/finder_spec.rb +64 -0
- data/spec/integration/has_many_spec.rb +18 -0
- data/spec/integration/index_spec.rb +57 -0
- data/spec/integration/named_scope_spec.rb +34 -0
- data/spec/integration/pagination_spec.rb +63 -0
- data/spec/integration/scope_chaining_spec.rb +22 -0
- data/spec/integration/table_creator_spec.rb +64 -0
- data/spec/integration/write_through_cache_spec.rb +53 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/unit/associations/association_spec.rb +57 -0
- data/spec/unit/associations/set_spec.rb +43 -0
- data/spec/unit/attribute_spec.rb +105 -0
- data/spec/unit/cache_by_id_spec.rb +102 -0
- data/spec/unit/cache_spec.rb +21 -0
- data/spec/unit/config_spec.rb +4 -0
- data/spec/unit/data_store_spec.rb +188 -0
- data/spec/unit/document_spec.rb +358 -0
- data/spec/unit/document_table_spec.rb +126 -0
- data/spec/unit/friendly_spec.rb +25 -0
- data/spec/unit/index_spec.rb +196 -0
- data/spec/unit/memcached_spec.rb +114 -0
- data/spec/unit/named_scope_spec.rb +16 -0
- data/spec/unit/query_spec.rb +104 -0
- data/spec/unit/scope_proxy_spec.rb +44 -0
- data/spec/unit/scope_spec.rb +113 -0
- data/spec/unit/storage_factory_spec.rb +59 -0
- data/spec/unit/storage_proxy_spec.rb +218 -0
- data/spec/unit/translator_spec.rb +96 -0
- data/website/index.html +210 -0
- data/website/scripts/clipboard.swf +0 -0
- data/website/scripts/shBrushAS3.js +61 -0
- data/website/scripts/shBrushBash.js +66 -0
- data/website/scripts/shBrushCSharp.js +67 -0
- data/website/scripts/shBrushColdFusion.js +102 -0
- data/website/scripts/shBrushCpp.js +99 -0
- data/website/scripts/shBrushCss.js +93 -0
- data/website/scripts/shBrushDelphi.js +57 -0
- data/website/scripts/shBrushDiff.js +43 -0
- data/website/scripts/shBrushErlang.js +54 -0
- data/website/scripts/shBrushGroovy.js +69 -0
- data/website/scripts/shBrushJScript.js +52 -0
- data/website/scripts/shBrushJava.js +59 -0
- data/website/scripts/shBrushJavaFX.js +60 -0
- data/website/scripts/shBrushPerl.js +74 -0
- data/website/scripts/shBrushPhp.js +91 -0
- data/website/scripts/shBrushPlain.js +35 -0
- data/website/scripts/shBrushPowerShell.js +76 -0
- data/website/scripts/shBrushPython.js +66 -0
- data/website/scripts/shBrushRuby.js +57 -0
- data/website/scripts/shBrushScala.js +53 -0
- data/website/scripts/shBrushSql.js +68 -0
- data/website/scripts/shBrushVb.js +58 -0
- data/website/scripts/shBrushXml.js +71 -0
- data/website/scripts/shCore.js +30 -0
- data/website/scripts/shLegacy.js +30 -0
- data/website/styles/friendly.css +103 -0
- data/website/styles/help.png +0 -0
- data/website/styles/ie.css +35 -0
- data/website/styles/magnifier.png +0 -0
- data/website/styles/page_white_code.png +0 -0
- data/website/styles/page_white_copy.png +0 -0
- data/website/styles/print.css +29 -0
- data/website/styles/printer.png +0 -0
- data/website/styles/screen.css +257 -0
- data/website/styles/shCore.css +330 -0
- data/website/styles/shThemeDefault.css +173 -0
- data/website/styles/shThemeDjango.css +176 -0
- data/website/styles/shThemeEclipse.css +190 -0
- data/website/styles/shThemeEmacs.css +175 -0
- data/website/styles/shThemeFadeToGrey.css +177 -0
- data/website/styles/shThemeMidnight.css +175 -0
- data/website/styles/shThemeRDark.css +175 -0
- metadata +302 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Friendly
|
|
2
|
+
class StorageFactory
|
|
3
|
+
attr_reader :table_klass, :index_klass, :cache_klass
|
|
4
|
+
|
|
5
|
+
def initialize(table_klass = DocumentTable, index_klass = Index,
|
|
6
|
+
cache_klass = Cache)
|
|
7
|
+
@table_klass = table_klass
|
|
8
|
+
@index_klass = index_klass
|
|
9
|
+
@cache_klass = cache_klass
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def document_table(*args)
|
|
13
|
+
table_klass.new(*args)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def index(*args)
|
|
17
|
+
index_klass.new(*args)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def cache(*args)
|
|
21
|
+
cache_klass.cache_for(*args)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require 'friendly/storage_factory'
|
|
2
|
+
require 'friendly/table_creator'
|
|
3
|
+
|
|
4
|
+
module Friendly
|
|
5
|
+
class StorageProxy
|
|
6
|
+
attr_reader :klass, :storage_factory, :tables, :table_creator, :caches
|
|
7
|
+
|
|
8
|
+
def initialize(klass, storage_factory = StorageFactory.new,
|
|
9
|
+
table_creator=TableCreator.new)
|
|
10
|
+
super()
|
|
11
|
+
@klass = klass
|
|
12
|
+
@storage_factory = storage_factory
|
|
13
|
+
@table_creator = table_creator
|
|
14
|
+
@tables = [storage_factory.document_table(klass)]
|
|
15
|
+
@caches = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def first(conditions)
|
|
19
|
+
first_from_cache(conditions) do
|
|
20
|
+
index_for(conditions).first(conditions)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def all(query)
|
|
25
|
+
objects = perform_all(query).compact
|
|
26
|
+
if query.preserve_order?
|
|
27
|
+
order = query.conditions[:id]
|
|
28
|
+
objects.sort { |a,b| order.index(a.id) <=> order.index(b.id) }
|
|
29
|
+
else
|
|
30
|
+
objects
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def count(query)
|
|
35
|
+
index_for(query).count(query)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add(*args)
|
|
39
|
+
tables << storage_factory.index(klass, *args)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def cache(fields, options = {})
|
|
43
|
+
caches << storage_factory.cache(klass, fields, options)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def create(document)
|
|
47
|
+
each_store { |s| s.create(document) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def update(document)
|
|
51
|
+
each_store { |s| s.update(document) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def destroy(document)
|
|
55
|
+
stores.reverse.each { |i| i.destroy(document) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def create_tables!
|
|
59
|
+
tables.each { |t| table_creator.create(t) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def index_for(conditions)
|
|
63
|
+
index = tables.detect { |i| i.satisfies?(conditions) }
|
|
64
|
+
if index.nil?
|
|
65
|
+
raise MissingIndex, "No index found to satisfy: #{conditions.inspect}."
|
|
66
|
+
end
|
|
67
|
+
index
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
protected
|
|
71
|
+
def each_store
|
|
72
|
+
stores.each { |s| yield(s) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def stores
|
|
76
|
+
tables + caches
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def first_from_cache(query)
|
|
80
|
+
cache = cache_for(query)
|
|
81
|
+
if cache
|
|
82
|
+
cache.first(query) { yield }
|
|
83
|
+
else
|
|
84
|
+
yield
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def cache_for(query)
|
|
89
|
+
caches.detect { |c| c.satisfies?(query) }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def perform_all(query)
|
|
93
|
+
cache = cache_for(query)
|
|
94
|
+
if cache
|
|
95
|
+
cache.all(query) do |missing_key|
|
|
96
|
+
index_for(query).first(Query.new(:id => missing_key.split("/").last))
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
index_for(query).all(query)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'friendly/storage'
|
|
2
|
+
module Friendly
|
|
3
|
+
class Table < Storage
|
|
4
|
+
attr_reader :datastore
|
|
5
|
+
|
|
6
|
+
def initialize(datastore)
|
|
7
|
+
@datastore = datastore
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def table_name
|
|
11
|
+
raise NotImplementedError, "#{self.class.name}#table_name is not implemented."
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Friendly
|
|
2
|
+
class TableCreator
|
|
3
|
+
attr_reader :db, :attr_klass
|
|
4
|
+
|
|
5
|
+
def initialize(db = Friendly.db, attr_klass = Friendly::Attribute)
|
|
6
|
+
@db = db
|
|
7
|
+
@attr_klass = attr_klass
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def create(table)
|
|
11
|
+
unless db.table_exists?(table.table_name)
|
|
12
|
+
case table
|
|
13
|
+
when DocumentTable
|
|
14
|
+
create_document_table(table)
|
|
15
|
+
when Index
|
|
16
|
+
create_index_table(table)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
protected
|
|
22
|
+
def create_document_table(table)
|
|
23
|
+
db.create_table(table.table_name) do
|
|
24
|
+
primary_key :added_id
|
|
25
|
+
column :id, :bytea
|
|
26
|
+
String :attributes, :text => true
|
|
27
|
+
Time :created_at
|
|
28
|
+
Time :updated_at
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def create_index_table(table)
|
|
33
|
+
attr = attr_klass # close around this please
|
|
34
|
+
|
|
35
|
+
db.create_table(table.table_name) do
|
|
36
|
+
column :id, :bytea
|
|
37
|
+
table.fields.flatten.each do |f|
|
|
38
|
+
klass = table.klass.attributes[f].type
|
|
39
|
+
type = attr.custom_type?(klass) ? attr.sql_type(klass) : klass
|
|
40
|
+
column(f, type)
|
|
41
|
+
end
|
|
42
|
+
primary_key table.fields.flatten + [:id]
|
|
43
|
+
unique :id
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# This class was extracted from the cassandra gem by Evan Weaver
|
|
2
|
+
# As such, it is distributed under the terms of the apache license.
|
|
3
|
+
# See the APACHE-LICENSE file in the root of this project for more information.
|
|
4
|
+
#
|
|
5
|
+
class Time
|
|
6
|
+
def self.stamp
|
|
7
|
+
Time.now.stamp
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def stamp
|
|
11
|
+
to_i * 1_000_000 + usec
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Friendly
|
|
2
|
+
class Translator
|
|
3
|
+
RESERVED_ATTRS = [:id, :created_at, :updated_at].freeze
|
|
4
|
+
|
|
5
|
+
attr_reader :serializer, :time
|
|
6
|
+
|
|
7
|
+
def initialize(serializer = JSON, time = Time)
|
|
8
|
+
@serializer = serializer
|
|
9
|
+
@time = time
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_object(klass, record)
|
|
13
|
+
record.delete(:added_id)
|
|
14
|
+
attributes = serializer.parse(record.delete(:attributes))
|
|
15
|
+
klass.new attributes.merge(record).merge(:new_record => false)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_record(document)
|
|
19
|
+
{ :id => document.id,
|
|
20
|
+
:created_at => document.created_at,
|
|
21
|
+
:updated_at => time.new,
|
|
22
|
+
:attributes => serialize(document) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
protected
|
|
26
|
+
def serialize(document)
|
|
27
|
+
attrs = document.to_hash.reject { |k,v| RESERVED_ATTRS.include?(k) }
|
|
28
|
+
serializer.generate(attrs)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require 'friendly/time'
|
|
2
|
+
require 'friendly/attribute'
|
|
3
|
+
|
|
4
|
+
# This class was extracted from the cassandra gem by Evan Weaver
|
|
5
|
+
# As such, it is distributed under the terms of the apache license.
|
|
6
|
+
# See the APACHE-LICENSE file in the root of this project for more information.
|
|
7
|
+
#
|
|
8
|
+
module Friendly
|
|
9
|
+
# UUID format version 1, as specified in RFC 4122, with jitter in place of the mac address and sequence counter.
|
|
10
|
+
class UUID
|
|
11
|
+
|
|
12
|
+
class InvalidVersion < StandardError; end
|
|
13
|
+
class TypeError < ::TypeError; end
|
|
14
|
+
|
|
15
|
+
GREGORIAN_EPOCH_OFFSET = 0x01B2_1DD2_1381_4000 # Oct 15, 1582
|
|
16
|
+
|
|
17
|
+
VARIANT = 0b1000_0000_0000_0000
|
|
18
|
+
|
|
19
|
+
def initialize(bytes = nil)
|
|
20
|
+
case bytes
|
|
21
|
+
when self.class # UUID
|
|
22
|
+
@bytes = bytes.to_s
|
|
23
|
+
when String
|
|
24
|
+
case bytes.size
|
|
25
|
+
when 16 # Raw byte array
|
|
26
|
+
@bytes = bytes
|
|
27
|
+
when 36 # Human-readable UUID representation; inverse of #to_guid
|
|
28
|
+
elements = bytes.split("-")
|
|
29
|
+
raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (malformed UUID representation)" if elements.size != 5
|
|
30
|
+
@bytes = Array(elements.join).pack('H32')
|
|
31
|
+
else
|
|
32
|
+
raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (invalid bytecount)"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
when Integer
|
|
36
|
+
raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (integer out of range)" if bytes < 0 or bytes > 2**128
|
|
37
|
+
@bytes = [
|
|
38
|
+
(bytes >> 96) & 0xFFFF_FFFF,
|
|
39
|
+
(bytes >> 64) & 0xFFFF_FFFF,
|
|
40
|
+
(bytes >> 32) & 0xFFFF_FFFF,
|
|
41
|
+
bytes & 0xFFFF_FFFF
|
|
42
|
+
].pack("NNNN")
|
|
43
|
+
|
|
44
|
+
when NilClass, Time
|
|
45
|
+
time = (bytes || Time).stamp * 10 + GREGORIAN_EPOCH_OFFSET
|
|
46
|
+
# See http://github.com/spectra/ruby-uuid/
|
|
47
|
+
@bytes = [
|
|
48
|
+
time & 0xFFFF_FFFF,
|
|
49
|
+
time >> 32,
|
|
50
|
+
((time >> 48) & 0x0FFF) | 0x1000,
|
|
51
|
+
# Top 3 bytes reserved
|
|
52
|
+
rand(2**13) | VARIANT,
|
|
53
|
+
rand(2**16),
|
|
54
|
+
rand(2**32)
|
|
55
|
+
].pack("NnnnnN")
|
|
56
|
+
|
|
57
|
+
else
|
|
58
|
+
raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (unknown source class)"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def to_i
|
|
63
|
+
ints = @bytes.unpack("NNNN")
|
|
64
|
+
(ints[0] << 96) +
|
|
65
|
+
(ints[1] << 64) +
|
|
66
|
+
(ints[2] << 32) +
|
|
67
|
+
ints[3]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def version
|
|
71
|
+
time_high = @bytes.unpack("NnnQ")[2]
|
|
72
|
+
version = (time_high & 0xF000).to_s(16)[0].chr.to_i
|
|
73
|
+
version > 0 and version < 6 ? version : -1
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def variant
|
|
77
|
+
@bytes.unpack('QnnN')[1] >> 13
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def to_guid
|
|
81
|
+
elements = @bytes.unpack("NnnCCa6")
|
|
82
|
+
node = elements[-1].unpack('C*')
|
|
83
|
+
elements[-1] = '%02x%02x%02x%02x%02x%02x' % node
|
|
84
|
+
"%08x-%04x-%04x-%02x%02x-%s" % elements
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def to_json(*args)
|
|
88
|
+
to_guid.to_json(*args)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def seconds
|
|
92
|
+
total_usecs / 1_000_000
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def usecs
|
|
96
|
+
total_usecs % 1_000_000
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def <=>(other)
|
|
100
|
+
total_usecs <=> other.send(:total_usecs)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def inspect(long = false)
|
|
104
|
+
"<Friendly::UUID##{object_id} time: #{
|
|
105
|
+
Time.at(seconds).inspect
|
|
106
|
+
}, usecs: #{
|
|
107
|
+
usecs
|
|
108
|
+
} jitter: #{
|
|
109
|
+
@bytes.unpack('QQ')[1]
|
|
110
|
+
}" + (long ? ", version: #{version}, variant: #{variant}, guid: #{to_guid}>" : ">")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def <=>(other)
|
|
114
|
+
self.to_i <=> other.to_i
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def hash
|
|
118
|
+
@bytes.hash
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def eql?(other)
|
|
122
|
+
other.is_a?(Comparable) and @bytes == other.to_s
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def ==(other)
|
|
126
|
+
self.to_s == other.to_s
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def to_s
|
|
130
|
+
@bytes
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def sql_literal(dataset)
|
|
134
|
+
dataset.literal(to_s.to_sequel_blob)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
def total_usecs
|
|
140
|
+
elements = @bytes.unpack("NnnQ")
|
|
141
|
+
(elements[0] + (elements[1] << 32) + ((elements[2] & 0x0FFF) << 48) - GREGORIAN_EPOCH_OFFSET) / 10
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
Friendly::Attribute.register_type(Friendly::UUID, 'bytea') do |s|
|
|
147
|
+
Friendly::UUID.new(s)
|
|
148
|
+
end
|
data/rails/init.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class DataStoreFake
|
|
2
|
+
attr_writer :insert, :all, :first
|
|
3
|
+
attr_reader :inserts, :updates
|
|
4
|
+
|
|
5
|
+
def initialize(opts = {})
|
|
6
|
+
opts.each { |k,v| send("#{k}=", v) }
|
|
7
|
+
@inserts = []
|
|
8
|
+
@updates = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def insert(*args)
|
|
12
|
+
@inserts << args
|
|
13
|
+
@insert
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def update(*args)
|
|
17
|
+
@updates << args
|
|
18
|
+
@update
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def all(*args)
|
|
22
|
+
@all[args]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def first(*args)
|
|
26
|
+
@first[args]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|