stepstepstep 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,24 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14
+ watch('config/routes.rb') { "spec/routing" }
15
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
16
+
17
+ # Capybara request specs
18
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
19
+
20
+ # Turnip features and steps
21
+ watch(%r{^spec/acceptance/(.+)\.feature$})
22
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
23
+ end
24
+
data/License.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 by Blake Taylor
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,52 @@
1
+ Stepstepstep
2
+ =================================================
3
+ Rails before_filters don't take it far enough. What stepstepstep allows you to do is define before_filters's dependecies in the same way you do with rake tasks.
4
+
5
+ Install
6
+ -------------------------------------------------
7
+ Stick this in your Gemfile.
8
+ ```ruby
9
+ gem 'stepstepstep'
10
+ ```
11
+
12
+ Usage
13
+ -------------------------------------------------
14
+
15
+ #### Include the DSL && Defining steps
16
+
17
+ ```ruby
18
+ class FooController < ApplicationController
19
+ include Stepstepstep
20
+
21
+ step :two => [:one_point_three, :one_point_seven] do
22
+ @a << 2
23
+ end
24
+ step :one_point_three => :one do
25
+ @a << 1.3
26
+ end
27
+ step :one do
28
+ @a = [1]
29
+ end
30
+ step :one_point_seven => :one_point_three, :only => :index do
31
+ @a << 1.7
32
+ end
33
+
34
+ def index
35
+ render :inline => @a.inspect # => [1, 1.3, 1.7, 2].inspect
36
+ end
37
+
38
+ def another
39
+ render :inline => @a.inspect # => [1, 1.3, 2].inspect
40
+ end
41
+ end
42
+ ```
43
+
44
+ Why does stepstepstep.gem exists?
45
+ -------------------------------------------------
46
+ A few months ago, I was writing a single page application about learning mobile development technology at http://learn.eoe.cn. This page contains lessons, a video, classes, teachers, students, reference material, question-to-answers, exams, chat messages, and their current all learning statuses and dependencies. In brief, there are fifteen steps to load this page, including privileges to judge, fourteen illegal `redirect_to` , etc. So I need to write a step dependencies management tool, like rake tasks.
47
+
48
+ At first, I thought maybe I could define several `proc`s in a single before_filter, but the execution context is really complicated. Then one day, I found action_jackson.gem, which was written by [Blake Taylor](https://github.com/blakefrost/action_jackson) two years ago. The core implementation of this gem is to define each action as a method, and at last call a class method `register_filters` to register all these methods as `before_filter` independently. Of course, they're ordered by the earlier declarations. This implementation is not elegant, but the idea is really awesome, it doesn't break Rails's rules.
49
+
50
+ Then I got a deep understanding of the Rails controllers filters's implementation mechanism. Maybe `skip_before_filter` helped. In each `step`, I insert it first, extract all the inserted steps by `skip_before_filter`, then sort them by TSort(a topological sorting algorithm provided by Ruby standard library), and at last append them again to before_filters. It works, and all rspecs are passed.
51
+
52
+ I renamed it from action_jackson to stepstepstep, because the DSL is only a `step` class method, which handles all the details. Most of the implementations were rewritten, and I added rspecs . Thanks Blake Taylor :)
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'test'
8
+ end
9
+
10
+ desc "Run tests"
11
+ task :default => :test
@@ -0,0 +1,83 @@
1
+ # encoding: UTF-8
2
+
3
+ module Stepstepstep
4
+ extend ActiveSupport::Concern
5
+
6
+ # Topological sorting using Tarjan's algorithm for strongly connected components.
7
+ require 'tsort'
8
+ class StepsInTSort < Hash
9
+ include TSort
10
+ alias tsort_each_node each_key
11
+ def tsort_each_child(node, &block)
12
+ (self[node] || []).each(&block)
13
+ end
14
+ end
15
+
16
+ included do
17
+ cattr_accessor :_steps_set, :_step_to_deps, :_before_filter_opts_hash
18
+ self._steps_set ||= Set.new
19
+ self._step_to_deps ||= StepsInTSort.new
20
+ self._before_filter_opts_hash ||= Hash.new { Hash.new }
21
+ end
22
+
23
+ module ClassMethods
24
+ def step(opts, opts2 = nil, &blk)
25
+ # adjust params
26
+ opts = opts2.merge(opts => nil) if opts2.is_a?(Hash)
27
+
28
+ # prepare data
29
+ __opts = {}
30
+ if opts.is_a?(Hash)
31
+ [:only, :except, :if].each do |symbol|
32
+ __opts[symbol] = opts.delete(symbol) if opts[symbol]
33
+ end
34
+ step_name, __deps = opts.first
35
+ step_name = step_name.to_sym
36
+ add_step_to_dep step_name, __deps
37
+ self._before_filter_opts_hash[step_name] = __opts
38
+ elsif opts.is_a?(Symbol) || opts.is_a?(String)
39
+ step_name = opts.to_sym
40
+ add_step_to_dep step_name
41
+ else
42
+ raise "Please use Hash, Symbol, String for opts"
43
+ end
44
+
45
+ # define method
46
+ if self._steps_set.include?(step_name)
47
+ if not self.instance_methods.include?(step_name)
48
+ blk ||= (Proc.new {})
49
+ define_method(step_name, blk)
50
+ else
51
+ Rails.logger.info "#{self.class.name}##{step_name} is already defined!"
52
+ end
53
+ end
54
+
55
+ # 1. append first
56
+ send(:before_filter, step_name, self._before_filter_opts_hash[step_name])
57
+
58
+ # 2. extract all
59
+ self._steps_set.each {|n1| self.skip_before_filter n1 }
60
+
61
+ # 3. resort
62
+ _steps = self._step_to_deps.tsort
63
+ _steps.each do |n1|
64
+ # 4. reappend all
65
+ if self.instance_methods.include?(n1)
66
+ puts "filter to opts: #{n1}, #{self._before_filter_opts_hash}" if ENV['DEBUG_STEPSTEPSTE']
67
+ self.send(:before_filter, n1, self._before_filter_opts_hash[n1])
68
+ end
69
+ end
70
+ end
71
+
72
+ private
73
+ def add_step_to_dep n1, deps = nil
74
+ self._steps_set.add n1
75
+
76
+ deps = Array(deps).compact
77
+ deps.each {|i| self._steps_set.add i }
78
+
79
+ self._step_to_deps[n1] = deps
80
+ end
81
+ end
82
+
83
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ describe FooController do
7
+ it "@a is sort correctly" do
8
+ callbacks = FooController.new._process_action_callbacks
9
+ puts "FooController.new._process_action_callbacks.size is #{callbacks.size}, filters are #{callbacks.map(&:filter)}"
10
+
11
+ get :index
12
+ response.body.should == [1, 1.3, 1.7, 2].inspect
13
+ end
14
+
15
+ it "@a should skip 1.7" do
16
+ get :another
17
+ response.body.should == [1, 1.3, 2, "another"].inspect
18
+ end
19
+ end
20
+
21
+ describe NextController do
22
+ it "skip if next in step method" do
23
+ get :index
24
+ response.body.should == [1, 1.3, 1.7, 2].inspect
25
+ end
26
+ end
27
+
28
+ describe RemoveController do
29
+ it "remove one step" do
30
+ get :index
31
+ response.body.should == [1, 1.7, 2].inspect
32
+ end
33
+ end
34
+ describe AfterRemoveController do
35
+ it "after remove one step" do
36
+ get :index
37
+ response.body.should == [1, 1.3, 1.7, 2].inspect
38
+ end
39
+ end
40
+
41
+ describe InsertController do
42
+ it "Insert one step" do
43
+ get :index
44
+ response.body.should == [1, 1.1, 1.3, 1.7, 2].inspect
45
+ end
46
+ end
47
+
48
+ describe SubController do
49
+ it "only should be skiped" do
50
+ get :index
51
+ response.body.should == [1, 1.3, 1.7, 2].inspect
52
+ end
53
+ it "only another" do
54
+ get :another
55
+ response.body.should == [1, 1.3, 2, 3, "another"].inspect
56
+ end
57
+ end
58
+
59
+ describe Sub2Controller do
60
+ it "one_point_three should be excepted" do
61
+ get :index
62
+ response.body.should == [1, 1.7, 2].inspect
63
+ end
64
+ end
65
+
66
+
67
+ describe RedirectToController do
68
+ it "support redirect_to" do
69
+ get :index
70
+ response.should redirect_to("/redirect_to")
71
+ end
72
+ end
@@ -0,0 +1,116 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'active_support/core_ext'
4
+ require 'action_controller'
5
+ require 'active_model'
6
+ require 'rails/all'
7
+
8
+
9
+ require 'rspec/rails'
10
+ require 'pry-debugger'
11
+ require File.join(ENV['HOME'], 'utils/ruby/irb.rb') rescue nil
12
+
13
+ $:.push File.expand_path("../../lib", __FILE__)
14
+ require 'stepstepstep'
15
+
16
+ Rails.logger ||= Logger.new($stderr)
17
+ ENV['DEBUG_STEPSTEPSTE'] = 'true' if `whoami`.strip == 'mvj3'
18
+
19
+
20
+ # https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/engine-routes-for-controllers
21
+
22
+ module Dummy
23
+ class Application < Rails::Application
24
+ end
25
+ end
26
+
27
+ class BarController < ActionController::Base
28
+ # Fix In order to use #url_for, you must include routing helpers explicitly. For instance, `include Rails.application.routes.url_helpers
29
+ # thx https://github.com/apotonick/apotomo/issues/35#issuecomment-3936941
30
+ # TODO blankslate.rails_engine.rspec.gem
31
+ include Rails.application.routes.url_helpers
32
+ def _routes; ::Rails.application.routes end
33
+ def controller; parent_controller end
34
+
35
+ end
36
+
37
+ class FooController < BarController
38
+ include Stepstepstep
39
+
40
+ step :two => [:one_point_three, :one_point_seven] do
41
+ @a << 2
42
+ end
43
+ step :one_point_three => :one do
44
+ @a << 1.3
45
+ end
46
+ step :one do
47
+ @a = [1]
48
+ end
49
+ step :one_point_seven => :one_point_three, :only => :index do
50
+ @a << 1.7
51
+ end
52
+
53
+ def index
54
+ render :inline => @a.inspect
55
+ end
56
+
57
+ def another
58
+ @a << 'another'
59
+ render :inline => @a.inspect
60
+ end
61
+ end
62
+
63
+
64
+ class NextController < FooController
65
+ step :test_next_with_three => :two do
66
+ next if true
67
+ @a << 3
68
+ end
69
+ end
70
+
71
+ class RemoveController < FooController
72
+ skip_filter :one_point_three
73
+ end
74
+ class AfterRemoveController < FooController
75
+ end
76
+ class InsertController < FooController
77
+ step :one_point_one => :one do
78
+ @a << 1.1
79
+ end
80
+ step :one_point_three => :one_point_one
81
+ end
82
+ class SubController < FooController
83
+ step :only_action => :two, :only => :another do
84
+ @a << 3
85
+ end
86
+
87
+ def another
88
+ @a << 'another'
89
+ render :inline => @a.inspect
90
+ end
91
+ end
92
+ class Sub2Controller < SubController
93
+ step :one_point_three, :except => [:index]
94
+ step :two => [:one, :one_point_seven] # remember to connect the broken edges
95
+ end
96
+
97
+ class RedirectToController < FooController
98
+ def index
99
+ redirect_to "/redirect_to" and return
100
+ end
101
+ end
102
+
103
+ Dummy::Application.routes.draw do
104
+ [:bar, :remove, :after_remove, :insert, :sub2, :redirect_to, :next].each do |s|
105
+ resources s
106
+ end
107
+ [:foo, :sub].each do |r|
108
+ resources r do
109
+ collection do
110
+ get :another
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "stepstepstep"
5
+ s.version = "0.0.1"
6
+ s.authors = ["Blake Taylor", "David Chen"]
7
+ s.email = ["blakefrost@gmail.com", "mvjome@gmail.com"]
8
+ s.homepage = "https://github.com/eoecn/stepstepstep"
9
+ s.summary = %q{DSL for defining before_filters's dependencies like rake tasks.}
10
+ s.description = File.read("README.markdown").split(/===+/)[0].strip
11
+ s.license = "MIT"
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_dependency "rake"
19
+ s.add_dependency "rails"
20
+ s.add_dependency "actionpack"
21
+ s.add_dependency "activesupport"
22
+
23
+ s.add_development_dependency 'pry-debugger'
24
+ s.add_development_dependency 'rspec-rails', '~> 2.0'
25
+ s.add_development_dependency 'guard-rspec'
26
+
27
+ end
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stepstepstep
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Blake Taylor
9
+ - David Chen
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-07-13 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rake
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: rails
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: actionpack
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: activesupport
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ - !ruby/object:Gem::Dependency
80
+ name: pry-debugger
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :development
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rspec-rails
97
+ requirement: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ~>
101
+ - !ruby/object:Gem::Version
102
+ version: '2.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '2.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard-rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ description: Stepstepstep
128
+ email:
129
+ - blakefrost@gmail.com
130
+ - mvjome@gmail.com
131
+ executables: []
132
+ extensions: []
133
+ extra_rdoc_files: []
134
+ files:
135
+ - .gitignore
136
+ - Gemfile
137
+ - Guardfile
138
+ - License.txt
139
+ - README.markdown
140
+ - Rakefile
141
+ - lib/stepstepstep.rb
142
+ - spec/controllers/foo_controller_spec.rb
143
+ - spec/spec_helper.rb
144
+ - stepstepstep.gemspec
145
+ homepage: https://github.com/eoecn/stepstepstep
146
+ licenses:
147
+ - MIT
148
+ post_install_message:
149
+ rdoc_options: []
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ! '>='
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubyforge_project:
166
+ rubygems_version: 1.8.25
167
+ signing_key:
168
+ specification_version: 3
169
+ summary: DSL for defining before_filters's dependencies like rake tasks.
170
+ test_files:
171
+ - spec/controllers/foo_controller_spec.rb
172
+ - spec/spec_helper.rb