nexter 0.0.3 → 0.0.4

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: e4900260b077482ad9229eaef70c81eb7a5d84cb
4
- data.tar.gz: 350f56554e30064f974872056b7201cd37666a89
3
+ metadata.gz: e4fadddadc452b36c498da25fdface6c1184c400
4
+ data.tar.gz: 310cfda6c5d6d65522523d37a75fa8df47e242ff
5
5
  SHA512:
6
- metadata.gz: dbdb94b58cc46a2aa2ccb20b1cb2a558de630c5f43fd4078b1bb06b508413fcce155dfa6c2af9235828d2fe8eaab20b6df90ee3f4eae96334849c14b0520254c
7
- data.tar.gz: 56263b5b0f2d217fc7c7d5b259ac76d836be5e7ea1cabf0e9f3660367942c4d482e237ee4815de3f39c020cc35f3f44556518c5aace9f20732d2e8536152c50e
6
+ metadata.gz: ec5355bf5157d6166813f1c3b04658556b042a03027538fa0ae40106bb196f9a16faa4cb8ae77dfdbd292c9f26cc3e9bafd556ff9900ee6b2e995fcda671a6d9
7
+ data.tar.gz: 2bf61ef5f7d480dedc2b2761d805903422c082a7b30cdf8f382f44bf3412a004837f360da9b5c97784db387628882e287cbef4758b56ad86379d475ac9229f51
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ .DS_Store
1
2
  *.gem
2
3
  *.rbc
3
4
  .bundle
@@ -3,7 +3,7 @@ rvm:
3
3
  - "1.9.3"
4
4
  - "2.0.0"
5
5
  #- jruby-18mode # JRuby in 1.8 mode
6
- - jruby-19mode # JRuby in 1.9 mode
6
+ #- jruby-19mode # JRuby in 1.9 mode
7
7
  #- rbx
8
8
  # uncomment this line if your project needs to run something other than `rake`:
9
9
  # script: bundle exec rspec spec
