nevermind 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/nevermind.rb +1 -1
- data/lib/nevermind/proxy.rb +154 -0
- data/lib/nevermind/version.rb +1 -1
- data/nevermind.gemspec +1 -1
- data/spec/nevermind_proxy_spec.rb +150 -0
- data/spec/schema.rb +2 -0
- data/spec/seed.rb +3 -3
- metadata +6 -8
- data/lib/nevermind/proto.rb +0 -41
- data/lib/nevermind/relation.rb +0 -5
- data/spec/nevermind_proto_spec.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fcf26ca60bf55a6350a343e9ccc5baf4cb7ac72
|
4
|
+
data.tar.gz: 18869cda8433a822bab32a0feda6c3909c024d4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66bef2ea482fafca3162490ad6691c8b4c47aaf4787ce9619021bc525bbba4ed908df00b4aa900b92132d5af5d9a7d147c80e8d921a9ebd8cfcc3aa2fb136d4c
|
7
|
+
data.tar.gz: a20a4b61d8845474d248eb40fbc905413a8718cb09f4b17efd6d73be16c418d9aa8d7cdf4d968a9ea52390b801c8cd61c1866b003bfd3107d251b2c40eb6db94
|
data/lib/nevermind.rb
CHANGED
@@ -0,0 +1,154 @@
|
|
1
|
+
module Nevermind
|
2
|
+
class Proxy
|
3
|
+
# include Enumerable
|
4
|
+
|
5
|
+
def initialize(first, second)
|
6
|
+
@first, @second = first, second
|
7
|
+
|
8
|
+
@order_params = nil
|
9
|
+
@limit_params = nil
|
10
|
+
@offset_params = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def each(&block)
|
14
|
+
if @order_params || @limit_params || @offset_params
|
15
|
+
get_scoped &block
|
16
|
+
else
|
17
|
+
@first.each { |obj| yield obj }
|
18
|
+
@second.each { |obj| yield obj }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](index)
|
23
|
+
#todo: optimize!
|
24
|
+
count = 0
|
25
|
+
self.each do |obj|
|
26
|
+
return obj if count == index
|
27
|
+
count+= 1
|
28
|
+
end
|
29
|
+
return nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def count
|
33
|
+
if @order_params || @limit_params || @offset_params
|
34
|
+
count = 0
|
35
|
+
get_scoped { |val| count+= 1 }
|
36
|
+
return count
|
37
|
+
else
|
38
|
+
@first.count + @second.count
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def order(*args)
|
43
|
+
@order_params = *args
|
44
|
+
return self.clone
|
45
|
+
end
|
46
|
+
|
47
|
+
def limit(*args)
|
48
|
+
@limit_params = *args
|
49
|
+
return self.clone
|
50
|
+
end
|
51
|
+
|
52
|
+
def offset(*args)
|
53
|
+
@offset_params = *args
|
54
|
+
return self.clone
|
55
|
+
end
|
56
|
+
|
57
|
+
def method_missing(method, *args, &block)
|
58
|
+
if [:all, :where, :order].include? method
|
59
|
+
call_relation_method(method, *args, &block)
|
60
|
+
else
|
61
|
+
if '!' == method.to_s.last
|
62
|
+
call_forced_method(method, *args, &block)
|
63
|
+
else
|
64
|
+
call_regular_method(method, *args, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
# find_by, first, etc
|
71
|
+
def call_regular_method(method, *args, &block)
|
72
|
+
found = @first.send method, *args, &block
|
73
|
+
if found.nil?
|
74
|
+
found = @second.send method, *args, &block
|
75
|
+
end
|
76
|
+
return found
|
77
|
+
end
|
78
|
+
|
79
|
+
# find_by!, first!, etc
|
80
|
+
def call_forced_method(method, *args, &block)
|
81
|
+
begin
|
82
|
+
found = @first.send method, *args, &block
|
83
|
+
rescue
|
84
|
+
found = @second.send method, *args, &block
|
85
|
+
ensure
|
86
|
+
return found
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def call_relation_method(method, *args, &block)
|
91
|
+
Proxy.new @first.send(method, *args, &block),
|
92
|
+
@second.send(method, *args, &block)
|
93
|
+
end
|
94
|
+
|
95
|
+
# yields scoped result
|
96
|
+
def get_scoped(&block)
|
97
|
+
# can be used only with ActiveRecord
|
98
|
+
#todo: implement Mongoid
|
99
|
+
unless defined?(ActiveRecord::Relation) && @first.is_a?(ActiveRecord::Relation) && @second.is_a?(ActiveRecord::Relation)
|
100
|
+
raise "#{ self.class }.#{ __method__ } not implemented for #{ @first.class } && #{ @second.class } yet. Only ActiveRecord::Relation i supported"
|
101
|
+
end
|
102
|
+
|
103
|
+
get_scoped_active_record &block
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_scoped_active_record(&block)
|
107
|
+
# get records ids in the desired scope
|
108
|
+
# prepare sql
|
109
|
+
#todo: protect from sql injection
|
110
|
+
tables = {@first.table_name => @first,
|
111
|
+
@second.table_name => @second}
|
112
|
+
|
113
|
+
order_by = @order_params.map do |param|
|
114
|
+
if param.is_a? Hash
|
115
|
+
"#{ param.first[0] } #{ param.first[1].upcase }"
|
116
|
+
elsif param.is_a? String
|
117
|
+
param
|
118
|
+
elsif param.is_a? Symbol
|
119
|
+
param.to_s
|
120
|
+
end
|
121
|
+
end.join(', ')
|
122
|
+
|
123
|
+
columns = (['id'] +
|
124
|
+
@order_params.map do |param|
|
125
|
+
if param.is_a? Hash
|
126
|
+
param.first[0].to_s
|
127
|
+
elsif param.is_a? String
|
128
|
+
param
|
129
|
+
elsif param.is_a? Symbol
|
130
|
+
param.to_s
|
131
|
+
end
|
132
|
+
end
|
133
|
+
).join(', ')
|
134
|
+
|
135
|
+
sql = "SELECT #{ columns }, '#{ tables.keys[0] }' as table_name FROM #{ tables.keys[0] } as `a`" +
|
136
|
+
' UNION' +
|
137
|
+
" SELECT #{ columns }, '#{ tables.keys[1] }' as table_name FROM #{ tables.keys[1] } as `b`" +
|
138
|
+
(order_by == '' ? '' : " ORDER BY #{ order_by }") +
|
139
|
+
if @limit_params.nil?
|
140
|
+
''
|
141
|
+
else
|
142
|
+
" LIMIT #{ @offset_params.nil? ? '' : "#{ @offset_params[0].to_s }, " }#{ @limit_params[0].to_s }"
|
143
|
+
end
|
144
|
+
|
145
|
+
# execute prepared sql
|
146
|
+
records_array = ActiveRecord::Base.connection.execute(sql)
|
147
|
+
|
148
|
+
# get records in the desired scope
|
149
|
+
records_array.each do |r|
|
150
|
+
yield tables[r['table_name']].find(r['id'])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/nevermind/version.rb
CHANGED
data/nevermind.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Roman Exempliarov"]
|
10
10
|
spec.email = ["urvala@gmail.com"]
|
11
11
|
spec.summary = %q{Abstraction layer that allows to work with multiple models just like with one.}
|
12
|
-
spec.description = %q{In development now.
|
12
|
+
spec.description = %q{In development now.}
|
13
13
|
spec.homepage = ""
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe Nevermind::Proxy do
|
4
|
+
before :all do
|
5
|
+
@posts = Nevermind::Proxy.new(Article.all, Video.all)
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'handles .find_by & .find_by!' do
|
9
|
+
[
|
10
|
+
[Article, @posts.find_by!(content: 'article')],
|
11
|
+
[Video, @posts.find_by!(content: 'video')],
|
12
|
+
[Article, @posts.find_by(content: 'article')],
|
13
|
+
[Video, @posts.find_by(content: 'video')]
|
14
|
+
].each do |klass, post|
|
15
|
+
expect(post.class).to eq klass
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'handles .first' do
|
20
|
+
post = @posts.first
|
21
|
+
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'handles .first!' do
|
25
|
+
post = @posts.first!
|
26
|
+
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'handles .where' do
|
30
|
+
[
|
31
|
+
[ Article, @posts.where(content: 'article') ],
|
32
|
+
[ Video, @posts.where(content: 'video') ]
|
33
|
+
].each do |klass, posts|
|
34
|
+
expect(posts.class).to eq Nevermind::Proxy
|
35
|
+
post = posts.first
|
36
|
+
expect(post.class).to eq klass
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'handles .all' do
|
41
|
+
posts = @posts.all
|
42
|
+
expect(posts.class).to eq Nevermind::Proxy
|
43
|
+
post = posts.first
|
44
|
+
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'handles .each' do
|
48
|
+
vid = 0
|
49
|
+
count = 0
|
50
|
+
vid_expectation = Article.all.map(&:vid).reduce(&:+) + Video.all.map(&:vid).reduce(&:+)
|
51
|
+
count_expectation = Article.count + Video.count
|
52
|
+
@posts.each { |post| vid+= post.vid; count+= 1 }
|
53
|
+
expect(count).to eq (count_expectation)
|
54
|
+
expect(vid).to eq (vid_expectation)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'handles .count' do
|
58
|
+
count = Article.all.count + Video.all.count
|
59
|
+
expect(@posts.count).to eq count
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'handles .order with single param' do
|
63
|
+
posts = @posts.order :rand
|
64
|
+
previous_post = false
|
65
|
+
expect( posts.count ).to eq Article.count + Video.count
|
66
|
+
posts.each do |post|
|
67
|
+
unless false == previous_post
|
68
|
+
expect(post.rand).to be >= previous_post.rand
|
69
|
+
end
|
70
|
+
previous_post = post
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'handles .order with single desc param' do
|
75
|
+
posts = @posts.order :rand => :desc
|
76
|
+
previous_post = false
|
77
|
+
expect(posts.count).to eq Article.count + Video.count
|
78
|
+
posts.each do |post|
|
79
|
+
unless false == previous_post
|
80
|
+
expect(post.rand).to be <= previous_post.rand
|
81
|
+
end
|
82
|
+
previous_post = post
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'handles .order with double param' do
|
87
|
+
posts = @posts.order :id, :rand => :desc
|
88
|
+
previous_post = false
|
89
|
+
expect(posts.count).to eq Article.count + Video.count
|
90
|
+
posts.each do |post|
|
91
|
+
unless false == previous_post
|
92
|
+
expect(post.id).to be >= previous_post.id
|
93
|
+
if post.id == previous_post.id
|
94
|
+
expect(post.rand).to be <= previous_post.rand
|
95
|
+
end
|
96
|
+
end
|
97
|
+
previous_post = post
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'handles .limit' do
|
102
|
+
posts = @posts.limit 5
|
103
|
+
expect(posts.count).to eq 5
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'handles []' do
|
107
|
+
posts = @posts.order(:id)
|
108
|
+
expect(posts[0].id).to eq 1
|
109
|
+
expect(posts[1].id).to eq 1
|
110
|
+
expect(posts[2].id).to eq 2
|
111
|
+
expect(posts[3].id).to eq 2
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'handles .offset' do
|
115
|
+
last_post = @posts.limit(5)[4]
|
116
|
+
posts = @posts.limit(5).offset(0)
|
117
|
+
expect(posts.count).to eq 5
|
118
|
+
expect(posts[4]).to eq last_post
|
119
|
+
|
120
|
+
posts = @posts.order(:id).limit(4).offset(2)
|
121
|
+
expect(posts[0].id).to eq 2
|
122
|
+
expect(posts[1].id).to eq 2
|
123
|
+
expect(posts[2].id).to eq 3
|
124
|
+
expect(posts[3].id).to eq 3
|
125
|
+
expect(posts[4]).to eq nil
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'handles .order with .limit & .offset' do
|
130
|
+
posts_offset_0 = @posts.order(:rand).limit(5)
|
131
|
+
posts_offset_2 = @posts.order(:rand).limit(5).offset(2)
|
132
|
+
|
133
|
+
expect(posts_offset_0.count).to eq 5
|
134
|
+
expect(posts_offset_2.count).to eq 5
|
135
|
+
|
136
|
+
[posts_offset_0, posts_offset_2].each do |posts|
|
137
|
+
previous_post = false
|
138
|
+
posts.each do |post|
|
139
|
+
unless false == previous_post
|
140
|
+
expect(post.rand).to be >= previous_post.rand
|
141
|
+
end
|
142
|
+
previous_post = post
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
[0,1,2].each do |i|
|
147
|
+
expect(posts_offset_2[i]).to be == posts_offset_0[i+2]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/spec/schema.rb
CHANGED
@@ -3,6 +3,7 @@ ActiveRecord::Schema.define do
|
|
3
3
|
|
4
4
|
create_table :articles, :force => true do |t|
|
5
5
|
t.integer :vid
|
6
|
+
t.integer :rand
|
6
7
|
t.string :content
|
7
8
|
|
8
9
|
t.timestamps
|
@@ -10,6 +11,7 @@ ActiveRecord::Schema.define do
|
|
10
11
|
|
11
12
|
create_table :videos, :force => true do |t|
|
12
13
|
t.integer :vid
|
14
|
+
t.integer :rand
|
13
15
|
t.string :content
|
14
16
|
|
15
17
|
t.timestamps
|
data/spec/seed.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
2
|
-
Article.create vid: i*2, content: 'article'
|
3
|
-
Video.create vid: i*2+1, content: 'video'
|
1
|
+
20.times do |i|
|
2
|
+
Article.create vid: i*2, content: 'article', rand: rand(1000)
|
3
|
+
Video.create vid: i*2+1, content: 'video', rand: rand(1000)
|
4
4
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nevermind
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Exempliarov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,8 +80,7 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description:
|
84
|
-
find_by. '
|
83
|
+
description: In development now.
|
85
84
|
email:
|
86
85
|
- urvala@gmail.com
|
87
86
|
executables: []
|
@@ -94,12 +93,11 @@ files:
|
|
94
93
|
- README.md
|
95
94
|
- Rakefile
|
96
95
|
- lib/nevermind.rb
|
97
|
-
- lib/nevermind/
|
98
|
-
- lib/nevermind/relation.rb
|
96
|
+
- lib/nevermind/proxy.rb
|
99
97
|
- lib/nevermind/version.rb
|
100
98
|
- nevermind.gemspec
|
101
99
|
- spec/models.rb
|
102
|
-
- spec/
|
100
|
+
- spec/nevermind_proxy_spec.rb
|
103
101
|
- spec/schema.rb
|
104
102
|
- spec/seed.rb
|
105
103
|
- spec/spec_helper.rb
|
@@ -130,7 +128,7 @@ summary: Abstraction layer that allows to work with multiple models just like wi
|
|
130
128
|
one.
|
131
129
|
test_files:
|
132
130
|
- spec/models.rb
|
133
|
-
- spec/
|
131
|
+
- spec/nevermind_proxy_spec.rb
|
134
132
|
- spec/schema.rb
|
135
133
|
- spec/seed.rb
|
136
134
|
- spec/spec_helper.rb
|
data/lib/nevermind/proto.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
module Nevermind
|
2
|
-
class Proto
|
3
|
-
def initialize(first, second)
|
4
|
-
@first, @second = first, second
|
5
|
-
end
|
6
|
-
|
7
|
-
def where(*args)
|
8
|
-
@first.where(*args)
|
9
|
-
@second.where(*args)
|
10
|
-
end
|
11
|
-
|
12
|
-
def method_missing(method, *args, &block)
|
13
|
-
if '!' == method.to_s.last
|
14
|
-
forced_method_missing(method, *args, &block)
|
15
|
-
else
|
16
|
-
regular_method_missing(method, *args, &block)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
# find_by, first, etc
|
22
|
-
def regular_method_missing(method, *args, &block)
|
23
|
-
found = @first.send method, *args, &block
|
24
|
-
if found.nil?
|
25
|
-
found = @second.send method, *args, &block
|
26
|
-
end
|
27
|
-
return found
|
28
|
-
end
|
29
|
-
|
30
|
-
# find_by!, first!, etc
|
31
|
-
def forced_method_missing(method, *args, &block)
|
32
|
-
begin
|
33
|
-
found = @first.send method, *args, &block
|
34
|
-
rescue
|
35
|
-
found = @second.send method, *args, &block
|
36
|
-
ensure
|
37
|
-
return found
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
data/lib/nevermind/relation.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
-
|
3
|
-
describe Nevermind::Proto do
|
4
|
-
it 'passes find_by' do
|
5
|
-
posts = Nevermind::Proto.new(Article, Video)
|
6
|
-
post = posts.find_by(content: 'article')
|
7
|
-
expect(post.class).to eq Article
|
8
|
-
post = posts.find_by(content: 'video')
|
9
|
-
expect(post.class).to eq Video
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'passes find_by!' do
|
13
|
-
posts = Nevermind::Proto.new(Article, Video)
|
14
|
-
post = posts.find_by!(content: 'article')
|
15
|
-
expect(post.class).to eq Article
|
16
|
-
post = posts.find_by!(content: 'video')
|
17
|
-
expect(post.class).to eq Video
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'passes first' do
|
21
|
-
posts = Nevermind::Proto.new(Article, Video)
|
22
|
-
post = posts.first
|
23
|
-
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'passes first!' do
|
27
|
-
posts = Nevermind::Proto.new(Article, Video)
|
28
|
-
post = posts.first
|
29
|
-
expect(post.class).to satisfy { |klass| [Article, Video].include? klass }
|
30
|
-
end
|
31
|
-
end
|