theme 0.0.1 → 0.1.0

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: 0baa36cad0a1b4ceba65b9e4383236c3ab8e5dc6
4
- data.tar.gz: a7d338f4e9a495c7ad6112c3f0267e7123f628f3
3
+ metadata.gz: 254228666c499ec52897053c19a69dc1ac3cf10c
4
+ data.tar.gz: ecc08329901e61dcdbbae73a82a1ee87f7c342a2
5
5
  SHA512:
6
- metadata.gz: bfe6f0c7b66208d5b8c9b62e7b60b8c4cd7fed3822dcf73ed50400fb237b342a3272816df000bf3dc61f3e314830495a1a1c5a72ade4fb0c530e956ad1d52f6b
7
- data.tar.gz: 2c6d5c7031478bb338f0cebf358ad98a611ed8d9103dc39a8f5438abdeda5d0e579b8aba1102f3b26f98dadde2509df0288a31b3ba60ee485ff96ed788c4e834
6
+ metadata.gz: bc532e11312b41cbbd2e0aa1bffeab09937543308aa0dddc5c6d56965233cf7ebe859e268be11130e41b7fd1ee409ef7340cc6aef7b5c221e724c0d57f43fa05
7
+ data.tar.gz: 56a8e8f3c276a3848ee5e6973de0edfe1ddeabb5b7d01da8175015fa40a512b9a660b1e8b519ea243a06603ccfbbbd97fdd21b3162103057fdec7a32d33eb161
data/.gems ADDED
@@ -0,0 +1,8 @@
1
+ nokogiri -v 1.6.3.rc3
2
+ nokogiri-styles -v 0.1.2
3
+ cuba -v 3.1.1
4
+ tilt -v 2.0.1
5
+ slim -v 2.0.3
6
+ mab -v 0.0.3
7
+ hashr -v 0.0.22
8
+ cutest-cj -v 1.5.0
data/.gitignore CHANGED
@@ -20,3 +20,5 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ .gs
24
+ .env*
data/Makefile ADDED
@@ -0,0 +1,44 @@
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=development' >> .env; \
20
+ fi;
21
+
22
+ bs gem list dep-cj -i || bs gem install dep-cj
23
+ bs gem list cutest-cj -i || bs 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
+ make clean
@@ -0,0 +1,133 @@
1
+ require 'rack/mime'
2
+ require 'open-uri'
3
+
4
+ module Theme
5
+ module Assets
6
+ class Middleware
7
+ attr_reader :app, :env, :res
8
+
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ def call env
14
+ dup.call! env
15
+ end
16
+
17
+ def call! env
18
+ @env = env
19
+
20
+ if assets_path
21
+ render_assets
22
+ else
23
+ res
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def res
30
+ @res ||= begin
31
+ if not assets_path
32
+ app.call(req.env)
33
+ else
34
+ Cuba::Response.new
35
+ end
36
+ end
37
+ end
38
+
39
+ def req
40
+ @req ||= Rack::Request.new env
41
+ end
42
+
43
+ def assets_path
44
+ path[Regexp.new("^#{Theme.config.asset_url}($|.*)")]
45
+ end
46
+
47
+ def path
48
+ env['PATH_INFO']
49
+ end
50
+
51
+ def type
52
+ if matched = path.match(/(?<=#{Theme.config.asset_url}\/).*(?=\/)/)
53
+ matched[0]
54
+ else
55
+ ''
56
+ end
57
+ end
58
+
59
+ def name
60
+ @name ||= begin
61
+ cleaned = path.gsub(/\.#{ext}$/, '')
62
+ cleaned = cleaned.gsub(/^#{Theme.config.asset_url}\//, '')
63
+ cleaned = cleaned.gsub(/^#{type}\//, '')
64
+ cleaned
65
+ end
66
+ end
67
+
68
+ def ext
69
+ if matched = path.match(/(?<=\.).*$/)
70
+ matched[0]
71
+ else
72
+ false
73
+ end
74
+ end
75
+
76
+ def render_assets
77
+ case type
78
+ when 'css', 'stylesheet', 'stylesheets'
79
+ content_type = 'text/css; charset=utf-8'
80
+ when 'js', 'javascript', 'javascripts'
81
+ content_type = 'text/javascript; charset=utf-8'
82
+ else
83
+ content_type = Rack::Mime.mime_type ext
84
+ end
85
+
86
+ res.headers.merge!({
87
+ "Content-Type" => content_type,
88
+ "Cache-Control" => 'public, max-age=2592000, no-transform',
89
+ 'Connection' => 'keep-alive',
90
+ 'Age' => '25637',
91
+ 'Strict-Transport-Security' => 'max-age=31536000',
92
+ 'Content-Disposition' => 'inline'
93
+ })
94
+
95
+ if name == "all-#{sha}"
96
+ @name = 'dominate-compiled'
97
+ res.write render_single_file
98
+ elsif name == 'all'
99
+ res.write render_all_files
100
+ else
101
+ res.write render_single_file
102
+ end
103
+
104
+ res.finish
105
+ end
106
+
107
+ def render_all_files
108
+ content = ''
109
+ files = Theme.config.assets[ext]
110
+ path = "#{Theme.config.asset_path}/#{type}"
111
+
112
+ files.each do |file|
113
+ if file[/^http/]
114
+ content += open(file).string
115
+ else
116
+ content += Theme.load_file("#{path}/#{file}")
117
+ end
118
+ end
119
+
120
+ content
121
+ end
122
+
123
+ def render_single_file
124
+ path = "#{Theme.config.asset_path}/#{type}"
125
+ Theme.load_file "#{path}/#{name}.#{ext}"
126
+ end
127
+
128
+ def sha
129
+ Thread.current[:_sha] ||= (Theme.config.sha || `git rev-parse HEAD`.strip)
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,45 @@
1
+ module Theme
2
+ module Assets
3
+ module Render
4
+ def self.setup app
5
+ app.settings[:render] ||= {}
6
+ load_engines
7
+ end
8
+
9
+ def view file, options = {}
10
+ path = "#{settings[:render][:views] || Theme.config.view_path}"
11
+ Theme.load_file "#{path}/#{file}", options, self
12
+ end
13
+
14
+ def render file, options = {}
15
+ path = "#{settings[:render][:views] || Theme.config.view_path}"
16
+ layout_path = settings[:layout_path] || Theme.config.layout_path
17
+ layout = "#{layout_path}/#{settings[:render][:layout] || Theme.config.layout}"
18
+ content = Theme.load_file "#{path}/#{file}", options, self
19
+ options[:content] = content
20
+ Theme.load_file layout, options, self
21
+ end
22
+
23
+ def partial file, options = {}
24
+ file.gsub! PARTIAL_REGEX, '_\1'
25
+ path = "#{settings[:render][:views] || Theme.config.view_path}"
26
+ Theme.load_file "#{path}/#{file}", options, self
27
+ end
28
+
29
+ private
30
+
31
+ def self.load_engines
32
+ if defined? Slim
33
+ Slim::Engine.set_default_options \
34
+ disable_escape: true,
35
+ use_html_safe: true,
36
+ disable_capture: false
37
+
38
+ if ENV['RACK_ENV'] == 'development'
39
+ Slim::Engine.set_default_options pretty: true
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,82 @@
1
+ require 'theme/mab'
2
+
3
+ module Theme
4
+ module Assets
5
+ autoload :Middleware, 'theme/assets/middleware'
6
+ autoload :Render, 'theme/assets/render'
7
+
8
+ class << self
9
+ def setup app
10
+ app.plugin Render
11
+ app.use Middleware
12
+ end
13
+
14
+ def css_assets options = {}
15
+ options = {
16
+ 'data-turbolinks-track' => 'true',
17
+ rel: 'stylesheet',
18
+ type: 'text/css',
19
+ media: 'all'
20
+ }.merge options
21
+
22
+ url = Theme.config.asset_url
23
+
24
+ if Theme.config.assets_compiled
25
+ options[:href] = "#{url}/css/all-#{sha}.css"
26
+ else
27
+ options[:href] = "#{url}/css/all.css"
28
+ end
29
+
30
+ mab { link options }
31
+ end
32
+
33
+ def js_assets options = {}
34
+ options = {
35
+ 'data-turbolinks-track' => 'true',
36
+ }.merge options
37
+
38
+ url = Theme.config.asset_url
39
+
40
+ if Theme.config.assets_compiled
41
+ options[:src] = "#{url}/js/all-#{sha}.js"
42
+ else
43
+ options[:src] = "#{url}/js/all.js"
44
+ end
45
+
46
+ mab { script options }
47
+ end
48
+
49
+ def compile
50
+ Theme.config.assets.to_h.each do |type, assets|
51
+ content = ''
52
+
53
+ if assets.length > 0
54
+ type_path = "#{Theme.config.asset_path}/#{Theme.config[:"asset_#{type}_folder"]}"
55
+ assets.each do |file|
56
+ path = "#{type_path}/#{file}"
57
+ content += Theme.load_file path
58
+ end
59
+ tmp_path = "#{type_path}/tmp.dominate-compiled.#{type}"
60
+ File.write tmp_path, content
61
+ system "minify #{tmp_path} > #{type_path}/dominate-compiled.#{type}"
62
+ File.delete tmp_path
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def css_assets options = {}
69
+ Theme::Assets.css_assets options
70
+ end
71
+
72
+ def js_assets options = {}
73
+ Theme::Assets.js_assets options
74
+ end
75
+
76
+ private
77
+
78
+ def sha
79
+ Thread.current[:_sha] ||= (Theme.config.sha || `git rev-parse HEAD`.strip)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,103 @@
1
+ require 'delegate'
2
+ require 'hashr'
3
+
4
+ Hashr.raise_missing_keys = true
5
+
6
+ module Theme
7
+ class Component < SimpleDelegator
8
+ include Theme::Events
9
+
10
+ attr_reader :instance
11
+
12
+ def initialize instance = false
13
+ @instance = instance
14
+ @node = self.class.node.clone if self.class.node
15
+ @name = self.class.name
16
+
17
+ instance.instance_variables.each do |name|
18
+ instance_variable_set name, instance.instance_variable_get(name)
19
+ end
20
+
21
+ super instance
22
+ end
23
+
24
+ class << self
25
+ attr_reader :html, :path, :id
26
+ attr_accessor :node
27
+
28
+ def id name
29
+ Theme.config.components[name] = self.to_s
30
+ @id = name
31
+ end
32
+
33
+ def src path
34
+ if path[/^\./]
35
+ @path = path
36
+ else
37
+ @path = "#{Theme.config.path}/#{path}"
38
+ end
39
+
40
+ @html = Theme.load_file @path
41
+ end
42
+
43
+ def dom location
44
+ node = Theme.cache.dom.fetch(path) do
45
+ n = Nokogiri::HTML html
46
+ Theme.cache.dom[path] = n
47
+ end
48
+
49
+ if location.is_a? String
50
+ @node = node.at location
51
+ else
52
+ @node = node.send location.keys.first, location.values.last
53
+ end
54
+ end
55
+
56
+ def clean &block
57
+ block.call node
58
+ end
59
+ alias :setup :clean
60
+ end
61
+
62
+ attr_accessor :node
63
+
64
+ def method_missing method, *args, &block
65
+ # respond_to?(symbol, include_all=false)
66
+ if instance.respond_to? method, true
67
+ instance.send method, *args, &block
68
+ else
69
+ super
70
+ end
71
+ end
72
+
73
+ def node
74
+ @node ||= self.class.node.clone
75
+ end
76
+
77
+ def render meth = 'display', options = {}
78
+ if method(meth).parameters.length > 0
79
+ opts = Hashr.new(options)
80
+ resp = send meth, opts
81
+ else
82
+ resp = send meth
83
+ end
84
+
85
+ options.clear
86
+
87
+ if resp.is_a? Nokogiri::XML::Element
88
+ resp.to_html
89
+ else
90
+ resp
91
+ end
92
+ end
93
+
94
+ def set_locals options
95
+ options.to_h.each do |key, value|
96
+ (class << self; self; end).send(:attr_accessor, key.to_sym)
97
+ instance_variable_set("@#{key}", value)
98
+ end
99
+
100
+ self
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,13 @@
1
+ module Theme
2
+ class Event
3
+ include Eventable
4
+
5
+ event :trigger
6
+
7
+ def trigger component_name, component_event, data = {}
8
+ opts = Hashr.new data
9
+ fire_event :trigger, component_name, component_event, opts
10
+ data.clear
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,66 @@
1
+ module Theme
2
+ module Events
3
+ def self.included(other)
4
+ other.extend(Macros)
5
+ end
6
+
7
+ def add_listener(listener)
8
+ (@listeners ||= []) << listener
9
+ end
10
+
11
+ def notify_listeners(event, *args)
12
+ id = self.class.instance_variable_get :@id
13
+
14
+ (@listeners || []).each do |listener|
15
+ if id
16
+ listener.trigger(:"#{id}_#{event}", *args)
17
+ else
18
+ listener.trigger(event, *args)
19
+ end
20
+ end
21
+ end
22
+
23
+ def trigger(name, options = {})
24
+ if self.class._event_blocks && callback = self.class._event_blocks[name]
25
+ if callback.is_a? Proc
26
+ callback.call options
27
+ else
28
+ if method(callback).parameters.length > 0
29
+ send callback, options
30
+ else
31
+ send callback
32
+ end
33
+ end
34
+ end
35
+
36
+ notify_listeners(name, options)
37
+ end
38
+
39
+ module Macros
40
+ attr_accessor :_event_blocks, :_listeners
41
+
42
+ def on_event(name, options = {}, &block)
43
+ if id = options[:for]
44
+ (@_listeners ||= []) << id
45
+ name = :"#{id}_#{name}"
46
+ end
47
+
48
+ @_event_blocks ||= {}
49
+ @_event_blocks[name] = options.fetch(:use) { block }
50
+
51
+ mod = if const_defined?(:Events, false)
52
+ const_get(:Events)
53
+ else
54
+ new_mod = Module.new do
55
+ def self.to_s
56
+ "Events(#{instance_methods(false).join(', ')})"
57
+ end
58
+ end
59
+ const_set(:Events, new_mod)
60
+ end
61
+
62
+ include mod
63
+ end
64
+ end
65
+ end
66
+ end
data/lib/theme/file.rb ADDED
@@ -0,0 +1,52 @@
1
+ module Theme
2
+ module File
3
+ extend self
4
+
5
+ IMAGE_TYPES = %w(png gif jpg jpeg)
6
+ FONT_TYPES = %w(eot woff ttf svg)
7
+ STATIC_TYPES = %w(html js css map)
8
+ VIEW_TYPES = %w(html slim haml erb md markdown mkd mab)
9
+
10
+ def load path, c = {}, instance = self
11
+ cache = Theme.cache.file.fetch(path) {
12
+ template = false
13
+
14
+ ext = path[/\.[^.]*$/][1..-1]
15
+
16
+ if ext && ::File.file?(path)
17
+ if STATIC_TYPES.include? ext
18
+ template = Tilt::PlainTemplate.new nil, 1, outvar: '@_output', default_encoding: 'UTF-8' do |t|
19
+ ::File.read(path)
20
+ end
21
+ elsif FONT_TYPES.include?(ext) || IMAGE_TYPES.include?(ext)
22
+ template = ::File.read path
23
+ else
24
+ template = Tilt.new path, 1, outvar: '@_output'
25
+ end
26
+ else
27
+ VIEW_TYPES.each do |type|
28
+ f = "#{path}.#{type}"
29
+
30
+ if ::File.file? f
31
+ template = Tilt.new f, 1, outvar: '@_output'
32
+ break
33
+ end
34
+ end
35
+ end
36
+
37
+ unless template
38
+ raise Theme::No::FileFound,
39
+ "Could't find file: #{path} with any of these extensions: #{VIEW_TYPES.join(', ')}."
40
+ end
41
+
42
+ template
43
+ }
44
+
45
+ if defined? cache.render
46
+ cache.render instance, c.to_h
47
+ else
48
+ cache.to_s
49
+ end
50
+ end
51
+ end
52
+ end
data/lib/theme/mab.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'mab'
2
+
3
+ def mab(&blk)
4
+ Mab::Builder.new({}, self, &blk).to_s
5
+ end
@@ -0,0 +1,59 @@
1
+ require 'rack/mime'
2
+ require 'open-uri'
3
+
4
+ module Theme
5
+ class Middleware
6
+ attr_reader :app, :env, :res
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call env
13
+ dup.call! env
14
+ end
15
+
16
+ def call! env
17
+ @env = env
18
+
19
+ if component_path
20
+ render_component
21
+ else
22
+ res
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def res
29
+ @res ||= begin
30
+ if not component_path
31
+ app.call(req.env)
32
+ else
33
+ Cuba::Response.new
34
+ end
35
+ end
36
+ end
37
+
38
+ def req
39
+ @req ||= Rack::Request.new env
40
+ end
41
+
42
+ def component_path
43
+ path[Regexp.new("^#{Theme.config.component_url}($|.*)")]
44
+ end
45
+
46
+ def path
47
+ env['PATH_INFO']
48
+ end
49
+
50
+ def render_component
51
+ app.instance_variable_set :@res, res
52
+ app.instance_variable_set :@req, req
53
+ name = req.params['component_name'].to_sym
54
+ event = req.params['component_event'].to_sym
55
+ app.theme_components[name].trigger event, Hashr.new(req.params)
56
+ res.finish
57
+ end
58
+ end
59
+ end
data/lib/theme/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Theme
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end