data/README.md CHANGED
@@ -3,13 +3,14 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/nexter.png)](http://badge.fury.io/rb/nexter)
4
4
  [![Build Status](https://travis-ci.org/charly/nexter.png?branch=master)](https://travis-ci.org/charly/nexter)
5
5
 
6
- What is Nexter ? A misspelled tv show or a killer feature ? Not sure but it wraps your ActiveRecord model with an ordered scope and consistently cuts out the _next_ and _previous_ records. It also works with associations & nested columns : `order("books.genre, authors.name, published_at desc")`
6
+ What is Nexter ? A misspelled tv show or a killer feature ? Not sure but it wraps your ActiveRecord model with an ordered scope and consistently cuts out the _next_ and _previous_ records. It also works with associations & nested columns : `Book.order("books.genre, authors.name, published_at desc")`
7
7
 
8
8
  ## Installation
9
9
 
10
10
  gem 'nexter'
11
+ # (edge) gem 'nexter', git: 'https://github.com/charly/nexter'
11
12
 
12
- ## Usage
13
+ ## Bare Usage
13
14
 
14
15
  ```ruby
15
16
  @books = Book.includes(:author).bestsellers.
@@ -20,9 +21,34 @@ nexter.previous
20
21
  nexter.next
21
22
  ```
22
23
 
23
- ## Use Case Full Stack
24
+ ## Rails Usage
24
25
 
25
- It helps you cycle consistentely through each record of any filtered collection instead of helplessly hit the back button of your browser to find the next item of your search. It plays well with gem which keeps the state of an `ActiveRelation like [siphon](https://github.com/charly/siphon), [ransack](https://github.com/activerecord-hackery/ransack), [has_scope](https://github.com/plataformatec/has_scope) & others.
26
+ It helps you cycle consistentely through each record of any filtered collection instead of helplessly hit the back button of your browser to find the next item of your search. It plays well with gem which keeps the state of an `ActiveRelation` like [siphon](https://github.com/charly/siphon), [ransack](https://github.com/activerecord-hackery/ransack) & others.
27
+
28
+ ### New way (bleeding edge)
29
+
30
+ With the new view helper `nexter` no need to inject previous/next in the ActiveRecord model.
31
+ However there's an assumptions : the formobject responds to `result` and returns an activerelation (like ransack does)
32
+
33
+ ```ruby
34
+ class BookController
35
+ before_filter :resource, except: :index
36
+
37
+ def resource
38
+ @book_search ||= BookSearch.new(params[:book_search])
39
+ @book ||= Book.includes([:author]).find(params[:id])
40
+ end
41
+ end
42
+ ```
43
+
44
+ ```erb
45
+ <%- nexter @book, @book_search do |b| %>
46
+ <%= link_to "previous", b.path([:edit, :admin, (b.previous || @book)]) %>
47
+ <%= link_to "collection", b.path([:admin, Book]) %>
48
+ <%= link_to "next", b.path([:edit, :admin, (b.next || @book)])
49
+ ```
50
+
51
+ ### Old way (still applies but verbose)
26
52
 
27
53
  ```ruby
28
54
  class Book
@@ -62,9 +88,12 @@ end
62
88
 
63
89
  ## TODO
64
90
 
91
+ - (feature) group/havings logic
92
+ - (test) viewhelper
65
93
  - (docs) How it works
66
94
  - (feature) Joins ?
67
95
  - (docs) previous/next through ctrl (not preloaded)
96
+ - (fix) for nil values you need a reorder with default delimiter
68
97
 
69
98
  ## Contributing
70
99
 
@@ -1,10 +1,13 @@
1
1
  require "bundler/setup"
2
2
 
3
3
  require "active_support/core_ext"
4
+ require "active_support/dependencies"
4
5
 
5
6
  require "nexter/version"
6
7
  require "nexter/wrap"
7
8
  require "nexter/derange"
9
+ require "nexter/compass"
10
+ require "nexter/eyecontact"
8
11
 
9
12
 
10
13
  module Nexter
@@ -0,0 +1,31 @@
1
+ module Nexter
2
+ #
3
+ #
4
+ #
5
+ class Compass
6
+
7
+ attr_accessor :goto, :direction
8
+
9
+ DIREC = {asc: 1, desc: -1}
10
+ GOTO = {next: 1, previous: -1}
11
+
12
+ def initialize(goto)
13
+ @goto = goto
14
+ end
15
+
16
+ def bracket
17
+ signature == -1 ? '<' : '>'
18
+ end
19
+
20
+ def redirection
21
+ signature == -1 ? 'desc' : 'asc'
22
+ end
23
+
24
+ private
25
+
26
+ def signature
27
+ DIREC[direction.to_sym] * GOTO[goto]
28
+ end
29
+ end
30
+ end
31
+
@@ -1,21 +1,37 @@
1
1
  module Nexter
2
2
  class Derange
3
3
 
4
- attr_reader :model, :table_name
4
+ attr_reader :model, :compass, :table_name, :reorder
5
5
 
6
- attr_accessor :delimiter, :columns
6
+ attr_accessor :delimiter, :columns, :trunks
7
7
 
8
- attr_accessor :sign, :direction, :trunks
8
+ delegate :goto, :bracket, :redirection, to: :compass
9
9
 
10
- def initialize(model)
10
+
11
+ def initialize(model, goto)
11
12
  @model = model
12
13
  @table_name = model.class.table_name
13
14
  @trunks = []
15
+ @reorder = false
16
+ @or_null = false
17
+ @compass = Nexter::Compass.new(goto)
14
18
  end
15
19
 
16
- def trunk
17
- raise unless columns
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
18
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
19
35
  trunk = columns.map do |col|
20
36
  if range_value = value_of(col[0])
21
37
  "#{col[0]} = '#{range_value}'"
@@ -23,29 +39,25 @@ module Nexter
23
39
  "#{col[0]} IS NULL"
24
40
  end
25
41
  end.join(' AND ')
26
-
27
- trunks << trunk
28
- "#{trunk} AND" unless trunk.blank?
29
42
  end
30
43
 
31
- def bigger_than
32
- if delimiter_value = value_of(delimiter)
33
- "#{delimiter} #{get_bracket(sign)} '#{delimiter_value}'"
44
+ def slice
45
+ if val = value_of(delimiter)
46
+ d = val.is_a?(String) ? val.gsub(/'/, "''") : val
47
+ delimited = "#{delimiter} #{bracket} '#{d}'"
34
48
  else
49
+ # @reorder = true
35
50
  "#{delimiter} IS NULL AND #{table_name}.id > #{model.id}"
36
51
  end
37
52
  end
38
53
 
39
- def get_bracket(sign)
40
- sign == -1 ? '<' : '>'
41
- end
42
-
43
54
  def value_of(cursor)
44
55
  splits = cursor.split(".")
45
- if splits.first == table_name || splits.size == 1
56
+ result = if splits.first == table_name || splits.size == 1
46
57
  model.send(splits.last)
47
58
  else
48
- asso = model.reflections.keys.grep(/#{splits.first.singularize}/).first
59
+ # binding.pry
60
+ asso = model.class.reflections.keys.grep(/#{splits.first.singularize}/).first
49
61
  asso = model.send(asso) and asso.send(splits.last)
50
62
  end
51
63
  end
@@ -0,0 +1,55 @@
1
+ module Nexter
2
+ # ViewHelper/ViewBuilder for previous/next out of the van
3
+ #
4
+ # - nexter(@asset, @asset_search) do |asset|
5
+ # = link_to "previous", asset.path([:edit, :admin, asset.previous ])
6
+ # = link_to "next", asset.path([:edit, :admin, asset.next ])
7
+ #
8
+ module Eyecontact
9
+
10
+ def nexter( model, search_form )
11
+ yield Nexter::Retina.new(model, search_form, self)
12
+ return false
13
+ end
14
+
15
+ ::ActionView::Base.send(:include, self) if defined?(Rails)
16
+ end
17
+
18
+ class Retina
19
+ attr_reader :model, :search_form, :view
20
+
21
+ attr_reader :relation, :params
22
+
23
+ attr_reader :nexter
24
+
25
+ delegate :previous, :next, :after, :before, :to => :nexter
26
+
27
+ def initialize( model, search_form, view)
28
+ @model = model
29
+ @search_form = search_form
30
+ @view = view
31
+
32
+ set_relation
33
+ set_params
34
+
35
+ @nexter = Nexter.wrap( relation, model )
36
+ end
37
+
38
+ def path(args)
39
+ @view.polymorphic_path(args, params)
40
+ end
41
+
42
+
43
+ private
44
+
45
+ def set_relation
46
+ @relation = @search_form.result
47
+ end
48
+
49
+ def set_params
50
+ param_key = @search_form.class.model_name.param_key
51
+ @params = view.params.select {|k, v| k == param_key}
52
+ end
53
+ end
54
+ end
55
+
@@ -1,3 +1,3 @@
1
1
  module Nexter
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -1,9 +1,6 @@
1
1
  module Nexter
2
2
  class Wrap
3
3
 
4
- # the gui concatenating strings
5
- attr_reader :derange
6
-
7
4
  # the current model & the scope
8
5
  attr_reader :model, :relation
9
6
 
@@ -13,19 +10,18 @@ module Nexter
13
10
  # list of build strings for finale SQL
14
11
  attr_reader :wheres, :reorders
15
12
 
16
- DIREC = {asc: 1, desc: -1}
17
- GOTO = {next: 1, previous: -1}
18
13
 
19
14
  def initialize(relation, model)
20
15
  @relation = relation
21
16
  @model = model
22
- @order_values = arrayize( relation.order_values )
17
+ @order_values = parse_order( relation.order_values )
23
18
  @associations = relation.includes_values
24
19
  # @cursor_column = extract_attr( @ranges.pop )
25
20
  # @cursor = model.send( @cursor_column.to_sym, )
26
- @derange = Nexter::Derange.new(model)
27
21
  end
28
22
 
23
+ # TODO : let user determine which strategy to choose:
24
+ # e.g: carousel or stay there
29
25
  def next
30
26
  after.first
31
27
  end
@@ -34,10 +30,11 @@ module Nexter
34
30
  before.first
35
31
  end
36
32
 
37
-
38
33
  def after
39
- cut(:next)
40
- relation.where( wheres.join(' OR ') )
34
+ derange = cut(:next)
35
+ r = relation.where( wheres.join(' OR ') )
36
+ # r = r.order(:id) if derange.reorder
37
+ r
41
38
  end
42
39
 
43
40
  def before
@@ -54,29 +51,21 @@ module Nexter
54
51
  order_vals = @order_values.dup
55
52
  @wheres = []
56
53
  @reorders = []
54
+ derange = Nexter::Derange.new(model, goto)
57
55
 
58
56
  while order_col = order_vals.pop do
57
+ derange.set_vals(order_vals, order_col)
59
58
 
60
- derange.columns = order_vals
61
- derange.delimiter = order_col[0]
62
- derange.direction = order_col[1]
63
- derange.sign = signature(derange.direction, goto)
64
-
65
- wheres << "( #{derange.trunk} #{derange.bigger_than} )"
66
- reorders.unshift(" #{order_col[0]} #{get_direction(derange.sign)}")
59
+ # should be derange's result
60
+ wheres << derange.where
61
+ reorders.unshift(derange.reorder)
67
62
  end
68
- end
69
-
70
- def signature(dir, goto)
71
- sign = DIREC[dir.to_sym] * GOTO[goto]
72
- end
73
63
 
74
- def get_direction(sign)
75
- sign == -1 ? 'desc' : 'asc'
64
+ derange
76
65
  end
77
66
 
78
67
  # helper to turn mixed order attributes to a consistant
79
- def arrayize(array)
68
+ def parse_order(array)
80
69
  array.join(",").split(",").map(&:strip).map do |column|
81
70
  splits = column.split(" ").map(&:strip).map(&:downcase)
82
71
  splits << "asc" if splits.size == 1
@@ -1,5 +1,3 @@
1
- Author = Struct.new(:name)
2
-
3
1
  Book = Struct.new(:genre, :name, :title) do
4
2
 
5
3
  def id; 71; end
@@ -13,7 +11,8 @@ Book = Struct.new(:genre, :name, :title) do
13
11
  end
14
12
 
15
13
  def author
16
- Author.new("nabokov")
14
+ a = Struct.new(:name)
15
+ a.new(name)
17
16
  end
18
17
  end
19
18
 
@@ -36,4 +35,8 @@ class Relation
36
35
  self
37
36
  end
38
37
 
38
+ def order(args)
39
+ self
40
+ end
41
+
39
42
  end
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+
3
+ describe Nexter::Compass do
4
+
5
+ describe "#arrwo or #bracket" do
6
+ context "when looking for *next*" do
7
+ let(:compass) {Nexter::Compass.new(:next)}
8
+
9
+ it "returns > when direction's *asc*" do
10
+ compass.direction= "asc"
11
+ expect(compass.bracket).to eq(">")
12
+ end
13
+
14
+ it "returns < when direction's *desc*" do
15
+ compass.direction= "desc"
16
+ expect(compass.bracket).to eq("<")
17
+ end
18
+ end
19
+
20
+ context "when looking for *previous*" do
21
+ let(:compass) {Nexter::Compass.new(:previous)}
22
+
23
+ it "returns < when direction's *asc*" do
24
+ compass.direction= "asc"
25
+ expect(compass.bracket).to eq("<")
26
+ end
27
+
28
+ it "returns > when direction's *desc*" do
29
+ compass.direction= "desc"
30
+ expect(compass.bracket).to eq(">")
31
+ end
32
+ end
33
+ end # desc
34
+ end
@@ -0,0 +1,53 @@
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
@@ -11,8 +11,7 @@ describe Nexter::Wrap do
11
11
  nexter = Nexter::Wrap.new(relation, book)
12
12
  nexter.after
13
13
 
14
- expect(nexter.wheres[0]).to eq("( authors.name = 'nabokov' AND title > 'ada' )")
15
- expect(nexter.wheres[1]).to eq("( authors.name > 'nabokov' )")
14
+ expect(nexter.wheres[0]).to eq("(authors.name = 'nabokov' AND title > 'ada')")
16
15
  end
17
16
  end
18
17
 
@@ -21,7 +20,7 @@ describe Nexter::Wrap do
21
20
  nexter = Nexter::Wrap.new(relation, book)
22
21
  nexter.before
23
22
 
24
- expect(nexter.wheres[0]).to eq("( authors.name = 'nabokov' AND title < 'ada' )")
23
+ expect(nexter.wheres[0]).to eq("(authors.name = 'nabokov' AND title < 'ada')")
25
24
  end
26
25
 
27
26
  it "has the right SQL order by" do
@@ -43,7 +42,7 @@ describe Nexter::Wrap do
43
42
  nexter = Nexter::Wrap.new(relation, book)
44
43
  nexter.after
45
44
 
46
- expect(nexter.wheres[0]).to eq("( genre IS NULL AND title > 'Ada' )")
45
+ expect(nexter.wheres[0]).to eq("(genre IS NULL AND title > 'Ada')")
47
46
  end
48
47
  end
49
48
 
@@ -54,7 +53,7 @@ describe Nexter::Wrap do
54
53
  nexter = Nexter::Wrap.new(relation, book)
55
54
  nexter.after
56
55
 
57
- expect(nexter.wheres[0]).to eq("( genre = 'novel' AND title IS NULL AND books.id > #{book.id} )")
56
+ expect(nexter.wheres[0]).to eq("(genre = 'novel' AND title IS NULL AND books.id > #{book.id})")
58
57
  end
59
58
  end
60
59
 
@@ -7,7 +7,7 @@
7
7
  require "book"
8
8
  require "nexter"
9
9
  require "active_support"
10
- require "pry-rescue/rspec"
10
+ # require "pry-rescue/rspec"
11
11
 
12
12
 
13
13
  RSpec.configure do |config|
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.3
4
+ version: 0.0.4
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-01-09 00:00:00.000000000 Z
11
+ date: 2014-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -127,11 +127,15 @@ files:
127
127
  - README.md
128
128
  - Rakefile
129
129
  - lib/nexter.rb
130
+ - lib/nexter/compass.rb
130
131
  - lib/nexter/derange.rb
132
+ - lib/nexter/eyecontact.rb
131
133
  - lib/nexter/version.rb
132
134
  - lib/nexter/wrap.rb
133
135
  - nexter.gemspec
134
136
  - spec/book.rb
137
+ - spec/nexter/compass_spec.rb
138
+ - spec/nexter/derange_spec.rb
135
139
  - spec/nexter/wrap_spec.rb
136
140
  - spec/spec_helper.rb
137
141
  homepage: https://github.com/charly/nexter
@@ -161,6 +165,8 @@ summary: Wrap your model with an ordered scope and cut out the _next_ and _previ
161
165
  record.
162
166
  test_files:
163
167
  - spec/book.rb
168
+ - spec/nexter/compass_spec.rb
169
+ - spec/nexter/derange_spec.rb
164
170
  - spec/nexter/wrap_spec.rb
165
171
  - spec/spec_helper.rb
166
172
  has_rdoc: