backstack 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/.document +5 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +20 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +38 -0
  6. data/Rakefile +54 -0
  7. data/VERSION +1 -0
  8. data/art/bowling_pins.graffle +0 -0
  9. data/backstack.gemspec +127 -0
  10. data/lib/backstack.rb +161 -0
  11. data/lib/backstacklib.rb +115 -0
  12. data/test/neutral/helper.rb +27 -0
  13. data/test/neutral/test_backstacklib.rb +181 -0
  14. data/test/rails_root/.gitignore +4 -0
  15. data/test/rails_root/Gemfile +33 -0
  16. data/test/rails_root/Gemfile.lock +77 -0
  17. data/test/rails_root/README +256 -0
  18. data/test/rails_root/Rakefile +12 -0
  19. data/test/rails_root/app/controllers/application_controller.rb +7 -0
  20. data/test/rails_root/app/controllers/c1_controller.rb +8 -0
  21. data/test/rails_root/app/controllers/c2_controller.rb +11 -0
  22. data/test/rails_root/app/controllers/c3_controller.rb +17 -0
  23. data/test/rails_root/app/controllers/c4_controller.rb +22 -0
  24. data/test/rails_root/app/helpers/application_helper.rb +2 -0
  25. data/test/rails_root/app/helpers/c1_helper.rb +2 -0
  26. data/test/rails_root/app/helpers/c2_helper.rb +2 -0
  27. data/test/rails_root/app/helpers/c3_helper.rb +2 -0
  28. data/test/rails_root/app/helpers/c4_helper.rb +2 -0
  29. data/test/rails_root/app/views/c1/a.html.erb +1 -0
  30. data/test/rails_root/app/views/c2/b.html.erb +1 -0
  31. data/test/rails_root/app/views/c2/c.html.erb +1 -0
  32. data/test/rails_root/app/views/c3/d.html.erb +1 -0
  33. data/test/rails_root/app/views/c3/e.html.erb +1 -0
  34. data/test/rails_root/app/views/c3/f.html.erb +1 -0
  35. data/test/rails_root/app/views/c4/g.html.erb +1 -0
  36. data/test/rails_root/app/views/c4/h.html.erb +1 -0
  37. data/test/rails_root/app/views/c4/i.html.erb +1 -0
  38. data/test/rails_root/app/views/c4/j.html.erb +1 -0
  39. data/test/rails_root/app/views/layouts/_pins.html.erb +64 -0
  40. data/test/rails_root/app/views/layouts/application.html.erb +15 -0
  41. data/test/rails_root/config/application.rb +47 -0
  42. data/test/rails_root/config/boot.rb +6 -0
  43. data/test/rails_root/config/environment.rb +5 -0
  44. data/test/rails_root/config/environments/development.rb +26 -0
  45. data/test/rails_root/config/environments/production.rb +49 -0
  46. data/test/rails_root/config/environments/test.rb +35 -0
  47. data/test/rails_root/config/initializers/backtrace_silencers.rb +7 -0
  48. data/test/rails_root/config/initializers/inflections.rb +10 -0
  49. data/test/rails_root/config/initializers/mime_types.rb +5 -0
  50. data/test/rails_root/config/initializers/secret_token.rb +7 -0
  51. data/test/rails_root/config/initializers/session_store.rb +8 -0
  52. data/test/rails_root/config/locales/en.yml +5 -0
  53. data/test/rails_root/config/routes.rb +15 -0
  54. data/test/rails_root/config.ru +4 -0
  55. data/test/rails_root/db/seeds.rb +7 -0
  56. data/test/rails_root/lib/tasks/.gitkeep +0 -0
  57. data/test/rails_root/public/404.html +26 -0
  58. data/test/rails_root/public/422.html +26 -0
  59. data/test/rails_root/public/500.html +26 -0
  60. data/test/rails_root/public/favicon.ico +0 -0
  61. data/test/rails_root/public/images/rails.png +0 -0
  62. data/test/rails_root/public/javascripts/.gitkeep +0 -0
  63. data/test/rails_root/public/javascripts/application.js +0 -0
  64. data/test/rails_root/public/robots.txt +5 -0
  65. data/test/rails_root/public/stylesheets/.gitkeep +0 -0
  66. data/test/rails_root/public/stylesheets/application.css +27 -0
  67. data/test/rails_root/script/rails +6 -0
  68. data/test/rails_root/test/functional/c1_controller_test.rb +10 -0
  69. data/test/rails_root/test/functional/c2_controller_test.rb +15 -0
  70. data/test/rails_root/test/functional/c3_controller_test.rb +20 -0
  71. data/test/rails_root/test/functional/c4_controller_test.rb +24 -0
  72. data/test/rails_root/test/integration/backstack_test.rb +96 -0
  73. data/test/rails_root/test/performance/browsing_test.rb +9 -0
  74. data/test/rails_root/test/test_helper.rb +7 -0
  75. data/test/rails_root/test/unit/helpers/c1_helper_test.rb +4 -0
  76. data/test/rails_root/test/unit/helpers/c2_helper_test.rb +4 -0
  77. data/test/rails_root/test/unit/helpers/c3_helper_test.rb +4 -0
  78. data/test/rails_root/test/unit/helpers/c4_helper_test.rb +4 -0
  79. data/test/rails_root/vendor/plugins/.gitkeep +0 -0
  80. metadata +176 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "minitest", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.0"
