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,56 @@
|
|
|
1
|
+
require 'friendly/table'
|
|
2
|
+
|
|
3
|
+
module Friendly
|
|
4
|
+
class DocumentTable < Table
|
|
5
|
+
attr_reader :klass, :translator
|
|
6
|
+
|
|
7
|
+
def initialize(klass, datastore=Friendly.datastore, translator=Translator.new)
|
|
8
|
+
super(datastore)
|
|
9
|
+
@klass = klass
|
|
10
|
+
@translator = translator
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def table_name
|
|
14
|
+
klass.table_name
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def satisfies?(query)
|
|
18
|
+
query.conditions.keys == [:id] && !query.order
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create(document)
|
|
22
|
+
record = translator.to_record(document)
|
|
23
|
+
datastore.insert(document, record)
|
|
24
|
+
update_document(document, record)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def update(document)
|
|
28
|
+
record = translator.to_record(document)
|
|
29
|
+
datastore.update(document, document.id, record)
|
|
30
|
+
update_document(document, record)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def destroy(document)
|
|
34
|
+
datastore.delete(document, document.id)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def first(query)
|
|
38
|
+
record = datastore.first(klass, query)
|
|
39
|
+
record && to_object(record)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def all(query)
|
|
43
|
+
datastore.all(klass, query).map { |r| to_object(r) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
protected
|
|
47
|
+
def update_document(document, record)
|
|
48
|
+
attrs = record.reject { |k,v| k == :attributes }.merge(:new_record => false)
|
|
49
|
+
document.attributes = attrs
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_object(record)
|
|
53
|
+
translator.to_object(klass, record)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'friendly/table'
|
|
2
|
+
|
|
3
|
+
module Friendly
|
|
4
|
+
class Index < Table
|
|
5
|
+
attr_reader :klass, :fields, :datastore
|
|
6
|
+
|
|
7
|
+
def initialize(klass, fields, datastore = Friendly.datastore)
|
|
8
|
+
@klass = klass
|
|
9
|
+
@fields = fields
|
|
10
|
+
@datastore = datastore
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def table_name
|
|
14
|
+
["index", klass.table_name, "on", fields.join("_and_")].join("_")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def satisfies?(query)
|
|
18
|
+
exact_match?(query) || valid_partial_match?(query)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def first(query)
|
|
22
|
+
row = datastore.first(self, query)
|
|
23
|
+
row && klass.first(:id => row[:id])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def all(query)
|
|
27
|
+
ids = datastore.all(self, query).map { |row| row[:id] }
|
|
28
|
+
klass.all(:id => ids, :preserve_order! => !query.order.nil?)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def count(query)
|
|
32
|
+
datastore.count(self, query)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create(document)
|
|
36
|
+
datastore.insert(self, record(document))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def update(document)
|
|
40
|
+
datastore.update(self, document.id, record(document))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def destroy(document)
|
|
44
|
+
datastore.delete(self, document.id)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
protected
|
|
48
|
+
def exact_match?(query)
|
|
49
|
+
query.conditions.keys.map { |f| f.to_s }.sort ==
|
|
50
|
+
fields.map { |f| f.to_s }.sort &&
|
|
51
|
+
valid_order?(query.order)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def valid_partial_match?(query)
|
|
55
|
+
condition_fields = query.conditions.keys
|
|
56
|
+
sorted = condition_fields.sort { |a,b| field_index(a) <=> field_index(b) }
|
|
57
|
+
sorted << query.order.expression if query.order
|
|
58
|
+
sorted.zip(fields).all? { |a,b| a == b }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def valid_order?(order)
|
|
62
|
+
order.nil? || order.expression == fields.last
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def field_index(attr)
|
|
66
|
+
fields.index(attr) || 0
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def record(document)
|
|
70
|
+
Hash[*(fields + [:id]).map { |f| [f, document.send(f)] }.flatten]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Friendly
|
|
2
|
+
class Memcached
|
|
3
|
+
attr_reader :cache
|
|
4
|
+
|
|
5
|
+
def initialize(cache)
|
|
6
|
+
@cache = cache
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def set(key, value)
|
|
10
|
+
@cache.set(key, value)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get(key)
|
|
14
|
+
@cache.get(key)
|
|
15
|
+
rescue ::Memcached::NotFound
|
|
16
|
+
if block_given?
|
|
17
|
+
miss(key) { yield }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def multiget(keys)
|
|
22
|
+
return {} if keys.empty?
|
|
23
|
+
|
|
24
|
+
hits = @cache.get(keys)
|
|
25
|
+
missing_keys = keys - hits.keys
|
|
26
|
+
|
|
27
|
+
if !missing_keys.empty? && block_given?
|
|
28
|
+
missing_keys.each do |missing_key|
|
|
29
|
+
hits.merge!(missing_key => miss(missing_key) { yield(missing_key) })
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
hits
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def delete(key)
|
|
37
|
+
cache.delete(key)
|
|
38
|
+
rescue ::Memcached::NotFound
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
def miss(key)
|
|
43
|
+
value = yield
|
|
44
|
+
@cache.set(key, value)
|
|
45
|
+
value
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'friendly/scope'
|
|
2
|
+
|
|
3
|
+
module Friendly
|
|
4
|
+
class NamedScope
|
|
5
|
+
attr_reader :klass, :parameters, :scope_klass
|
|
6
|
+
|
|
7
|
+
def initialize(klass, parameters, scope_klass = Scope)
|
|
8
|
+
@klass = klass
|
|
9
|
+
@parameters = parameters
|
|
10
|
+
@scope_klass = scope_klass
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def scope
|
|
14
|
+
@scope_klass.new(@klass, @parameters)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Friendly
|
|
2
|
+
class Query
|
|
3
|
+
attr_reader :conditions, :limit, :order,
|
|
4
|
+
:preserve_order, :offset, :uuid_klass,
|
|
5
|
+
:page, :per_page
|
|
6
|
+
|
|
7
|
+
def initialize(parameters, uuid_klass = UUID)
|
|
8
|
+
@uuid_klass = uuid_klass
|
|
9
|
+
@conditions = parameters.reject { |k,v| k.to_s =~ /!$/ }
|
|
10
|
+
@page = (parameters[:page!] || 1).to_i
|
|
11
|
+
|
|
12
|
+
[:per_page!, :limit!, :offset!, :order!, :preserve_order!].each do |p|
|
|
13
|
+
instance_variable_set("@#{p.to_s.gsub(/!/, '')}", parameters[p])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
handle_pagination if per_page
|
|
17
|
+
convert_ids_to_uuids
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def preserve_order?
|
|
21
|
+
preserve_order
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def offset?
|
|
25
|
+
offset
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
def convert_ids_to_uuids
|
|
30
|
+
if conditions[:id] && conditions[:id].is_a?(Array)
|
|
31
|
+
conditions[:id] = conditions[:id].map { |i| uuid_klass.new(i) }
|
|
32
|
+
elsif conditions[:id]
|
|
33
|
+
conditions[:id] = uuid_klass.new(conditions[:id])
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def handle_pagination
|
|
38
|
+
@limit = per_page
|
|
39
|
+
@offset = (page - 1) * per_page
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module Friendly
|
|
2
|
+
class Scope
|
|
3
|
+
attr_reader :klass, :parameters
|
|
4
|
+
|
|
5
|
+
def initialize(klass, parameters)
|
|
6
|
+
@klass = klass
|
|
7
|
+
@parameters = parameters
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Fetch all documents at this scope.
|
|
11
|
+
#
|
|
12
|
+
# @param [Hash] extra_parameters add extra parameters to this query.
|
|
13
|
+
#
|
|
14
|
+
def all(extra_parameters = {})
|
|
15
|
+
klass.all(params(extra_parameters))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Fetch the first document at this scope.
|
|
19
|
+
#
|
|
20
|
+
# @param [Hash] extra_parameters add extra parameters to this query.
|
|
21
|
+
#
|
|
22
|
+
def first(extra_parameters = {})
|
|
23
|
+
klass.first(params(extra_parameters))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Paginate the documents at this scope.
|
|
27
|
+
#
|
|
28
|
+
# @param [Hash] extra_parameters add extra parameters to this query.
|
|
29
|
+
# @return WillPaginate::Collection
|
|
30
|
+
#
|
|
31
|
+
def paginate(extra_parameters = {})
|
|
32
|
+
klass.paginate(params(extra_parameters))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Build an object at this scope.
|
|
36
|
+
#
|
|
37
|
+
# e.g.
|
|
38
|
+
# Post.scope(:name => "James").build.name # => "James"
|
|
39
|
+
#
|
|
40
|
+
# @param [Hash] extra_parameters add extra parameters to this query.
|
|
41
|
+
#
|
|
42
|
+
def build(extra_parameters = {})
|
|
43
|
+
klass.new(params_without_modifiers(extra_parameters))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Create an object at this scope.
|
|
47
|
+
#
|
|
48
|
+
# e.g.
|
|
49
|
+
# @post = Post.scope(:name => "James").create
|
|
50
|
+
# @post.new_record? # => false
|
|
51
|
+
# @post.name # => "James"
|
|
52
|
+
#
|
|
53
|
+
# @param [Hash] extra_parameters add extra parameters to this query.
|
|
54
|
+
#
|
|
55
|
+
def create(extra_parameters = {})
|
|
56
|
+
klass.create(params_without_modifiers(extra_parameters))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Override #respond_to? so that we can return true when it's another named_scope.
|
|
60
|
+
#
|
|
61
|
+
# @override
|
|
62
|
+
#
|
|
63
|
+
def respond_to?(method_name, include_private = false)
|
|
64
|
+
klass.has_named_scope?(method_name) || super
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Use method_missing to respond to other named scopes on klass.
|
|
68
|
+
#
|
|
69
|
+
# @override
|
|
70
|
+
#
|
|
71
|
+
def method_missing(method_name, *args, &block)
|
|
72
|
+
respond_to?(method_name) ? chain_with(method_name) : super
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Chain with another one of klass's named_scopes.
|
|
76
|
+
#
|
|
77
|
+
# @param [Symbol] scope_name The name of the scope to chain with.
|
|
78
|
+
#
|
|
79
|
+
def chain_with(scope_name)
|
|
80
|
+
self + klass.send(scope_name)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Create a new Scope that is the combination of self and other, where other takes priority
|
|
84
|
+
#
|
|
85
|
+
# @param [Friendly::Scope] other The scope to merge with.
|
|
86
|
+
#
|
|
87
|
+
def +(other_scope)
|
|
88
|
+
self.class.new(klass, parameters.merge(other_scope.parameters))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
protected
|
|
92
|
+
def params(extra)
|
|
93
|
+
parameters.merge(extra)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def params_without_modifiers(extra)
|
|
97
|
+
params(extra).reject { |k,v| k.to_s =~ /!$/ }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'friendly/named_scope'
|
|
2
|
+
|
|
3
|
+
module Friendly
|
|
4
|
+
class ScopeProxy
|
|
5
|
+
attr_reader :klass, :scope_klass, :scopes
|
|
6
|
+
|
|
7
|
+
def initialize(klass, scope_klass = Scope)
|
|
8
|
+
@klass = klass
|
|
9
|
+
@scope_klass = scope_klass
|
|
10
|
+
@scopes = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add_named(name, parameters)
|
|
14
|
+
scopes[name] = parameters
|
|
15
|
+
add_scope_method_to_klass(name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def get(name)
|
|
19
|
+
scopes[name]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get_instance(name)
|
|
23
|
+
scope_klass.new(klass, get(name))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def ad_hoc(parameters)
|
|
27
|
+
scope_klass.new(klass, parameters)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def has_named_scope?(name)
|
|
31
|
+
scopes.has_key?(name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
protected
|
|
35
|
+
def add_scope_method_to_klass(scope_name)
|
|
36
|
+
klass.class_eval do
|
|
37
|
+
eval <<-__END__
|
|
38
|
+
def self.#{scope_name}
|
|
39
|
+
scope_proxy.get_instance(:#{scope_name})
|
|
40
|
+
end
|
|
41
|
+
__END__
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'sequel'
|
|
2
|
+
|
|
3
|
+
# Out of the box, Sequel uses IS TRUE/FALSE for boolean parameters
|
|
4
|
+
# This prevents MySQL from using indexes.
|
|
5
|
+
#
|
|
6
|
+
# This patch fixes that.
|
|
7
|
+
module Sequel
|
|
8
|
+
module SQL
|
|
9
|
+
class BooleanExpression
|
|
10
|
+
def self.from_value_pairs(pairs, op=:AND, negate=false)
|
|
11
|
+
pairs = pairs.collect do |l,r|
|
|
12
|
+
ce = case r
|
|
13
|
+
when Range
|
|
14
|
+
new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
|
|
15
|
+
when Array, ::Sequel::Dataset, SQLArray
|
|
16
|
+
new(:IN, l, r)
|
|
17
|
+
when NegativeBooleanConstant
|
|
18
|
+
new(:"IS NOT", l, r.constant)
|
|
19
|
+
when BooleanConstant
|
|
20
|
+
new(:IS, l, r.constant)
|
|
21
|
+
when NilClass
|
|
22
|
+
new(:IS, l, r)
|
|
23
|
+
when Regexp
|
|
24
|
+
StringExpression.like(l, r)
|
|
25
|
+
else
|
|
26
|
+
new(:'=', l, r)
|
|
27
|
+
end
|
|
28
|
+
negate ? invert(ce) : ce
|
|
29
|
+
end
|
|
30
|
+
pairs.length == 1 ? pairs.at(0) : new(op, *pairs)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Friendly
|
|
2
|
+
class Storage
|
|
3
|
+
def create(document)
|
|
4
|
+
raise NotImplementedError, "#{self.class.name}#create is not implemented."
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def update(document)
|
|
8
|
+
raise NotImplementedError, "#{self.class.name}#update is not implemented."
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def destroy(document)
|
|
12
|
+
raise NotImplementedError, "#{self.class.name}#destroy is not implemented."
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def first(conditions)
|
|
16
|
+
raise NotImplementedError, "#{self.class.name}#first is not implemented."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def all(conditions)
|
|
20
|
+
raise NotImplementedError, "#{self.class.name}#all is not implemented."
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def count(query)
|
|
24
|
+
raise NotImplementedError, "#{self.class.name}#count is not implemented."
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def satisfies?(conditions)
|
|
28
|
+
raise NotImplementedError, "#{self.class.name}#satisfies? is not implemented."
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|