nexter 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dc5b3a8f682a5e6c6190429e13c694c0e356d1d6
4
- data.tar.gz: 16cd9f84c826b55a63dbd0c8559941ccf8ae171e
3
+ metadata.gz: 9fe3377bb4621f5dd4334001c71bfc251c1aab7a
4
+ data.tar.gz: b40d8a4a5dd7cfc932313cbceb18b538008e9bad
5
5
  SHA512:
6
- metadata.gz: 0ae768b8cb07511b9e7889bb06c465445e1130aceb57550f3ea091a75a716702fadda4e6b159d8cef19e7114c0000e1c79bf8539a898f2a62f3ca4de688f6671
7
- data.tar.gz: 66d07657f94d7f181b0d6966d3c82106dc52ef6815816d02e3615f9a89d4293bd7b202784c03d8c596866d178dbc8ad6ab429f5a26824104db1e38e157437165
6
+ metadata.gz: b19d7a48206c5276a18d6d6805c771d5d3f443047504d54b074a72ee1cfe9f77bf57743b5381f7a1738d276d72980f15f5df7af21168f7912edd13a8f33aa587
7
+ data.tar.gz: 7214177fefacbade18b798b5058a4b01979199fc4837ecfe5858af830fa3012a59e6fc184eddd0d13b0ce52e304589cd211bb07c0a9f4fbe867c2fd3d97121fc
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ spec/reports
16
16
  test/tmp
17
17
  test/version_tmp
18
18
  tmp
19
+ examples
@@ -1,11 +1,14 @@
1
1
  require "bundler/setup"
2
2
 
3
- require "active_support/core_ext"
4
- require "active_support/dependencies"
3
+ require "active_support/all"
4
+ # require "active_support/dependencies"
5
5
 
6
6
  require "nexter/version"
7
7
  require "nexter/wrap"
8
- require "nexter/derange"
8
+ require "nexter/query"
9
+ require "nexter/query/section"
10
+ require "nexter/query/direction"
11
+
9
12
  require "nexter/compass"
10
13
  require "nexter/eyecontact"
11
14
 
@@ -14,17 +14,15 @@ module Nexter
14
14
  end
15
15
 
16
16
  def bracket
17
- signature == -1 ? '<' : '>'
17
+ sign == -1 ? '<' : '>'
18
18
  end
19
19
 
20
20
  def redirection
21
- signature == -1 ? 'desc' : 'asc'
21
+ sign == -1 ? 'DESC' : 'ASC'
22
22
  end
23
23
 
24
- private
25
-
26
- def signature
27
- DIREC[direction.to_sym] * GOTO[goto]
24
+ def sign
25
+ DIREC[direction.downcase.to_sym] * GOTO[goto]
28
26
  end
29
27
  end
30
28
  end
@@ -0,0 +1,37 @@
1
+ module Nexter
2
+ class Query
3
+ attr_reader :columns, :compass, :wheres, :reorders
4
+
5
+ def initialize(columns, goto)
6
+ @columns = columns
7
+ @compass = Compass.new(goto)
8
+ @wheres = []
9
+ @reorders = []
10
+ iterate
11
+ end
12
+
13
+ def iterate
14
+ columns = @columns.dup
15
+
16
+ while column = columns.pop do
17
+ section = Section.new(columns)
18
+ direction = Direction.new(column, compass)
19
+
20
+ reorders.unshift("#{column[:col]} #{direction.compass.redirection}")
21
+
22
+ next unless direction.slice
23
+
24
+ @wheres << "#{section.sql}#{section.blank? ? '' : ' AND '}#{direction.sql}"
25
+ end
26
+
27
+ # binding.pry
28
+ @wheres.compact!
29
+ end
30
+
31
+ alias sql iterate
32
+
33
+
34
+
35
+
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ class Nexter::Query
2
+ class Direction
3
+ attr_reader :column, :compass
4
+ delegate :goto, :bracket, :redirection, to: :compass
5
+
6
+ def initialize(column, compass)
7
+ @column = column
8
+ @compass = compass
9
+ compass.direction = column[:dir]
10
+ end
11
+
12
+ def slice
13
+ if column[:val].present?
14
+ delimited = "#{column[:col]} #{bracket} '#{column[:val]}'"
15
+ delimited.concat(" OR #{column[:col]} IS NULL") if @compass.sign == 1
16
+ "(#{delimited})"
17
+ elsif @compass.sign == -1
18
+ "#{column[:col]} IS NOT NULL"
19
+ end
20
+ end
21
+ alias sql slice
22
+
23
+ end
24
+ end
25
+
@@ -0,0 +1,38 @@
1
+ class Nexter::Query
2
+
3
+ # Iterates over the columns, extracts their values
4
+ # and builds query part that says :
5
+ #
6
+ # > "col1 = value1"
7
+ # > "col2 = value2"
8
+ #
9
+ # then joins them with AND :
10
+ #
11
+ # > "col1 = value1 AND col2 = value2"
12
+ #
13
+ class Section
14
+ attr_reader :columns, :compass
15
+
16
+ # TODO : check if compass is needed (don't think so!)
17
+ def initialize(columns)
18
+ @columns = columns
19
+ @compass = compass
20
+ end
21
+
22
+ def iterate
23
+ @where ||= columns.map do |column|
24
+ if column[:val]
25
+ "#{column[:col]} = '#{column[:val]}'"
26
+ else
27
+ "#{column[:col]} IS NULL"
28
+ end
29
+ end.join(' AND ')
30
+ end
31
+ alias sql iterate
32
+
33
+ def blank?
34
+ iterate.blank?
35
+ end
36
+
37
+ end #section
38
+ end
@@ -1,3 +1,3 @@
1
1
  module Nexter