12
+ gem "rcov", ">= 0"
13
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,20 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.6.3)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ minitest (2.3.1)
10
+ rake (0.9.2)
11
+ rcov (0.9.9)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ bundler (~> 1.0.0)
18
+ jeweler (~> 1.6.0)
19
+ minitest
20
+ rcov
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Kevin Swope
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,38 @@
1
+ = backstack
2
+
3
+ This is in extreme beta. The docs are not yet written and will
4
+ probably be supplimented with an illustrated blog posting.
5
+
6
+ Backstack is a gem plugin (currently just rails) that dynamically and
7
+ intelligently generates a "back" link, or breadcrumb trail. Sounds so
8
+ simple, why wouldn't it be?
9
+
10
+ To get a quick idea of what it does, goes to the test/rails_root
11
+ directory and 'rails server'
12
+
13
+ The plugin guts are in:
14
+ https://github.com/kswope/backstack/blob/master/lib/backstack.rb and
15
+ https://github.com/kswope/backstack/blob/master/lib/backstacklib.rb
16
+
17
+ Non-plugin tests are in:
18
+ https://github.com/kswope/backstack/blob/master/test/neutral/test_backstacklib.rb
19
+
20
+ Most rails tests are in:
21
+ https://github.com/kswope/backstack/blob/master/test/rails_root/test/integration/backstack_test.rb
22
+
23
+
24
+ == Contributing to backstack
25
+
26
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
27
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
28
+ * Fork the project
29
+ * Start a feature/bugfix branch
30
+ * Commit and push until you are happy with your contribution
31
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
32
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
33
+
34
+ == Copyright
35
+
36
+ Copyright (c) 2011 Kevin Swope. See LICENSE.txt for
37
+ further details.
38
+
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
12
+ end
13
+
14
+ require 'rake'
15
+
16
+ require 'jeweler'
17
+ Jeweler::Tasks.new do |gem|
18
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
19
+ gem.name = "backstack"
20
+ gem.homepage = "http://github.com/kswope/backstack"
21
+ gem.license = "MIT"
22
+ gem.summary = %Q{Rails plugin used to generate "back" links or a breadcrumb trail.}
23
+ gem.description = %Q{Rails plugin used to dynamically and intelligently generate "back" links or a breadcrumb trail.}
24
+ gem.email = "git-kevdev@snkmail.com"
25
+ gem.authors = ["Kevin Swope"]
26
+ # dependencies defined in Gemfile
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ task :default => :test
31
+ task :test => [:test_neutral, :test_rails]
32
+
33
+ require 'rake/testtask'
34
+ Rake::TestTask.new(:test_neutral) do |test|
35
+ test.libs << 'lib' << 'test/neutral'
36
+ test.pattern = 'test/neutral/**/test_*.rb'
37
+ test.verbose = true
38
+ end
39
+
40
+ # Bundler.setup (above) sets this and ruins rail's chance for loading
41
+ # properly in the test below. Somebody tell me why a gem is setting
42
+ # ENV variables.
43
+ ENV['BUNDLE_GEMFILE'] = nil
44
+
45
+ desc "Run tests in rails root"
46
+ rails_root = "test/rails_root"
47
+ command = "rake"
48
+ task :test_rails do |t|
49
+ chdir rails_root do
50
+ puts "*** descending into #{rails_root} and running '#{command}'"
51
+ system command
52
+ end
53
+ end
54
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
Binary file
data/backstack.gemspec ADDED
@@ -0,0 +1,127 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{backstack}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kevin Swope"]
12
+ s.date = %q{2011-07-09}
13
+ s.description = %q{Rails plugin used to dynamically and intelligently generate "back" links or a breadcrumb trail.}
14
+ s.email = %q{git-kevdev@snkmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "art/bowling_pins.graffle",
28
+ "backstack.gemspec",
29
+ "lib/backstack.rb",
30
+ "lib/backstacklib.rb",
31
+ "test/neutral/helper.rb",
32
+ "test/neutral/test_backstacklib.rb",
33
+ "test/rails_root/.gitignore",
34
+ "test/rails_root/Gemfile",
35
+ "test/rails_root/Gemfile.lock",
36
+ "test/rails_root/README",
37
+ "test/rails_root/Rakefile",
38
+ "test/rails_root/app/controllers/application_controller.rb",
39
+ "test/rails_root/app/controllers/c1_controller.rb",
40
+ "test/rails_root/app/controllers/c2_controller.rb",
41
+ "test/rails_root/app/controllers/c3_controller.rb",
42
+ "test/rails_root/app/controllers/c4_controller.rb",
43
+ "test/rails_root/app/helpers/application_helper.rb",
44
+ "test/rails_root/app/helpers/c1_helper.rb",
45
+ "test/rails_root/app/helpers/c2_helper.rb",
46
+ "test/rails_root/app/helpers/c3_helper.rb",
47
+ "test/rails_root/app/helpers/c4_helper.rb",
48
+ "test/rails_root/app/views/c1/a.html.erb",
49
+ "test/rails_root/app/views/c2/b.html.erb",
50
+ "test/rails_root/app/views/c2/c.html.erb",
51
+ "test/rails_root/app/views/c3/d.html.erb",
52
+ "test/rails_root/app/views/c3/e.html.erb",
53
+ "test/rails_root/app/views/c3/f.html.erb",
54
+ "test/rails_root/app/views/c4/g.html.erb",
55
+ "test/rails_root/app/views/c4/h.html.erb",
56
+ "test/rails_root/app/views/c4/i.html.erb",
57
+ "test/rails_root/app/views/c4/j.html.erb",
58
+ "test/rails_root/app/views/layouts/_pins.html.erb",
59
+ "test/rails_root/app/views/layouts/application.html.erb",
60
+ "test/rails_root/config.ru",
61
+ "test/rails_root/config/application.rb",
62
+ "test/rails_root/config/boot.rb",
63
+ "test/rails_root/config/environment.rb",
64
+ "test/rails_root/config/environments/development.rb",
65
+ "test/rails_root/config/environments/production.rb",
66
+ "test/rails_root/config/environments/test.rb",
67
+ "test/rails_root/config/initializers/backtrace_silencers.rb",
68
+ "test/rails_root/config/initializers/inflections.rb",
69
+ "test/rails_root/config/initializers/mime_types.rb",
70
+ "test/rails_root/config/initializers/secret_token.rb",
71
+ "test/rails_root/config/initializers/session_store.rb",
72
+ "test/rails_root/config/locales/en.yml",
73
+ "test/rails_root/config/routes.rb",
74
+ "test/rails_root/db/seeds.rb",
75
+ "test/rails_root/lib/tasks/.gitkeep",
76
+ "test/rails_root/public/404.html",
77
+ "test/rails_root/public/422.html",
78
+ "test/rails_root/public/500.html",
79
+ "test/rails_root/public/favicon.ico",
80
+ "test/rails_root/public/images/rails.png",
81
+ "test/rails_root/public/javascripts/.gitkeep",
82
+ "test/rails_root/public/javascripts/application.js",
83
+ "test/rails_root/public/robots.txt",
84
+ "test/rails_root/public/stylesheets/.gitkeep",
85
+ "test/rails_root/public/stylesheets/application.css",
86
+ "test/rails_root/script/rails",
87
+ "test/rails_root/test/functional/c1_controller_test.rb",
88
+ "test/rails_root/test/functional/c2_controller_test.rb",
89
+ "test/rails_root/test/functional/c3_controller_test.rb",
90
+ "test/rails_root/test/functional/c4_controller_test.rb",
91
+ "test/rails_root/test/integration/backstack_test.rb",
92
+ "test/rails_root/test/performance/browsing_test.rb",
93
+ "test/rails_root/test/test_helper.rb",
94
+ "test/rails_root/test/unit/helpers/c1_helper_test.rb",
95
+ "test/rails_root/test/unit/helpers/c2_helper_test.rb",
96
+ "test/rails_root/test/unit/helpers/c3_helper_test.rb",
97
+ "test/rails_root/test/unit/helpers/c4_helper_test.rb",
98
+ "test/rails_root/vendor/plugins/.gitkeep"
99
+ ]
100
+ s.homepage = %q{http://github.com/kswope/backstack}
101
+ s.licenses = ["MIT"]
102
+ s.require_paths = ["lib"]
103
+ s.rubygems_version = %q{1.6.2}
104
+ s.summary = %q{Rails plugin used to generate "back" links or a breadcrumb trail.}
105
+
106
+ if s.respond_to? :specification_version then
107
+ s.specification_version = 3
108
+
109
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
110
+ s.add_development_dependency(%q<minitest>, [">= 0"])
111
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
112
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.0"])
113
+ s.add_development_dependency(%q<rcov>, [">= 0"])
114
+ else
115
+ s.add_dependency(%q<minitest>, [">= 0"])
116
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
117
+ s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
118
+ s.add_dependency(%q<rcov>, [">= 0"])
119
+ end
120
+ else
121
+ s.add_dependency(%q<minitest>, [">= 0"])
122
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
123
+ s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
124
+ s.add_dependency(%q<rcov>, [">= 0"])
125
+ end
126
+ end
127
+
data/lib/backstack.rb ADDED
@@ -0,0 +1,161 @@
1
+ require "backstacklib"
2
+
3
+
4
+ module BackStack
5
+
6
+ def self.included(base)
7
+ base.send :extend, ClassMethods
8
+ end
9
+
10
+ module InstanceMethods
11
+ # maybe later
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ include BackStackLib
17
+
18
+ # Normalize controller and action to "controller#action", unless
19
+ # second param already has "#". This is up here in the class methods
20
+ # because normalizing controller/actions is utilized both here
21
+ # and in the ApplicationControllers objects
22
+ def bs_action_normal(controller, x)
23
+ x.to_s.index("#") ? "#{x}" : "#{controller}##{x}"
24
+ end
25
+
26
+ # This is the "macro" you put at the top of your controllers
27
+ def backstack(edges)
28
+
29
+ # Note: its a little hard to follow but @bs_graph is a instance
30
+ # variable of the class ApplicationController::Base (not an
31
+ # object of AC::B). There will only be one in the rails runtime,
32
+ # and may behave differently depending on whether we're in dev
33
+ # or prod mode, because dev mode reloads classes with each
34
+ # request, and prod hopefully doesn't. We'll write this module
35
+ # to work correctly under both circumstances.
36
+
37
+ # In rails we're going to use the string "controller#action" to
38
+ # identify the page. We're NOT going to let bs_add_edges
39
+ # normalize that because it might be used for other frameworks.
40
+ # If the user didn't pass in the controller we'll add it here.
41
+ # The complete value should look like "controller#action", for
42
+ # both keys and values.
43
+ normalizer = lambda {|x| bs_action_normal(controller_name, x) }
44
+
45
+ # Add new edges to existing graph, and extract out the names.
46
+ # bs_add_edges will also accumulate names for us.
47
+ @bs_graph, @bs_names = bs_add_edges(@bs_graph, @bs_names,
48
+ edges, normalizer)
49
+
50
+ end
51
+
52
+ # So ApplicationController methods can reach @bs_graph and
53
+ # @bs_names. I wanted to use @@bs_graph, so both AC::B and the
54
+ # instance of AC could reach it, but this mysteriously breaks
55
+ # rails routing! So here are getters than we can use and access
56
+ # from the AC instance as self.class.get_bs_graph. RoR can be a
57
+ # real pit of dispair sometimes.
58
+ def get_bs_graph
59
+ @bs_graph
60
+ end
61
+
62
+ def get_bs_names
63
+ @bs_names
64
+ end
65
+
66
+ send :include, InstanceMethods
67
+
68
+ end
69
+
70
+ # These functions will be available in views
71
+ module Helpers
72
+
73
+ def backstack_link(text, *args)
74
+
75
+ bs_graph = controller.class.get_bs_graph # found it! lol
76
+
77
+ # If we don't have these we can't do anything
78
+ return unless session[:bs_stack] && bs_graph
79
+
80
+ # If the top of stack (current location) is stacked on top of
81
+ # link the graph indicates it closes to, then create a link from
82
+ # that.
83
+ current = session[:bs_stack][-1]
84
+ previous = session[:bs_stack][-2]
85
+
86
+ if current && previous && bs_graph[current.first].include?(previous.first)
87
+ return link_to(text, previous.second, *args)
88
+ end
89
+
90
+ end
91
+
92
+ # Iterator to build breadcrumb trails
93
+ def backstack_trail
94
+
95
+ hashify = lambda{|x|
96
+ c, a = x[0].split /#/
97
+ {:controller => c, :action => a, :fullpath => x[1], :name => x[2]}
98
+ }
99
+
100
+ if block_given?
101
+ session[:bs_stack].each { |x| yield hashify.call(x) }
102
+ else # return an array
103
+ session[:bs_stack].map { |x| hashify.call(x) }
104
+ end
105
+
106
+ end
107
+
108
+ end
109
+
110
+
111
+ end
112
+
113
+
114
+ ActionController::Base.send :include, BackStack
115
+ ActionView::Helpers.send :include, BackStack::Helpers
116
+
117
+
118
+ # note, do not do this here:
119
+ # class ApplicationController < ActionController::Base
120
+ # it will prevent application_controller.rb from loading
121
+ class ActionController::Base
122
+
123
+ include BackStackLib
124
+
125
+ # Debugging method for calling inside controller, probably best as
126
+ # before_filter :backstack_dump
127
+ # in application_controller.rb
128
+ def backstack_dump
129
+
130
+ # don't accidentally run in production
131
+ return unless Rails.env == 'development'
132
+
133
+ puts "=== backstack_dump() " + '=' * 50
134
+
135
+ puts "backstack graph: #{self.class.get_bs_graph}"
136
+ puts "backstack names: #{self.class.get_bs_names}"
137
+ puts "backstack stack: #{session[:bs_stack]}"
138
+
139
+ puts '=' * 71
140
+
141
+ end
142
+
143
+
144
+
145
+ def bs_pusher
146
+
147
+ action = self.class.bs_action_normal(controller_name, action_name)
148
+
149
+ session[:bs_stack] = bs_push(self.class.get_bs_graph,
150
+ session[:bs_stack],
151
+ action,
152
+ request.fullpath,
153
+ self.class.get_bs_names[action])
154
+
155
+ end
156
+
157
+ before_filter :bs_pusher
158
+
159
+ end
160
+
161
+
@@ -0,0 +1,115 @@
1
+ # BackStackLib should know nothing about rails, sinatra or whatever
2
+ # framework this gem is supporting. It should also have no storage of
3
+ # its own. Keep it that way.
4
+ module BackStackLib
5
+
6
+ # Combine a "graph", described by connected nodes, in hash form,
7
+ # with another.
8
+ #
9
+ # Input graph must be normalized and described like this (could also
10
+ # be {}). Normalized means key is not a collection data type (not
11
+ # an array) but the value IS an array.
12
+ #
13
+ # In other words, don't pass as the first argument anything that hasn't
14
+ # been already normalized by this method, or use {} to start
15
+ #
16
+ # An example of normalized is {:b=>[:a], :c=>[:a], :z=>[:a, :x]}
17
+ # which reads "b is connected to a and c is connected to a and z is
18
+ # connected to both :a and :x", or more relevantly "b closes to a
19
+ # and c closes to a and z closes to both a and x".
20
+ #
21
+ # When this method is run it combines the *already* normalized graph
22
+ # (could be {}), with a another graph description, which for
23
+ # convenience can be in more of a shorthand, like {[:f, :e, :d] =>
24
+ # :c}, which reads "f, e, and d all close to c"
25
+ #
26
+ # The returned value will be a combination of the graphs, in a
27
+ # normalized form, for example {:b=>[:a], :c=>[:a], :f=>[:b, :c],
28
+ # :e=>[:b, :c], :d=>[:b, :c]}
29
+ #
30
+ # Note: the shorthand form of the edges parameter can be taken as far as
31
+ # something like this {[:d, :e, :f] => [:b, :c]}
32
+ #
33
+ # New feature: named nodes. Instead of {:c => :a}, the "key" can be
34
+ # named like this {{:c => "Charlie"} => :a}. We're going to remove
35
+ # those named keys, replace them with just the key, and return them as
36
+ # the second value
37
+ #
38
+ # Decided to allow user to pass a normalizer proc/lambda in to
39
+ # modify all the keys and values.
40
+ def bs_add_edges(graph, names, edges, normalizer=nil)
41
+
42
+ graph ||= {}
43
+ names ||= {}
44
+
45
+ edges.each do |k,v| # k is a scalar or array, same with v
46
+
47
+ # Does this [x].flatten idiom make it less readable?
48
+ [k].flatten.each do |x|
49
+
50
+ # Extract names out into their own hash, and normalize key
51
+ if x.class == Hash # if its a hash it contains a name
52
+ names[x.first[0]] = x.first[1]
53
+ x = x.first[0] # remove name and replace with normal key
54
+ end
55
+
56
+ # Run normalizer on all keys and values of new edges
57
+ if normalizer
58
+ x = normalizer.call(x)
59
+ v = [v].flatten.map{|y| normalizer.call(y)}
60
+ # also run normalizer on names keys
61
+ names = Hash[names.map {|k,v| [normalizer.call(k), v]}]
62
+ end
63
+
64
+ # If merge finds dupe keys it will use block to determine
65
+ # value, which in our case is to combine the two values.
66
+ graph.merge!(x => [v].flatten) do |key, old_v, new_v|
67
+ [old_v, new_v].flatten
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ [graph, names]
75
+
76
+ end
77
+
78
+ # Judging by graph, push onto stack if appropriate. Pushing doesn't
79
+ # necessarily build stack up, it might cause a rewind and actually
80
+ # shrink stack.
81
+ def bs_push(graph, stack, action, fullpath, name=nil)
82
+
83
+ # bs_push might be called before there is a graph or stack
84
+ graph ||= {}
85
+ stack ||= []
86
+
87
+ element = [action, fullpath, name]
88
+
89
+ # if action closes to what's on top of stack, build stack up
90
+ if graph[action] && stack.last && graph[action].include?(stack.last.first)
91
+ stack.push element
92
+ return stack
93
+ end
94
+
95
+ # if action already in stack rewind *past* it and place on stack
96
+ # (we rewind past it and push because the path part might be
97
+ # different)
98
+ if i = stack.find_index {|x| x.first == action}
99
+ stack = stack.slice(0,i)
100
+ stack.push element
101
+ return stack
102
+ end
103
+
104
+ # if none of the clever stuff above happened then just replace top
105
+ # of stack
106
+ if stack.empty?
107
+ return [element]
108
+ else
109
+ stack[-1] = element
110
+ return stack
111
+ end
112
+
113
+ end
114
+
115
+ end
@@ -0,0 +1,27 @@
1
+
2
+ require "backstacklib"
3
+ require 'minitest/autorun'
4
+
5
+ # All this following stuff is already in the Rakefile, why is it also
6
+ # here jeweler? -kswope
7
+
8
+ # require 'rubygems'
9
+ # require 'bundler'
10
+
11
+ # begin
12
+ # Bundler.setup(:default, :development)
13
+ # rescue Bundler::BundlerError => e
14
+ # $stderr.puts e.message
15
+ # $stderr.puts "Run `bundle install` to install missing gems"
16
+ # exit e.status_code
17
+ # end
18
+ # require 'minitest/unit'
19
+
20
+ # #$LOAD_PATH.unshift(File.dirname(__FILE__))
21
+ # #$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
22
+ # require 'backstacklib'
23
+
24
+ # class MiniTest::Unit::TestCase
25
+ # end
26
+
27
+ # MiniTest::Unit.autorun