stepstepstep 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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