2
- VERSION = "0.0.5"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -7,17 +7,11 @@ module Nexter
7
7
  # extracted values from the relation
8
8
  attr_reader :order_values, :associations
9
9
 
10
- # list of build strings for finale SQL
11
- attr_reader :wheres, :reorders
12
-
13
-
14
10
  def initialize(relation, model)
15
11
  @relation = relation
16
12
  @model = model
17
13
  @order_values = parse_order( relation.order_values )
18
14
  @associations = relation.includes_values
19
- # @cursor_column = extract_attr( @ranges.pop )
20
- # @cursor = model.send( @cursor_column.to_sym, )
21
15
  end
22
16
 
23
17
  # TODO : let user determine which strategy to choose:
@@ -31,37 +25,31 @@ module Nexter
31
25
  end
32
26
 
33
27
  def after
34
- derange = cut(:next)
35
- r = relation.where( wheres.join(' OR ') )
36
- # r = r.order(:id) if derange.reorder
37
- r
28
+ query = Query.new(map_column_values, :next)
29
+ relation.where( query.wheres.join(' OR ') )
38
30
  end
39
31
 
40
32
  def before
41
- cut(:previous)
42
- relation.where( wheres.join(' OR ') ).reorder( reorders.join(", ") )
33
+ query = Query.new(map_column_values, :previous)
34
+ relation.where( query.wheres.join(' OR ') ).
35
+ reorder( query.reorders.join(", ") )
43
36
  end
44
37
 
45
- private
46
- # model.order(a, b,c) loop
47
- # 1. ( (a and b and c > c.val)
48
- # 2. (a and b > b.val)
49
- # 3. (a > a.val))
50
- def cut(goto = :next)
51
- order_vals = @order_values.dup
52
- @wheres = []
53
- @reorders = []
54
- derange = Nexter::Derange.new(model, goto)
55
-
56
- while order_col = order_vals.pop do
57
- derange.set_vals(order_vals, order_col)
58
-
59
- # should be derange's result
60
- wheres << derange.where
61
- reorders.unshift(derange.reorder)
38
+ def map_column_values
39
+ @column_values ||= @order_values.map do |column|
40
+ {col: column[0], val: value_of(column[0]), dir: column[1]}
62
41
  end
42
+ end
63
43
 
