nevermind 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 801d69d84b3ad05b15dba9d8d321c8618ab6b2ef
4
- data.tar.gz: faf22b0eb6e66da9f35c797785fa30c9d0f90b15
3
+ metadata.gz: 7fcf26ca60bf55a6350a343e9ccc5baf4cb7ac72
4
+ data.tar.gz: 18869cda8433a822bab32a0feda6c3909c024d4c
5
5
  SHA512:
6
- metadata.gz: 9cb18bf0a46718c59af0cb3d26e427b71fb1bcdd409ed3b58008f94680e96fa0d7fb22c70f391a8834ea109692ea8ccee46ca1f9d891c8f103b4988e645e4175
7
- data.tar.gz: 5ec0cbe2a1cbf2c0e1650e7ad0cf27be4d301a0946779ca3cf1fcd16d049e226eb6c74215220ebc266caef2cc8b3716c4f70c5b93ffa73490ebe158159eb9457
6
+ metadata.gz: 66bef2ea482fafca3162490ad6691c8b4c47aaf4787ce9619021bc525bbba4ed908df00b4aa900b92132d5af5d9a7d147c80e8d921a9ebd8cfcc3aa2fb136d4c
7
+ data.tar.gz: a20a4b61d8845474d248eb40fbc905413a8718cb09f4b17efd6d73be16c418d9aa8d7cdf4d968a9ea52390b801c8cd61c1866b003bfd3107d251b2c40eb6db94
@@ -1,5 +1,5 @@
1
1
  require 'nevermind/version'
2
- require 'nevermind/proto'
2
+ require 'nevermind/proxy'
3
3
 
4
4
  module Nevermind
5
5
  # Your code goes here...
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Nevermind
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -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. Next milestone: 1. Handling a pair of models with find_by. }
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
@@ -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
@@ -1,4 +1,4 @@
1
- 5.times do |i|
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.1
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-17 00:00:00.000000000 Z
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: 'In development now. Next milestone: 1. Handling a pair of models with
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/proto.rb
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/nevermind_proto_spec.rb
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/nevermind_proto_spec.rb
131
+ - spec/nevermind_proxy_spec.rb
134
132
  - spec/schema.rb
135
133
  - spec/seed.rb
136
134
  - spec/spec_helper.rb
@@ -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
@@ -1,5 +0,0 @@
1
- module Nevermind
2
- class Relation
3
-
4
- end
5
- end
@@ -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