theme 0.0.1 → 0.1.0

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.
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