64
- derange
44
+ private
45
+ def value_of(cursor)
46
+ splits = cursor.split(".")
47
+ result = if splits.first == model.class.table_name || splits.size == 1
48
+ model.send(splits.last) if model.respond_to?(splits.last)
49
+ else
50
+ asso = model.class.reflections.keys.grep(/#{splits.first.singularize}/).first
51
+ asso = model.send(asso) and asso.send(splits.last)
52
+ end
65
53
  end
66
54
 
67
55
  # helper to turn mixed order attributes to a consistant
@@ -26,7 +26,6 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "rake"
27
27
  spec.add_development_dependency "rspec"
28
28
  spec.add_development_dependency "pry"
29
- spec.add_development_dependency "pry-plus"
30
29
 
31
30
 
32
31
  end
@@ -1,13 +1,18 @@
1
1
  Book = Struct.new(:genre, :name, :title) do
2
2
 
3
+
3
4
  def id; 71; end
4
5
 
5
6
  def self.table_name
6
7
  "books"
7
8
  end
8
9
 
9
- def reflections
10
- {:author => "blurb"}
10
+ def self.sanitize(args)
11
+ "'#{args}'"
12
+ end
13
+
14
+ def self.reflections
15
+ {author: "blurb"}
11
16
  end
12
17
 
13
18
  def author
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Nexter::Compass do
3
+ describe Nexter::Compass, focus: true do
4
4
 
5
5
  describe "#arrwo or #bracket" do
6
6
  context "when looking for *next*" do
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe Nexter::Query::Direction do
4
+
5
+ let(:compass) {Nexter::Compass.new(:next)}
6
+ let(:columns) {[
7
+ {col: "authors.name", val: "nabokov", dir: "asc"},
8
+ {col: "genre", val: "novel", dir: "asc"},
9
+ {col: "title", val: "ada", dir: "asc"}
10
+ ]}
11
+
12
+ describe "#slice", focus: true do
13
+
14
+ it "should be awesome" do
15
+ direction = Nexter::Query::Direction.new(columns.pop, compass)
16
+
17
+ expect(direction.slice).to eq("(title > 'ada' OR title IS NULL)")
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe Nexter::Query::Section, focus: true do
4
+
5
+ # let(:compass) {Nexter::Compass.new(:next)}
6
+ let(:columns) {[
7
+ {col: "authors.name", val: "nabokov", dir: "asc"},
8
+ {col: "genre", val: "novel", dir: "asc"},
9
+ {col: "title", val: "ada", dir: "asc"}
10
+ ]}
11
+
12
+ describe "#iterate" do
13
+
14
+ it "should iterate mother fucker" do
15
+ section = Nexter::Query::Section.new(columns)
16
+
17
+ expect(section.iterate).to eq(
18
+ "authors.name = 'nabokov' AND genre = 'novel' AND title = 'ada'")
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ describe Nexter::Query, broken: true do
4
+
5
+ let(:columns) {[
6
+ {col: "authors.name", val: "nabokov", dir: "asc"},
7
+ {col: "genre", val: "novel", dir: "asc"},
8
+ {col: "title", val: "ada", dir: "asc"}
9
+ ]}
10
+
11
+ describe "#where" do
12
+
13
+ it "builds the query from its ashes" do
14
+ query = Nexter::Query.new(columns, :next)
15
+
16
+ expect(query.wheres).to eq(
17
+ [ "authors.name = 'nabokov' AND genre = 'novel' AND title > 'ada'",
18
+ "authors.name = 'nabokov' AND genre > 'novel'",
19
+ "authors.name > 'nabokov'"])
20
+ end
21
+ end
22
+
23
+ end
@@ -1,8 +1,23 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Nexter::Wrap do
4
+ let(:relation) {Relation.new.tap{|r| r.order_values=["authors.name","genre","title"]}}
5
+ let(:book) {Book.new("novel", "nabokov", "ada")}
4
6
 
5
- context "no missing values" do
7
+ describe "#map_column_values", focus: true do
8
+
9
+ it "should create hashes of attributes out of the order values" do
10
+ nexter = Nexter::Wrap.new(relation, book)
11
+
12
+ expect(nexter.map_column_values).to eq(
13
+ [ {col: "authors.name", val: "nabokov", dir: "asc"},
14
+ {col: "genre", val: "novel", dir: "asc"},
15
+ {col: "title", val: "ada", dir: "asc"}
16
+ ])
17
+ end
18
+ end
19
+
20
+ context "no missing values", broken: true do
6
21
  let(:relation) { Relation.new.tap {|rel| rel.order_values=["authors.name", "title"]} }
7
22
  let(:book) { Book.new("novel", "nabokov", "ada") }
8
23
 
@@ -27,36 +42,64 @@ describe Nexter::Wrap do
27
42
  nexter = Nexter::Wrap.new(relation, book)
28
43
  nexter.before
29
44
 
30
- expect(nexter.reorders[0]).to eq(" authors.name desc")
45
+ expect(nexter.reorders[0]).to eq(" authors.name DESC")
31
46
  end
32
47
  end
33
48
  end
34
49
 
35
- context "nil values" do
36
- let(:relation) { Relation.new.tap {|rel| rel.order_values=["genre", "title"]} }
50
+ context "nil values", broken: true do
51
+ let(:relation) {Relation.new.tap {|r| r.order_values=["genre", "title", "id"]} }
37
52
 
38
- describe "#wheres" do
53
+ describe "#wheres with Book#genre IS NULL" do
39
54
  let(:book) { Book.new(nil, "nabokov", "Ada") }
40
55
 
41
56
  it "has the right SQL condition" do
57
+ skip
42
58
  nexter = Nexter::Wrap.new(relation, book)
43
59
  nexter.after
44
60
 
45
- expect(nexter.wheres[0]).to eq("(genre IS NULL AND title > 'Ada')")
61
+ expect(nexter.wheres).to eq("(genre IS NULL AND title > 'Ada')")
46
62
  end
47
63
  end
48
64
 
49
- describe "#wheres" do
65
+ describe "#wheres with Book#title IS NULL" do
50
66
  let(:book) { Book.new("novel", "nabokov", nil) }
51
67
 
52
- it "has the right SQL condition" do
68
+ it "has the right SQL condition for NEXT" do
53
69
  nexter = Nexter::Wrap.new(relation, book)
54
70
  nexter.after
55
71
 
56
- expect(nexter.wheres[0]).to eq("(genre = 'novel' AND title IS NULL AND books.id > #{book.id})")
72
+ expect(nexter.wheres[0]).to \
73
+ eq("(genre = 'novel' AND title IS NULL AND id > '#{book.id}')")
74
+
75
+ expect(nexter.wheres[1]).to \
76
+ eq("( genre > 'novel')")
77
+ end
78
+
79
+
80
+ it "has the right SQL condition for PREVIOUS" do
81
+ nexter = Nexter::Wrap.new(relation, book)
82
+ nexter.before
83
+
84
+ expect(nexter.wheres[0]).to \
85
+ eq("(genre = 'novel' AND title IS NULL AND id < '#{book.id}')")
86
+
87
+ expect(nexter.wheres[1]).to \
88
+ eq("( genre > 'novel')")
57
89
  end
58
90
  end
59
91
 
92
+ describe "#after with Book#title IS NULL" do
93
+ let(:book) { Book.new("novel", "nabokov", nil) }
94
+
95
+ it "has the right SQL condition" do
96
+ nexter = Nexter::Wrap.new(relation, book)
97
+ nexter.after
98
+
99
+ expect(nexter.wheres[0]).to \
100
+ eq("(genre = 'novel' AND title IS NULL AND id > '#{book.id}')")
101
+ end
102
+ end
60
103
  end #contextr
61
104
 
62
105
  end
@@ -7,13 +7,14 @@
7
7
  require "book"
8
8
  require "nexter"
9
9
  require "active_support"
10
- # require "pry-rescue/rspec"
10
+ require "pry"
11
11
 
12
12
 
13
13
  RSpec.configure do |config|
14
- config.treat_symbols_as_metadata_keys_with_true_values = true
14
+ # config.treat_symbols_as_metadata_keys_with_true_values = true
15
15
  config.run_all_when_everything_filtered = true
16
- config.filter_run :focus
16
+ config.filter_run focus: true
17
+ config.filter_run_excluding broken: true
17
18
 
18
19
  # Run specs in random order to surface order dependencies. If you find an
19
20
  # order dependency and want to debug it, you can fix the order by providing
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nexter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Sistovaris
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-30 00:00:00.000000000 Z
11
+ date: 2015-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -94,20 +94,6 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: pry-plus
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
97
  description: 'What is Nexter ? A misspelled tv show or a killer feature ? Almost :
112
98
  it wraps your model with an ordered scope and cuts out the next and previous record.
113
99
  It also works with associations & nested columns.'
@@ -128,14 +114,18 @@ files:
128
114
  - Rakefile
129
115
  - lib/nexter.rb
130
116
  - lib/nexter/compass.rb
131
- - lib/nexter/derange.rb
132
117
  - lib/nexter/eyecontact.rb
118
+ - lib/nexter/query.rb
119
+ - lib/nexter/query/direction.rb
120
+ - lib/nexter/query/section.rb
133
121
  - lib/nexter/version.rb
134
122
  - lib/nexter/wrap.rb
135
123
  - nexter.gemspec
136
124
  - spec/book.rb
137
125
  - spec/nexter/compass_spec.rb
138
- - spec/nexter/derange_spec.rb
126
+ - spec/nexter/query/direction_spec.rb
127
+ - spec/nexter/query/section_spec.rb
128
+ - spec/nexter/query_spec.rb
139
129
  - spec/nexter/wrap_spec.rb
140
130
  - spec/spec_helper.rb
141
131
  homepage: https://github.com/charly/nexter
@@ -158,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
148
  version: '0'
159
149
  requirements: []
160
150
  rubyforge_project:
161
- rubygems_version: 2.2.2
151
+ rubygems_version: 2.4.5.1
162
152
  signing_key:
163
153
  specification_version: 4
164
154
  summary: Wrap your model with an ordered scope and cut out the _next_ and _previous_
@@ -166,6 +156,8 @@ summary: Wrap your model with an ordered scope and cut out the _next_ and _previ
166
156
  test_files:
167
157
  - spec/book.rb
168
158
  - spec/nexter/compass_spec.rb
169
- - spec/nexter/derange_spec.rb
159
+ - spec/nexter/query/direction_spec.rb
160
+ - spec/nexter/query/section_spec.rb
161
+ - spec/nexter/query_spec.rb
170
162
  - spec/nexter/wrap_spec.rb
171
163
  - spec/spec_helper.rb
@@ -1,66 +0,0 @@
1
- module Nexter
2
- class Derange
3
-
4
- attr_reader :model, :compass, :table_name, :reorder
5
-
6
- attr_accessor :delimiter, :columns, :trunks
7
-
8
- delegate :goto, :bracket, :redirection, to: :compass
9
-
10
-
11
- def initialize(model, goto)
12
- @model = model
13
- @table_name = model.class.table_name
14
- @trunks = []
15
- @reorder = false
16
- @or_null = false
17
- @compass = Nexter::Compass.new(goto)
18
- end
19
-
20
- def set_vals(order_vals, order_col)
21
- @columns = order_vals
22
- @delimiter = order_col[0]
23
- compass.direction = order_col[1]
24
- end
25
-
26
- def where
27
- "(#{range} #{range.blank? ? '' : 'AND'} #{slice})"
28
- end
29
-
30
- def reorder
31
- " #{delimiter} #{redirection}"
32
- end
33
-
34
- def range
35
- trunk = columns.map do |col|
36
- if range_value = value_of(col[0])
37
- # binding.pry
38
- "#{col[0]} = #{model.class.sanitize range_value}"
39
- else
40
- "#{col[0]} IS NULL"
41
- end
42
- end.join(' AND ')
43
- end
44
-
45
- def slice
46
- if val = value_of(delimiter)
47
- d = model.class.sanitize val
48
- delimited = "#{delimiter} #{bracket} #{d}"
49
- else
50
- # @reorder = true
51
- "#{delimiter} IS NULL AND #{table_name}.id > #{model.id}"
52
- end
53
- end
54
-
55
- def value_of(cursor)
56
- splits = cursor.split(".")
57
- result = if splits.first == table_name || splits.size == 1
58
- model.send(splits.last)
59
- else
60
- # binding.pry
61
- asso = model.class.reflections.keys.grep(/#{splits.first.singularize}/).first
62
- asso = model.send(asso) and asso.send(splits.last)
63
- end
64
- end
65
- end
66
- end
@@ -1,53 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Nexter::Derange do
4
-
5
- let(:relation) { Relation.new }
6
- let(:book) { Book.new("novel", "nabokov", "Ada") }
7
- let(:nexter) { Nexter::Wrap.new(relation, book)}
8
-
9
- describe "#range" do
10
- it "returns a range of rows related to the current model (book)" do
11
- derange = Nexter::Derange.new(book, :next)
12
- derange.columns = nexter.order_values.tap(&:pop)
13
-
14
- expect(derange.range).to eq("books.genre = 'novel' AND authors.name = 'nabokov'")
15
- end
16
-
17
- it "handles nil values" do
18
- book.genre = nil
19
- derange = Nexter::Derange.new(book, :next)
20
- derange.columns = nexter.order_values.tap(&:pop)
21
-
22
- expect(derange.range).to eq("books.genre IS NULL AND authors.name = 'nabokov'")
23
- end
24
-
25
- it "handles nil values (just for peace of mind)" do
26
- book.name = nil
27
- derange = Nexter::Derange.new(book, :next)
28
- derange.columns = nexter.order_values.tap(&:pop)
29
-
30
- expect(derange.range).to eq("books.genre = 'novel' AND authors.name IS NULL")
31
- end
32
- end
33
-
34
- describe "#slice" do
35
- it "returns a range of rows related to the current model (book)" do
36
- derange = Nexter::Derange.new(book, :next)
37
- order_col = nexter.order_values.pop
38
- derange.set_vals(nexter.order_values, order_col)
39
-
40
- expect(derange.slice).to eq("books.title > 'Ada'")
41
- end
42
-
43
- it "handles nil values" do
44
- book.title = nil
45
- derange = Nexter::Derange.new(book, :next)
46
- order_col = nexter.order_values.pop
47
- derange.set_vals(nexter.order_values, order_col)
48
-
49
- expect(derange.slice).to eq("books.title IS NULL AND books.id > 71")
50
- end
51
- end
52
-
53
- end