kangaroo 0.0.1.pre
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/.gitignore +1 -0
- data/.rspec +2 -0
- data/.yardopts +6 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +66 -0
- data/LICENSE +22 -0
- data/README.md +65 -0
- data/Rakefile +36 -0
- data/config/kangaroo.yml.sample +12 -0
- data/docs/AdditionalServices.md +3 -0
- data/docs/Architecture.md +50 -0
- data/docs/Installation.md +73 -0
- data/docs/Usage.md +161 -0
- data/features/connection.feature +5 -0
- data/features/env.rb +8 -0
- data/features/step_definitions/basic_steps.rb +24 -0
- data/features/step_definitions/connection_steps.rb +13 -0
- data/features/support/test.yml +11 -0
- data/features/utility_services.feature +39 -0
- data/kangaroo.gemspec +29 -0
- data/lib/kangaroo.rb +6 -0
- data/lib/kangaroo/exception.rb +7 -0
- data/lib/kangaroo/model/attributes.rb +124 -0
- data/lib/kangaroo/model/base.rb +70 -0
- data/lib/kangaroo/model/condition_normalizer.rb +63 -0
- data/lib/kangaroo/model/default_attributes.rb +22 -0
- data/lib/kangaroo/model/field.rb +20 -0
- data/lib/kangaroo/model/finder.rb +92 -0
- data/lib/kangaroo/model/inspector.rb +55 -0
- data/lib/kangaroo/model/open_object_orm.rb +117 -0
- data/lib/kangaroo/model/persistence.rb +180 -0
- data/lib/kangaroo/model/relation.rb +212 -0
- data/lib/kangaroo/model/remote_execute.rb +29 -0
- data/lib/kangaroo/railtie.rb +13 -0
- data/lib/kangaroo/ruby_adapter/base.rb +28 -0
- data/lib/kangaroo/ruby_adapter/class_definition.rb +46 -0
- data/lib/kangaroo/ruby_adapter/fields.rb +18 -0
- data/lib/kangaroo/util/client.rb +59 -0
- data/lib/kangaroo/util/configuration.rb +82 -0
- data/lib/kangaroo/util/database.rb +92 -0
- data/lib/kangaroo/util/loader.rb +98 -0
- data/lib/kangaroo/util/loader/model.rb +21 -0
- data/lib/kangaroo/util/loader/namespace.rb +15 -0
- data/lib/kangaroo/util/proxy.rb +35 -0
- data/lib/kangaroo/util/proxy/common.rb +111 -0
- data/lib/kangaroo/util/proxy/db.rb +34 -0
- data/lib/kangaroo/util/proxy/object.rb +153 -0
- data/lib/kangaroo/util/proxy/report.rb +24 -0
- data/lib/kangaroo/util/proxy/superadmin.rb +71 -0
- data/lib/kangaroo/util/proxy/wizard.rb +25 -0
- data/lib/kangaroo/util/proxy/workflow.rb +14 -0
- data/lib/kangaroo/version.rb +3 -0
- data/spec/model/attributes_spec.rb +70 -0
- data/spec/model/base_spec.rb +19 -0
- data/spec/model/default_attributes_spec.rb +37 -0
- data/spec/model/finder_spec.rb +104 -0
- data/spec/model/inspector_spec.rb +56 -0
- data/spec/model/open_object_orm_spec.rb +134 -0
- data/spec/model/persistence_spec.rb +53 -0
- data/spec/model/relation_spec.rb +122 -0
- data/spec/ruby_adapter/class_definition_spec.rb +51 -0
- data/spec/server_helper.rb +167 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/test_env/test.yml +11 -0
- data/spec/util/configuration_spec.rb +36 -0
- data/spec/util/loader_spec.rb +50 -0
- data/spec/util/proxy_spec.rb +61 -0
- metadata +260 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'active_support/core_ext/module'
|
2
|
+
|
3
|
+
module Kangaroo
|
4
|
+
module Model
|
5
|
+
module Finder
|
6
|
+
RELATION_DELEGATES = %w(where limit offset order select context reverse)
|
7
|
+
delegate *(RELATION_DELEGATES + [:to => :relation])
|
8
|
+
|
9
|
+
# Retrieve all records
|
10
|
+
#
|
11
|
+
# @return [Array] records
|
12
|
+
def all
|
13
|
+
relation.all
|
14
|
+
end
|
15
|
+
|
16
|
+
# ActiveRecord-ish find method
|
17
|
+
#
|
18
|
+
# @overload find(id)
|
19
|
+
# Find a record by id
|
20
|
+
# @param [Number] id
|
21
|
+
# @overload find(keyword)
|
22
|
+
# Find all, first or last record
|
23
|
+
# @param [String, Symbol] keyword :all, :first or :last
|
24
|
+
def find id_or_keyword
|
25
|
+
case id_or_keyword
|
26
|
+
when :all, 'all'
|
27
|
+
all
|
28
|
+
when :first, 'first'
|
29
|
+
first
|
30
|
+
when :last, 'last'
|
31
|
+
last
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def exists? ids
|
38
|
+
where(:id => ids).exists?
|
39
|
+
end
|
40
|
+
|
41
|
+
# Retrieve first record
|
42
|
+
#
|
43
|
+
# @return record
|
44
|
+
def first
|
45
|
+
relation.first
|
46
|
+
end
|
47
|
+
|
48
|
+
# Retrieve last record
|
49
|
+
#
|
50
|
+
# @return record
|
51
|
+
def last
|
52
|
+
relation.last
|
53
|
+
end
|
54
|
+
|
55
|
+
# Count number of records
|
56
|
+
#
|
57
|
+
# @return [Number]
|
58
|
+
def count
|
59
|
+
count_by
|
60
|
+
end
|
61
|
+
|
62
|
+
# @private
|
63
|
+
# Search, read and instantiate records at once
|
64
|
+
#
|
65
|
+
# @param [Array, Hash, String] conditions
|
66
|
+
# @param [Hash] search_options
|
67
|
+
# @param [Hash] read_options
|
68
|
+
# @return [Array]
|
69
|
+
def search_and_read conditions, search_options = {}, read_options = {}
|
70
|
+
ids = search conditions, search_options.merge(:count => false)
|
71
|
+
return [] if ids.blank?
|
72
|
+
|
73
|
+
read ids, read_options
|
74
|
+
end
|
75
|
+
|
76
|
+
# @private
|
77
|
+
# Count objects matching the conditions
|
78
|
+
#
|
79
|
+
# @param [Array, Hash, String] conditions
|
80
|
+
# @param [Hash] search options
|
81
|
+
# @return [Number]
|
82
|
+
def count_by conditions = [], search_options = {}
|
83
|
+
search conditions, search_options.merge(:count => true)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @private
|
87
|
+
def relation
|
88
|
+
Relation.new self
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Kangaroo
|
2
|
+
module Model
|
3
|
+
module Inspector
|
4
|
+
# @private
|
5
|
+
def self.included klass
|
6
|
+
klass.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
# @private
|
10
|
+
def inspect
|
11
|
+
inspect_wrap do |str|
|
12
|
+
str << [inspect_id, *inspect_attributes].join(', ')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def inspect_id
|
18
|
+
"id: #{id.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect_attributes
|
22
|
+
attributes.to_a.map do |key_val|
|
23
|
+
name, value = key_val
|
24
|
+
[name, value.inspect].join ": "
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect_wrap
|
29
|
+
"".tap do |str|
|
30
|
+
str << "<#{self.class.name} "
|
31
|
+
yield str
|
32
|
+
str << ">"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @private
|
37
|
+
module ClassMethods
|
38
|
+
def inspect
|
39
|
+
inspect_wrap do |str|
|
40
|
+
str << ['id', *attribute_names].join(', ')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def inspect_wrap
|
46
|
+
"".tap do |str|
|
47
|
+
str << "<#{name} "
|
48
|
+
yield str
|
49
|
+
str << ">"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'kangaroo/model/condition_normalizer'
|
2
|
+
require 'kangaroo/model/field'
|
3
|
+
|
4
|
+
module Kangaroo
|
5
|
+
module Model
|
6
|
+
module OpenObjectOrm
|
7
|
+
include ConditionNormalizer
|
8
|
+
|
9
|
+
# Search for records in the OpenERP database
|
10
|
+
#
|
11
|
+
# @param [Hash, Array, String] conditions
|
12
|
+
# @param [Hash] options
|
13
|
+
# @option options [Number] limit
|
14
|
+
# @option options [Number] offset
|
15
|
+
# @option options [String] order
|
16
|
+
# @option options [Hash] context
|
17
|
+
# @option options [boolean] count
|
18
|
+
# @return list of ids
|
19
|
+
def search conditions, options = {}
|
20
|
+
options = {
|
21
|
+
:limit => false,
|
22
|
+
:offset => 0
|
23
|
+
}.merge(options)
|
24
|
+
|
25
|
+
remote.search normalize_conditions(conditions),
|
26
|
+
options[:offset],
|
27
|
+
options[:limit],
|
28
|
+
options[:order],
|
29
|
+
options[:context],
|
30
|
+
options[:count]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Read records and instantiate them
|
34
|
+
#
|
35
|
+
# @param [Array] ids
|
36
|
+
# @param [Hash] options
|
37
|
+
# @option options [Array] fields
|
38
|
+
# @option options [Hash] context
|
39
|
+
# @return [Array] list of Kangaroo::Model::Base instances
|
40
|
+
def read ids, options = {}
|
41
|
+
fields = options[:fields]
|
42
|
+
fields = attribute_names if options[:fields].blank?
|
43
|
+
context = options[:context]
|
44
|
+
|
45
|
+
[].tap do |result|
|
46
|
+
remote.read(ids, fields, context).each do |attributes|
|
47
|
+
position = ids.index(attributes[:id].to_i)
|
48
|
+
result[position] = instantiate attributes, context
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Retrieve field informations
|
54
|
+
#
|
55
|
+
# @param [Hash] options
|
56
|
+
# @option options [Array] fields
|
57
|
+
# @option options [Hash] context
|
58
|
+
# @return [Hash]
|
59
|
+
def fields_get options = {}
|
60
|
+
options = {
|
61
|
+
:fields => attribute_names,
|
62
|
+
:context => {}
|
63
|
+
}.merge(options)
|
64
|
+
|
65
|
+
remote.fields_get(options[:fields], options[:context]).map do |key, val|
|
66
|
+
Field.new key, val
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create a OpenObject record.
|
71
|
+
#
|
72
|
+
# @param [Hash] attributes
|
73
|
+
# @param [Hash] options
|
74
|
+
# @option options [Hash] context
|
75
|
+
# @return [Number] id
|
76
|
+
def create_record attributes, options = {}
|
77
|
+
remote.create attributes, options[:context]
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# Fetch default attribute values from OpenERP database
|
82
|
+
#
|
83
|
+
# @param [Hash] options
|
84
|
+
# @option options [Array] fields
|
85
|
+
# @option options [Hash] context
|
86
|
+
# @return [Hash] default values
|
87
|
+
def default_get options = {}
|
88
|
+
options = {
|
89
|
+
:fields => attribute_names
|
90
|
+
}.merge(options)
|
91
|
+
|
92
|
+
remote.default_get options[:fields], options[:context]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Write values to records
|
96
|
+
#
|
97
|
+
# @param [Array] ids
|
98
|
+
# @param [Hash] attributes
|
99
|
+
# @param [Hash] options
|
100
|
+
# @option options [Hash] context
|
101
|
+
# @return [boolean] true/false
|
102
|
+
def write_record ids, attributes, options = {}
|
103
|
+
remote.write ids, attributes, options[:context]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Remove records
|
107
|
+
#
|
108
|
+
# @param [Array] ids
|
109
|
+
# @param [Hash] options
|
110
|
+
# @option options [Hash] context
|
111
|
+
# @return [boolean] true/false
|
112
|
+
def unlink ids, options = {}
|
113
|
+
remote.unlink ids, options[:context]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'kangaroo/exception'
|
2
|
+
require 'active_support/core_ext/hash'
|
3
|
+
|
4
|
+
module Kangaroo
|
5
|
+
module Model
|
6
|
+
module Persistence
|
7
|
+
# @private
|
8
|
+
def self.included klass
|
9
|
+
klass.extend ClassMethods
|
10
|
+
klass.define_model_callbacks :destroy, :save, :update, :create, :find
|
11
|
+
klass.after_destroy :mark_destroyed
|
12
|
+
klass.after_create :mark_persisted
|
13
|
+
klass.after_save :reload
|
14
|
+
|
15
|
+
klass.send :attr_accessor, :context
|
16
|
+
|
17
|
+
klass.before_initialize do
|
18
|
+
@destroyed = false
|
19
|
+
@readonly = false
|
20
|
+
@new_record = !@id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Check if this record hasnt been persisted yet
|
25
|
+
#
|
26
|
+
# @return [boolean] true/false
|
27
|
+
def new_record?
|
28
|
+
@new_record
|
29
|
+
end
|
30
|
+
|
31
|
+
# Check if this record has been persisted yet
|
32
|
+
#
|
33
|
+
# @return [boolean] true/false
|
34
|
+
def persisted?
|
35
|
+
!@new_record
|
36
|
+
end
|
37
|
+
|
38
|
+
# Check if this record has been destroyed
|
39
|
+
#
|
40
|
+
# @return [boolean] true/false
|
41
|
+
def destroyed?
|
42
|
+
@destroyed
|
43
|
+
end
|
44
|
+
|
45
|
+
# Destroy this record
|
46
|
+
#
|
47
|
+
# @return self
|
48
|
+
def destroy
|
49
|
+
return true if destroyed? || new_record?
|
50
|
+
_run_destroy_callbacks do
|
51
|
+
remote.unlink [id], :context => context
|
52
|
+
end
|
53
|
+
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Save this record
|
58
|
+
#
|
59
|
+
# @param [Hash] options unused
|
60
|
+
# @return [boolean] true/false
|
61
|
+
def save options = {}
|
62
|
+
_run_save_callbacks do
|
63
|
+
create_or_update
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Save this record or raise an error
|
68
|
+
#
|
69
|
+
# @param [Hash] options unused
|
70
|
+
# @return [boolean] true
|
71
|
+
def save! options = {}
|
72
|
+
save options ||
|
73
|
+
raise(RecordSavingFailed)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Reload this record, or just a subset of fields
|
77
|
+
#
|
78
|
+
# @param [Array] fields to reload
|
79
|
+
# @return self
|
80
|
+
def reload fields = self.class.attribute_names
|
81
|
+
@attributes = remote.read([id], fields, context).first.except(:id).stringify_keys
|
82
|
+
fields.each do |field|
|
83
|
+
@changed_attributes.delete field.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
module ClassMethods
|
90
|
+
# Initialize a record and immediately save it
|
91
|
+
#
|
92
|
+
# @param [Hash] attributes
|
93
|
+
# @return saved record
|
94
|
+
def create attributes = {}
|
95
|
+
new(attributes).tap do |new_record|
|
96
|
+
new_record.save
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Retrieve a record by id
|
101
|
+
#
|
102
|
+
# @param [Number] id
|
103
|
+
# @return record
|
104
|
+
# @raise RecordNotFound
|
105
|
+
def find id
|
106
|
+
Array === id ?
|
107
|
+
find_every(ids) :
|
108
|
+
find_single(id)
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
def find_single id
|
113
|
+
read([id]).first ||
|
114
|
+
raise(RecordNotFound)
|
115
|
+
end
|
116
|
+
|
117
|
+
def find_every ids
|
118
|
+
read ids
|
119
|
+
end
|
120
|
+
|
121
|
+
def instantiate attributes, context = {}
|
122
|
+
allocate.tap do |instance|
|
123
|
+
instance.instance_exec(attributes.stringify_keys, context) do |attributes, context|
|
124
|
+
@attributes = attributes.except 'id'
|
125
|
+
@id = attributes['id']
|
126
|
+
@context = context
|
127
|
+
raise InstantiatedRecordNeedsIDError if @id.nil?
|
128
|
+
|
129
|
+
@new_record = false
|
130
|
+
@destroyed = false
|
131
|
+
@readonly = false
|
132
|
+
|
133
|
+
_run_initialize_callbacks
|
134
|
+
_run_find_callbacks
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
protected
|
141
|
+
def attributes_for_update
|
142
|
+
@attributes
|
143
|
+
end
|
144
|
+
|
145
|
+
def attributes_for_create
|
146
|
+
@attributes
|
147
|
+
end
|
148
|
+
|
149
|
+
def create_or_update
|
150
|
+
new_record? ? create : update
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
def create
|
155
|
+
_run_create_callbacks do
|
156
|
+
if id = self.class.create_record(attributes_for_create, :context => context)
|
157
|
+
@id = id
|
158
|
+
else
|
159
|
+
false
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def update
|
165
|
+
_run_update_callbacks do
|
166
|
+
self.class.write_record [id], attributes_for_update, :context => context
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def mark_persisted
|
171
|
+
@new_record = false
|
172
|
+
end
|
173
|
+
|
174
|
+
def mark_destroyed
|
175
|
+
@destroyed = true
|
176
|
+
freeze
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
module Kangaroo
|
2
|
+
module Model
|
3
|
+
class Relation
|
4
|
+
# @private
|
5
|
+
ARRAY_DELEGATES = %w( all? any? as_json at b64encode blank? choice class clone collect collect! combination compact compact! concat
|
6
|
+
cycle decode64 delete delete_at delete_if detect drop drop_while dup duplicable? each each_cons each_index
|
7
|
+
each_slice each_with_index each_with_object empty? encode64 encode_json entries enum_cons enum_for enum_slice
|
8
|
+
enum_with_index eql? equal? exclude? extract_options! fetch fifth fill find_all find_index flatten
|
9
|
+
flatten! forty_two fourth freeze frozen? grep group_by html_safe? in_groups in_groups_of include? index
|
10
|
+
index_by indexes indices inject insert inspect instance_eval instance_exec instance_of? is_a? join kind_of?
|
11
|
+
last many? map map! max max_by member? min min_by minmax minmax_by nitems none? one? pack paginate
|
12
|
+
partition permutation pop presence present? pretty_inspect pretty_print pretty_print_cycle pretty_print_inspect
|
13
|
+
pretty_print_instance_variables product push rassoc reduce reject reject! replace respond_to? returning reverse
|
14
|
+
reverse! reverse_each rindex sample second shelljoin shift shuffle shuffle! slice slice! sort sort!
|
15
|
+
sort_by split sum take take_while tap third to to_ary to_default_s to_enum to_formatted_s to_json to_matcher
|
16
|
+
to_param to_query to_s to_sentence to_set to_xml to_xml_rpc to_yaml to_yaml_properties to_yaml_style transpose
|
17
|
+
type uniq uniq! uniq_by uniq_by! unshift values_at yaml_initialize zip |).freeze
|
18
|
+
|
19
|
+
# @private
|
20
|
+
attr_accessor :target, :where_clauses, :offset_clause, :limit_clause, :select_clause, :order_clause, :context_clause
|
21
|
+
|
22
|
+
alias_method :_clone, :clone
|
23
|
+
alias_method :_tap, :tap
|
24
|
+
|
25
|
+
delegate *(ARRAY_DELEGATES + [:to => :to_a])
|
26
|
+
|
27
|
+
# @private
|
28
|
+
def initialize target
|
29
|
+
@target = target
|
30
|
+
@where_clauses = []
|
31
|
+
@select_clause = []
|
32
|
+
@order_clause = []
|
33
|
+
@context_clause = {}
|
34
|
+
end
|
35
|
+
|
36
|
+
# Submit the query
|
37
|
+
def to_a
|
38
|
+
@target.search_and_read @where_clauses, search_parameters, read_parameters
|
39
|
+
end
|
40
|
+
alias_method :all, :to_a
|
41
|
+
|
42
|
+
# Return only the first record
|
43
|
+
def first
|
44
|
+
limit(1).to_a.first
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return only the last record
|
48
|
+
def last
|
49
|
+
reverse.first
|
50
|
+
end
|
51
|
+
|
52
|
+
# Check if a record with fulfilling this conditions exist
|
53
|
+
def exists?
|
54
|
+
@target.search(@where_clauses, search_parameters.merge(:limit => 1)).present?
|
55
|
+
end
|
56
|
+
|
57
|
+
# Find record(s) by id(s)
|
58
|
+
def find ids
|
59
|
+
records = where(:id => ids)
|
60
|
+
|
61
|
+
Array === ids ?
|
62
|
+
records.all :
|
63
|
+
records.first
|
64
|
+
end
|
65
|
+
|
66
|
+
# Count how many records fulfill this conditions
|
67
|
+
def count
|
68
|
+
@target.count_by @where_clauses, search_parameters
|
69
|
+
end
|
70
|
+
alias_method :size, :count
|
71
|
+
alias_method :length, :count
|
72
|
+
|
73
|
+
# Reverse all order clauses
|
74
|
+
def reverse
|
75
|
+
if @order_clause.blank?
|
76
|
+
order('id', true)
|
77
|
+
else
|
78
|
+
_clone._tap do |c|
|
79
|
+
c.order_clause = c.order_clause.map do |order|
|
80
|
+
reverse_order order
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Clone this relation and add the condition to the where clause
|
87
|
+
#
|
88
|
+
# @param [Hash, Array, String] condition
|
89
|
+
# @return [Relation] cloned relation
|
90
|
+
def where condition
|
91
|
+
_clone._tap do |c|
|
92
|
+
c.where_clauses += [condition]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Clone this relation and (re)set the limit clause
|
97
|
+
#
|
98
|
+
# @param [Number] limit maximum records to retriebe
|
99
|
+
# @return [Relation] cloned relation
|
100
|
+
def limit limit
|
101
|
+
_clone._tap do |c|
|
102
|
+
c.limit_clause = limit
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Clone this relation and (re)set the offset clause
|
107
|
+
#
|
108
|
+
# @param [Number] offset number of records to skip
|
109
|
+
# @return [Relation] cloned relation
|
110
|
+
def offset offset
|
111
|
+
_clone._tap do |c|
|
112
|
+
c.offset_clause = offset
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Clone this relation and add select expressions to select clause
|
117
|
+
#
|
118
|
+
# @param [Array, String, Symbol] selects fields to retrieve
|
119
|
+
# @return [Relation] cloned relation
|
120
|
+
def select *selects
|
121
|
+
selects = selects.flatten.map &:to_s
|
122
|
+
_clone._tap do |c|
|
123
|
+
c.select_clause += selects
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Clone this relation and add a context
|
128
|
+
#
|
129
|
+
# @param [Hash] context
|
130
|
+
# @return [Relation] cloned relation
|
131
|
+
def context context = {}
|
132
|
+
_clone._tap do |c|
|
133
|
+
c.context_clause = c.context_clause.merge(context)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Clone this relation and add an order instruction
|
138
|
+
#
|
139
|
+
# @param [String, Symbol] column field to order by
|
140
|
+
# @param [boolean] desc true to order descending
|
141
|
+
def order column, desc = false
|
142
|
+
column = column.to_s + " desc" if desc
|
143
|
+
_clone._tap do |c|
|
144
|
+
c.order_clause += [column.to_s]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Shortcut to set limit and offset and execute queries immediately.
|
149
|
+
# If {#limit} or {#offset} are already set, [] is equal to #to_a[]
|
150
|
+
#
|
151
|
+
# @overload [](n)
|
152
|
+
# @param [Number] n
|
153
|
+
# @return [Kangaroo::Model::Base] n-th record
|
154
|
+
# @overload [](n, m)
|
155
|
+
# @param [Number] n
|
156
|
+
# @param [Number] m
|
157
|
+
# @return [Array<Kangaroo::Model::Base>] m records, offset n
|
158
|
+
# @overload [](n..m)
|
159
|
+
# @param [Range] range n..m
|
160
|
+
# @return [Array<Kangaroo::Model::Base>] records m to n
|
161
|
+
def [] start_or_range, stop = nil
|
162
|
+
if @limit_clause || @offset_clause
|
163
|
+
return to_a[start_or_range, stop] if stop
|
164
|
+
return to_a[start_or_range]
|
165
|
+
end
|
166
|
+
|
167
|
+
c = _clone
|
168
|
+
|
169
|
+
c.offset_clause = if start_or_range.is_a?(Range)
|
170
|
+
range_end = start_or_range.end
|
171
|
+
range_end += 1 unless start_or_range.exclude_end?
|
172
|
+
|
173
|
+
c.limit_clause = range_end - start_or_range.begin
|
174
|
+
start_or_range.begin
|
175
|
+
elsif stop
|
176
|
+
c.limit_clause = stop
|
177
|
+
start_or_range
|
178
|
+
else
|
179
|
+
c.limit_clause = 1
|
180
|
+
start_or_range
|
181
|
+
end
|
182
|
+
|
183
|
+
(stop.nil? && Integer===start_or_range) ? c.to_a.first : c.to_a
|
184
|
+
end
|
185
|
+
|
186
|
+
protected
|
187
|
+
def search_parameters
|
188
|
+
{
|
189
|
+
:offset => @offset_clause,
|
190
|
+
:limit => @limit_clause,
|
191
|
+
:order => @order_clause.join(", "),
|
192
|
+
:context => @context_clause
|
193
|
+
}
|
194
|
+
end
|
195
|
+
|
196
|
+
def read_parameters
|
197
|
+
{
|
198
|
+
:fields => @select_clause,
|
199
|
+
:context => @context_clause
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
def reverse_order order
|
204
|
+
if match = order.match(/(.*)\sdesc/i)
|
205
|
+
match[1]
|
206
|
+
else
|
207
|
+
order + " desc"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|