roda-plugins 0.0.2 → 0.0.3

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: 543af8c2f42d4aae849b5c93f0b295cc1fb504c8
4
- data.tar.gz: cc52ff3a94e5000ea00e01e8912beceeefd9dd8f
3
+ metadata.gz: f390fa66a5124b6f929fc4569dae579a31320676
4
+ data.tar.gz: e14588885bc08bfaa9da27f7403dc0e112ac8452
5
5
  SHA512:
6
- metadata.gz: b9d3f2bbfe4fc14c2a0aa992fb132bd3e1ba9dc0fa053b8a54e6dce34e41cfcce2c910efc1d9eba20630d533b450851ff047db0d5e24e4d6e05acd03967d5da3
7
- data.tar.gz: 9205e24946270fdfc9d6bf2d87f8bc186d5dd0c36133e9b673100aad83392a67112cce7e1cb0fc31f87bf1090be3927e0bb33497575f49e8413cc6dd8e6e8bbd
6
+ metadata.gz: f6df848c99bb4b8eb030e2f7548330895bdc227b25233619ff8820702c0061790b4deb28cfcdd282747e614e350923489d9a5da9d482a133112112dd2c5ad7a1
7
+ data.tar.gz: 69f3b7eacab67dd5aa2e8a2319db5e75e56b91607ff9c59fb669bdfab562d929778973cee1a31fc9528f91bfac050196e85c2b76829c691d2af2746aff508561
data/.gems ADDED
@@ -0,0 +1,7 @@
1
+ roda -v 1.0.0
2
+ nokogiri -v 1.6.3.1
3
+ cutest-cj -v 1.7.0
4
+ tilt -v 2.0.1
5
+ sass -v 3.4.1
6
+ coffee-script -v 2.3.0
7
+ therubyracer -v 0.12.1
data/.gitignore CHANGED
@@ -20,3 +20,6 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ .gs
24
+ .env*
25
+ .sass-cache
@@ -0,0 +1 @@
1
+ 2.1.2
@@ -0,0 +1,45 @@
1
+ GEMSPEC=$(shell ls *.gemspec | head -1)
2
+ VERSION=$(shell ruby -rubygems -e 'puts Gem::Specification.load("$(GEMSPEC)").version')
3
+ PROJECT=$(shell ruby -rubygems -e 'puts Gem::Specification.load("$(GEMSPEC)").name')
4
+ GEM=$(PROJECT)-$(VERSION).gem
5
+
6
+ .PHONY: install package publish test server $(GEM)
7
+
8
+ define install_bs
9
+ which bs || (wget https://raw.githubusercontent.com/educabilia/bs/master/bin/bs && chmod +x bs && sudo mv bs /usr/local/bin)
10
+
11
+ @if [ -s .gs ]; then \
12
+ true; \
13
+ else \
14
+ mkdir .gs; \
15
+ touch .env; \
16
+ echo 'GEM_HOME=$(PWD)/.gs' >> .env; \
17
+ echo 'GEM_PATH=$(PWD)/.gs' >> .env; \
18
+ echo 'PATH=$(PWD)/.gs/bin:$$PATH' >> .env; \
19
+ echo 'RACK_ENV=test' >> .env.test; \
20
+ fi;
21
+
22
+ bs gem list dep-cj -i || bs gem install dep-cj
23
+ gem list cutest-cj -i || gem install cutest-cj
24
+ bs gem list pry -i || bs gem install pry
25
+ bs gem list awesome_print -i || bs gem install awesome_print
26
+ endef
27
+
28
+ install:
29
+ $(call install_bs)
30
+ bs dep install
31
+ bs gem cleanup
32
+
33
+ test:
34
+ bs env $$(cat .env.test) cutest test/**/*_test.rb
35
+
36
+ package: $(GEM)
37
+
38
+ # Always build the gem
39
+ $(GEM):
40
+ gem build $(PROJECT).gemspec
41
+
42
+ publish: $(GEM)
43
+ gem push $(GEM)
44
+ rm $(GEM)
45
+ git tag -a $(VERSION)
@@ -0,0 +1,178 @@
1
+ require "tilt"
2
+
3
+ class Roda
4
+ module RodaPlugins
5
+ module Assets
6
+ def self.load_dependencies(app, opts={})
7
+ app.plugin :render
8
+ end
9
+
10
+ def self.configure(app, opts={}, &block)
11
+ if app.opts[:assets]
12
+ app.opts[:assets].merge!(opts)
13
+ else
14
+ app.opts[:assets] = opts.dup
15
+ end
16
+
17
+ opts = app.opts[:assets]
18
+ opts[:css] ||= []
19
+ opts[:js] ||= []
20
+ opts[:js_folder] ||= 'js'
21
+ opts[:css_folder] ||= 'css'
22
+ opts[:path] ||= File.expand_path("assets", Dir.pwd)
23
+ opts[:route] ||= 'assets'
24
+ opts[:css_engine] ||= 'scss'
25
+ opts[:js_engine] ||= 'coffee'
26
+ opts[:headers] ||= {}
27
+ opts[:cache] = app.thread_safe_cache if opts.fetch(:cache, true)
28
+
29
+ yield opts if block
30
+ end
31
+
32
+ module ClassMethods
33
+ # Copy the assets options into the subclass, duping
34
+ # them as necessary to prevent changes in the subclass
35
+ # affecting the parent class.
36
+ def inherited(subclass)
37
+ super
38
+ opts = subclass.opts[:assets] = assets_opts.dup
39
+ opts[:cache] = thread_safe_cache if opts[:cache]
40
+ end
41
+
42
+ # Return the assets options for this class.
43
+ def assets_opts
44
+ opts[:assets]
45
+ end
46
+
47
+
48
+ def asset_path file, type
49
+ file.gsub!(/\.#{type}$/, '')
50
+ assets = assets_opts[:"#{type}"]
51
+
52
+ if assets.is_a? Array
53
+ file_path = assets.select {|a| a["#{file}"]}.first
54
+ else
55
+ file = file.split('/')
56
+ sub = file.shift.to_sym
57
+ file = file.join '/'
58
+ file_path = assets[sub].select {|a| a["#{file}"]}.first
59
+
60
+ if file_path && !file_path[/^\.\//]
61
+ file_path = "#{sub}/#{file_path}"
62
+ end
63
+ end
64
+
65
+ folder = assets_opts[:"#{type}_folder"]
66
+
67
+ if file_path
68
+ if !file_path[/^\.\//]
69
+ "#{assets_opts[:path]}/#{folder}/#{file_path}"
70
+ else
71
+ file_path
72
+ end
73
+ else
74
+ file
75
+ end
76
+ end
77
+ end
78
+
79
+ module InstanceMethods
80
+ def assets_opts
81
+ self.class.assets_opts
82
+ end
83
+
84
+ def assets type, options = {}
85
+ attrs = options.map{|k,v| "#{k}=\"#{v}\""}
86
+ tags = []
87
+ type = [type] unless type.is_a? Array
88
+ files = type.length == 1 \
89
+ ? assets_opts[:"#{type[0]}"] \
90
+ : assets_opts[:"#{type[0]}"][:"#{type[1]}"]
91
+
92
+ files.each do |file|
93
+ file.gsub!(/\./, '$2E')
94
+ file = file.split('/')
95
+ file[file.length - 1] = file.last.gsub(/\$2E/, '.')
96
+ file = file.join '/'
97
+ path = assets_opts[:route] + '/' + assets_opts[:"#{type[0]}_folder"]
98
+ attr = type[0].to_s == 'js' ? 'src' : 'href'
99
+ attrs.unshift "#{attr}=\"/#{path}/#{file}\""
100
+ tags << send("#{type[0]}_assets_tag", attrs.join(' '))
101
+ end
102
+
103
+ tags.join "\n"
104
+ end
105
+
106
+ private
107
+
108
+ # <link rel="stylesheet" href="theme.css">
109
+ def css_assets_tag attrs
110
+ "<link rel=\"stylesheet\" #{attrs} />"
111
+ end
112
+
113
+ # <script src="scriptfile.js"></script>
114
+ def js_assets_tag attrs
115
+ "<script type=\"text/javascript\" #{attrs}></script>"
116
+ end
117
+
118
+ def render_asset *args
119
+ self.class.render_asset(*args)
120
+ end
121
+ end
122
+
123
+ module RequestClassMethods
124
+ def assets_opts
125
+ roda_class.assets_opts
126
+ end
127
+
128
+ %w(css js).each do |type|
129
+ define_method "#{type}_assets_path" do
130
+ Regexp.new(
131
+ assets_opts[:route] + '/' + assets_opts[:"#{type}_folder"] + '/(.*)'
132
+ )
133
+ end
134
+ end
135
+ end
136
+
137
+ module RequestMethods
138
+ def assets
139
+ %w(css js).each do |type|
140
+ on self.class.public_send "#{type}_assets_path" do |file|
141
+ file.gsub!(/\$2E/, '.')
142
+
143
+ content_type = Rack::Mime.mime_type File.extname(file)
144
+
145
+ response.headers.merge!({
146
+ "Content-Type" => content_type + '; charset=UTF-8',
147
+ }.merge(scope.assets_opts[:headers]))
148
+
149
+ engine = scope.assets_opts[:"#{type}_engine"]
150
+
151
+ if !file[/^\.\//]
152
+ path = scope.assets_opts[:route] + '/' + scope.assets_opts[:"#{type}_folder"] + "/#{file}"
153
+ else
154
+ path = file
155
+ end
156
+
157
+ if File.exists? "#{path}.#{engine}"
158
+ scope.render path: "#{path}.#{engine}"
159
+ elsif File.exists? "#{path}.#{type}"
160
+ File.read "#{path}.#{type}"
161
+ elsif File.exists?(path) && path[/\.#{type}$/]
162
+ File.read path
163
+ elsif File.exists? path
164
+ begin
165
+ scope.render path: path
166
+ rescue
167
+ File.read path
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ register_plugin(:assets, Assets)
177
+ end
178
+ end
@@ -0,0 +1,195 @@
1
+ class Roda
2
+ module RodaPlugins
3
+ module Components
4
+ def self.configure(app, opts={})
5
+ if app.opts[:components]
6
+ app.opts[:components].merge!(opts)
7
+ else
8
+ app.opts[:components] = opts.dup
9
+ end
10
+
11
+ opts = app.opts[:components]
12
+ opts[:cache] = app.thread_safe_cache if opts.fetch(:cache, true)
13
+ opts[:settings] ||= {}
14
+ opts[:cache][:components] ||= {}
15
+ opts[:cache][:events] ||= {}
16
+ end
17
+
18
+ module ClassMethods
19
+ def inherited(subclass)
20
+ super
21
+ opts.merge! subclass.opts[:components]
22
+ end
23
+
24
+ def components_opts
25
+ opts[:components]
26
+ end
27
+
28
+ def components
29
+ cache[:components].keys
30
+ end
31
+
32
+ def load_component name
33
+ cache[:components][name]
34
+ end
35
+
36
+ def component(name, events = [], &block)
37
+ name = name.to_s
38
+ cache[:components][name] = block
39
+ cache[:events][name] ||= {}
40
+
41
+ events.each do |event|
42
+ if event.is_a? String
43
+ event_array = cache[:events][name][event] ||= []
44
+ event_array << { component: name, call: event }
45
+ elsif event.is_a? Hash
46
+ for_component = event[:for].to_s
47
+ response_to = event[:respond_to].to_s
48
+ call_with = event[:with]
49
+
50
+ event_array = cache[:events][for_component || name][response_to] ||= []
51
+ event_array << { component: name, call: call_with || response_to }
52
+ end
53
+ end
54
+ end
55
+
56
+ def load_setup_component name
57
+ cache[:components]["_setup_#{name}"]
58
+ end
59
+
60
+ def setup_component(name, &block)
61
+ cache[:components]["_setup_#{name}"] = block
62
+ end
63
+
64
+ private
65
+
66
+ def cache
67
+ components_opts[:cache]
68
+ end
69
+ end
70
+
71
+ module InstanceMethods
72
+ def component(name, opts = {})
73
+ name = name.to_s
74
+
75
+ component_request = ComponentRequest.new(self, self.class, name, opts)
76
+
77
+ content = catch :halt do
78
+ if setup_component = self.class.load_setup_component(name)
79
+ instance_exec(component_request, &setup_component)
80
+ end
81
+
82
+ opts[:call] ||= 'display'
83
+
84
+ instance_exec(component_request, &self.class.load_component(name))
85
+
86
+ raise "Couldn't find on method `#{opts[:call]}`, for the `#{name}` component."
87
+ end
88
+
89
+ response.write content if content.is_a? String
90
+
91
+ component_request.trigger_events
92
+ end
93
+ end
94
+
95
+ class ComponentRequest
96
+ attr_reader :app, :component_class, :component_name, :component_opts, :cache
97
+
98
+ def initialize app, component_class, component_name, opts = {}
99
+ @app = app
100
+ @component_class = component_class
101
+ @component_name = component_name
102
+ @component_opts = opts
103
+ @cache = Roda::RodaCache.new
104
+ end
105
+
106
+ def on name, &block
107
+ name = name.to_s
108
+
109
+ if name == component_opts[:call].to_s
110
+ throw :halt, yield(component_opts[:locals] || {})
111
+ end
112
+ end
113
+
114
+ def display &block
115
+ on 'display', &block
116
+ end
117
+
118
+ def html &block
119
+ class_cache[:html_loaded] ||= begin
120
+ comp_cache[:html] ||= yield
121
+ true
122
+ end
123
+ end
124
+
125
+ def setup &block
126
+ class_cache[:ran_setup] ||= begin
127
+ block.call comp_dom, comp_tmpl
128
+ true
129
+ end
130
+ end
131
+
132
+ def dom
133
+ cache[:dom] ||= comp_cache[:dom].dup
134
+ end
135
+
136
+ def dom_html
137
+ dom.to_html
138
+ end
139
+
140
+ def tmpl name
141
+ (cache[:tmpl] ||= {}).fetch(name){ comp_tmpl.fetch(name).dup }
142
+ end
143
+
144
+ def set_tmpl name, value, keep = false
145
+ comp_tmpl[name] = value
146
+ value.remove unless keep
147
+ end
148
+
149
+ def trigger_events
150
+ trigger component_opts.dup.delete(:call), component_opts
151
+ end
152
+
153
+ def trigger event, opts = {}
154
+ event = event.to_s
155
+
156
+ if opts.key?(:for)
157
+ name = opts.delete(:for).to_s
158
+ else
159
+ name = component_name
160
+ end
161
+
162
+ if events = class_cache[:events][name]
163
+ (events[event] || []).each do |e|
164
+ if component_opts[:call] != e[:call]
165
+ e_opts = opts.dup.merge({call: e[:call]})
166
+ app.component e[:component], e_opts
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ private
173
+
174
+ def comp_dom
175
+ comp_cache[:dom] ||= Nokogiri::HTML(comp_cache[:html])
176
+ end
177
+
178
+ def comp_tmpl
179
+ comp_cache[:tmpl] ||= {}
180
+ end
181
+
182
+ def class_cache
183
+ component_class.send(:cache)
184
+ end
185
+
186
+ def component_cache
187
+ class_cache["#{component_name}_cache"] ||= {}
188
+ end
189
+ alias :comp_cache :component_cache
190
+ end
191
+ end
192
+
193
+ register_plugin(:components, Components)
194
+ end
195
+ end
@@ -1,3 +1,3 @@
1
1
  class Roda
2
- PluginsVersion = '0.0.2'.freeze
2
+ PluginsVersion = '0.0.3'.freeze
3
3
  end
@@ -0,0 +1,53 @@
1
+ require_relative 'helper'
2
+
3
+ require 'tilt'
4
+ require 'tilt/sass'
5
+ require 'tilt/coffee'
6
+
7
+ setup do
8
+ app :bare do
9
+ plugin(:assets, {
10
+ path: './test/dummy/assets',
11
+ css_engine: 'scss',
12
+ js_engine: 'coffee',
13
+ headers: {
14
+ "Cache-Control" => 'public, max-age=2592000, no-transform',
15
+ 'Connection' => 'keep-alive',
16
+ 'Age' => '25637',
17
+ 'Strict-Transport-Security' => 'max-age=31536000',
18
+ 'Content-Disposition' => 'inline'
19
+ }
20
+ })
21
+
22
+ assets_opts[:css] = ['app', '../raw.css']
23
+ assets_opts[:js] = { head: ['app'] }
24
+
25
+ route do |r|
26
+ r.assets
27
+
28
+ r.is 'test' do
29
+ response.write assets :css
30
+ response.write assets [:js, :head]
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ scope 'assets' do
37
+ test 'config' do |app|
38
+ assert app.assets_opts[:path] == './test/dummy/assets'
39
+ assert app.assets_opts[:css].include? 'app'
40
+ end
41
+
42
+ test 'middleware/render' do |app|
43
+ assert body('/assets/css/app.css')['color: red']
44
+ assert body('/assets/css/raw.css')['color: blue']
45
+ assert body('/assets/js/head/app.js')['console.log']
46
+ end
47
+
48
+ test 'instance_methods' do |app|
49
+ html = body '/test'
50
+ assert html['link']
51
+ assert html['script']
52
+ end
53
+ end
@@ -0,0 +1,99 @@
1
+ require_relative 'helper'
2
+ require 'nokogiri'
3
+
4
+ setup do
5
+ app :bare do
6
+ plugin :components
7
+
8
+ component(:foo, ['world']) do |c|
9
+ c.display do
10
+ 'bar'
11
+ end
12
+
13
+ c.on 'bar' do
14
+ 'foo'
15
+ end
16
+
17
+ c.on 'test' do
18
+ c.trigger 'world'
19
+ 'foo'
20
+ end
21
+
22
+ c.on 'world' do
23
+ 'hello'
24
+ end
25
+
26
+ c.on 'dom' do
27
+ c.dom_html
28
+ end
29
+ end
30
+
31
+ events = [
32
+ {respond_to: 'test', for: :foo, with: 'foo_test'},
33
+ {respond_to: 'world', for: :foo},
34
+ ]
35
+
36
+ component(:bar, events) do |c|
37
+ c.on 'foo_test' do
38
+ 'bar'
39
+ end
40
+
41
+ c.on 'world' do
42
+ 'world'
43
+ end
44
+ end
45
+
46
+ route do |r|
47
+ r.root do
48
+ component(:foo)
49
+ end
50
+
51
+ r.on 'bar' do
52
+ component(:foo, call: :bar)
53
+ end
54
+
55
+ r.on 'test' do
56
+ component(:foo, call: 'test')
57
+ end
58
+
59
+ r.on 'dom' do
60
+ component(:foo, call: :dom)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ scope 'components' do
67
+ test '#routing' do
68
+ assert body['bar']
69
+ assert !body['foo']
70
+ assert body('/bar')['foo']
71
+ assert !body('/bar')['bar']
72
+ end
73
+
74
+ test '#before' do |app|
75
+ app.setup_component(:foo) do |c|
76
+ c.html do
77
+ <<-EOF
78
+ <html>
79
+ <body>
80
+ <div class='before'>before</div>
81
+ </body>
82
+ <html>
83
+ EOF
84
+ end
85
+
86
+ c.setup do |dom|
87
+ dom.at('body').add_child '<div>after</div>'
88
+ end
89
+ end
90
+
91
+ body = body('/dom')
92
+ assert body['before']
93
+ assert body['after']
94
+ end
95
+
96
+ test 'events' do |app|
97
+ assert body('/test')['helloworldfoobar']
98
+ end
99
+ end
@@ -0,0 +1 @@
1
+ body { color: red; }
@@ -0,0 +1 @@
1
+ console.log 'test'
@@ -0,0 +1 @@
1
+ body { color: blue; }
@@ -0,0 +1,57 @@
1
+ lib = File.expand_path('../../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'roda'
5
+
6
+ after do
7
+ @app = nil
8
+ end
9
+
10
+ module Kernel
11
+ private
12
+
13
+ def app(type=nil, &block)
14
+ case type
15
+ when :new
16
+ @app = _app{route(&block)}
17
+ when :bare
18
+ @app = _app(&block)
19
+ when Symbol
20
+ @app = _app do
21
+ plugin type
22
+ route(&block)
23
+ end
24
+ else
25
+ @app ||= _app{route(&block)}
26
+ end
27
+ end
28
+
29
+ def req(path='/', env={})
30
+ if path.is_a?(Hash)
31
+ env = path
32
+ else
33
+ env['PATH_INFO'] = path
34
+ end
35
+
36
+ env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/", "SCRIPT_NAME" => ""}.merge(env)
37
+ @app.call(env)
38
+ end
39
+
40
+ def status(path='/', env={})
41
+ req(path, env)[0]
42
+ end
43
+
44
+ def header(name, path='/', env={})
45
+ req(path, env)[1][name]
46
+ end
47
+
48
+ def body(path='/', env={})
49
+ req(path, env)[2].join
50
+ end
51
+
52
+ def _app(&block)
53
+ c = Class.new(Roda)
54
+ c.class_eval(&block)
55
+ c
56
+ end
57
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda-plugins
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - cj
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-10 00:00:00.000000000 Z
11
+ date: 2014-09-03 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Plugins for Roda
14
14
  email:
@@ -17,15 +17,24 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
- - .gitignore
21
- - Gemfile
20
+ - ".gems"
21
+ - ".gitignore"
22
+ - ".ruby-version"
22
23
  - LICENSE.txt
24
+ - Makefile
23
25
  - README.md
24
- - Rakefile
25
26
  - lib/roda/plugins.rb
27
+ - lib/roda/plugins/assets.rb
28
+ - lib/roda/plugins/components.rb
26
29
  - lib/roda/plugins/root.rb
27
30
  - lib/roda/plugins/version.rb
28
31
  - roda-plugins.gemspec
32
+ - test/assets_test.rb
33
+ - test/components_test.rb
34
+ - test/dummy/assets/css/app.scss
35
+ - test/dummy/assets/js/head/app.coffee
36
+ - test/dummy/assets/raw.css
37
+ - test/helper.rb
29
38
  homepage: ''
30
39
  licenses:
31
40
  - MIT
@@ -36,19 +45,24 @@ require_paths:
36
45
  - lib
37
46
  required_ruby_version: !ruby/object:Gem::Requirement
38
47
  requirements:
39
- - - '>='
48
+ - - ">="
40
49
  - !ruby/object:Gem::Version
41
50
  version: '0'
42
51
  required_rubygems_version: !ruby/object:Gem::Requirement
43
52
  requirements:
44
- - - '>='
53
+ - - ">="
45
54
  - !ruby/object:Gem::Version
46
55
  version: '0'
47
56
  requirements: []
48
57
  rubyforge_project:
49
- rubygems_version: 2.0.14
58
+ rubygems_version: 2.2.2
50
59
  signing_key:
51
60
  specification_version: 4
52
61
  summary: Plugins for Roda.
53
- test_files: []
54
- has_rdoc:
62
+ test_files:
63
+ - test/assets_test.rb
64
+ - test/components_test.rb
65
+ - test/dummy/assets/css/app.scss
66
+ - test/dummy/assets/js/head/app.coffee
67
+ - test/dummy/assets/raw.css
68
+ - test/helper.rb
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in roda-plugins.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- require "bundler/gem_tasks"
2
-