tight-engine 0.0.1 → 0.0.2

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: 065ac37dc49f5b5e67ec4271bb92f71eee632e0f
4
- data.tar.gz: 72dfcdf5eae3c379407c7c698293515e59822db8
3
+ metadata.gz: d9af08abd886546c991e706361e0a40872e0e6ca
4
+ data.tar.gz: b9e8bb833bf96cfbc027ff4f446c41b085930b1c
5
5
  SHA512:
6
- metadata.gz: 50b1e579488cad7dc14f878f8e2605e8d1837500de6e980aa175f14839693e7027d5df4dd078ef2d86a085e1714ee0f94988976cb39ba56b547dfad6a8b00ea9
7
- data.tar.gz: 0bc29fa1374faeb73b5949bc6f504f613fad53d39eac471350c69dfae7c6a78ae94e5429a6a927f3aba1eb4c6bb6255791c3112cd72c175f05f231448a13d3eb
6
+ metadata.gz: da17884b6953ce5babcb31c05a577f114c56f16ae41dde952840a849c0d6f70ea297d92c33025da579b3a491ded507a523d1d810a827cb66b3db962da1280aab
7
+ data.tar.gz: 1e6484ce7ec08c592a25c9fb9575f853b8826986ca8868883ad3eab2a94d4a95d639bed33d8f7f024ef47573aa8c60b3ff649261ad85cf2120f790457c49d618
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  *.rbc
2
2
  *.sassc
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ gemspec
@@ -0,0 +1,23 @@
1
+ require 'tight-engine/init'
2
+ require 'tight-engine/locale'
3
+ require 'tight-engine/defer'
4
+ require 'tight-engine/template'
5
+ require 'tight-engine/render'
6
+ require 'tight-engine/url'
7
+ require 'tight-engine/utils'
8
+
9
+ module Tight
10
+ module Engine
11
+ def self.registered(app)
12
+ app.send(:include, Tight::Engine::Init)
13
+ app.send(:include, Tight::Engine::Locale)
14
+ app.send(:include, Tight::Engine::Defer)
15
+ app.send(:include, Tight::Engine::Template)
16
+ app.send(:include, Tight::Engine::Render)
17
+ app.send(:include, Tight::Engine::Url)
18
+ app.send(:include, Tight::Engine::Utils)
19
+
20
+ app.default :deferred_elements, []
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ module Tight
2
+ module Engine
3
+ module Defer
4
+ def placeholders
5
+ @placeholders ||= {}
6
+ end
7
+
8
+ def defer_element( name, args, opts )
9
+ return unless settings.deferred_elements.include?(name)
10
+ placeholders[name] = [ name, args, opts ]
11
+ "%{placeholder[:#{name}]}"
12
+ end
13
+
14
+ def process_deferred_elements( text )
15
+ text.to_str.gsub /(\s*)\%\{placeholder\[\:([^\]]+)\]\}/ do
16
+ if deferred = placeholders[$2]
17
+ output = process_element *deferred
18
+ if engine.pretty?
19
+ $1+output.gsub(/\r|\n|\r\n/, $1)+$1
20
+ else
21
+ output
22
+ end
23
+ else
24
+ ''
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,120 @@
1
+ require 'ostruct'
2
+
3
+ module Tight
4
+ module Engine
5
+ module Init
6
+ def engine
7
+ @tight_env ||= OpenStruct.new
8
+ end
9
+
10
+ def init_page
11
+ path = normalize_path swift.path
12
+ page = find_page_or_module path
13
+ redirect_if_blank page
14
+ merge_default_params page
15
+ init_path_pages page
16
+ @page = page
17
+ end
18
+
19
+ def init_error( errno )
20
+ init_path_pages
21
+ @page = Page.first :path => "/error/#{errno}"
22
+ @page ||= Page.new :title => "Error #{errno}", :text => "page /error/#{errno} not found"
23
+ end
24
+
25
+ def init_swift
26
+ return if @_inited
27
+ init_folders
28
+ init_media
29
+ init_locale
30
+ init_http
31
+ @_inited = true
32
+ end
33
+
34
+ private
35
+
36
+ def find_page_or_module( path )
37
+ page = Page.first( :conditions => [ "? LIKE IF(is_module,CONCAT(path,'%'),path)", path ], :order => :path.desc )
38
+ detect_module_slug page, path
39
+ end
40
+
41
+ def normalize_path( path )
42
+ path.squeeze! '/'
43
+ path[1] ? path.chomp('/') : path
44
+ end
45
+
46
+ def redirect_if_blank( page )
47
+ if page && page.parent_id && page.fragment_id == 'page' && page.text.blank? && !page.is_module
48
+ first_child = page.children.first( :order => :position )
49
+ redirect first_child.path if first_child
50
+ end
51
+ end
52
+
53
+ def merge_default_params( page )
54
+ params.reverse_merge! Rack::Utils.parse_query(page.params) if page && page.params.present?
55
+ end
56
+
57
+ def detect_module_slug( page, path )
58
+ if page && page.is_module
59
+ swift.module_root = page.path
60
+ swift.slug = case path[page.path.length]
61
+ when '/'
62
+ path[(page.path.length+1)..-1]
63
+ when nil
64
+ ''
65
+ else
66
+ page = nil
67
+ end
68
+ end
69
+ page
70
+ end
71
+
72
+ def init_path_pages( page = Page.root )
73
+ while page
74
+ swift.path_pages.unshift page
75
+ swift.path_ids.unshift page.id
76
+ page = page.parent
77
+ end
78
+ end
79
+
80
+ def init_http
81
+ swift.http_method = request.env['REQUEST_METHOD']
82
+ swift.host = request.env['SERVER_NAME']
83
+ swift.request = request.env['REQUEST_URI']
84
+ swift.uri = "/#{params[:request_uri]}"
85
+ swift.path = swift.uri.partition('?').first
86
+ swift.path_pages = []
87
+ swift.path_ids = []
88
+ end
89
+
90
+ def init_folders
91
+ swift.root = Swift::Application.root
92
+ swift.public = Swift::Application.public_folder
93
+ swift.views = Swift::Application.views
94
+ end
95
+
96
+ def init_media
97
+ media = params.has_key?('print') ? 'print' : 'screen'
98
+ swift.send("#{media}?=", media)
99
+ swift.media = media
100
+ swift.send("pretty?=", Padrino.env == :development)
101
+ end
102
+
103
+ def init_locale
104
+ swift.locales = Option(:locales) || %w(ru en)
105
+ swift.locale = params[:locale] ? detect_selected_locale : detect_session_locale
106
+ session[:locale] = swift.locale
107
+ I18n.available_locales = swift.locales
108
+ I18n.locale = swift.locale
109
+ end
110
+
111
+ public
112
+
113
+ # deprecated
114
+ def swift
115
+ warn '#swift method is deprecated'
116
+ @tight_env ||= OpenStruct.new
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,40 @@
1
+ module Tight
2
+ module Engine
3
+ module Locale
4
+ private
5
+
6
+ def detect_selected_locale
7
+ selected_locale = params[:locale].to_s[0..1]
8
+ if swift.locales.include?(selected_locale)
9
+ selected_locale.to_sym
10
+ else
11
+ detect_session_locale
12
+ end
13
+ end
14
+
15
+ def detect_session_locale
16
+ if session[:locale].present? && swift.locales.include?(session[:locale].to_s)
17
+ session[:locale].to_sym
18
+ else
19
+ detect_preferred_locale
20
+ end
21
+ end
22
+
23
+ def detect_preferred_locale
24
+ detected_locale = if preferred_languages = request.env['HTTP_ACCEPT_LANGUAGE']
25
+ (parse_http_accept_language(preferred_languages) & swift.locales).first
26
+ end
27
+ detected_locale ||= swift.locales.first
28
+ detected_locale.to_sym
29
+ end
30
+
31
+ def parse_http_accept_language( languages )
32
+ languages.gsub(/\s+/,'').split(/,/)
33
+ .sort_by{ |tags| -(tags.partition(/;/).last.split(/=/)[1]||1).to_f }
34
+ .map{ |language| language[0..1] }.uniq
35
+ rescue # !!! FIXME detect valid Exceptions
36
+ []
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,193 @@
1
+ module Tight
2
+ module Engine
3
+ module Render
4
+ MAX_PARSE_LEVEL = 4
5
+
6
+ REGEX_RECURSIVE_BRACKETS = /
7
+ (?<re> #
8
+ \[ #
9
+ (?: #
10
+ (?>[^\[\]]+) # 1
11
+ | #
12
+ \g<re> #
13
+ )* #
14
+ \] #
15
+ ) #
16
+ /x.freeze
17
+
18
+ def strip_code( text )
19
+ text && text.gsub(REGEX_RECURSIVE_BRACKETS, '').strip
20
+ end
21
+
22
+ def engine_render( text )
23
+ Markdown.render( parse_content( text.to_s ) ).html_safe
24
+ end
25
+
26
+ def inline_render( text )
27
+ engine_render( text ).gsub(/^<p>(.*)<\/p>$/, '\1').chomp
28
+ end
29
+
30
+ private
31
+
32
+ def parse_content( text )
33
+ limit_recursion do |flags|
34
+ draft = text.gsub(REGEX_RECURSIVE_BRACKETS) do |tag|
35
+ internal_tag(tag) || external_tag(tag, flags)
36
+ end
37
+ capture_tag_content draft, flags
38
+ end
39
+ end
40
+
41
+ INTERNAL_TAGS = {
42
+ 'page' => 'PageLink',
43
+ 'link' => 'PageLink',
44
+ 'block' => 'Block',
45
+ 'table' => 'Block',
46
+ 'image' => 'Image',
47
+ 'img' => 'Image',
48
+ 'file' => 'File',
49
+ 'asset' => 'File',
50
+ 'element' => :self,
51
+ }.freeze
52
+
53
+ def dispatch_element( tag_name, args, opts )
54
+ element_name = INTERNAL_TAGS[tag_name] or return
55
+ element_name = args.shift if element_name == :self
56
+ process_element element_name, args, opts
57
+ end
58
+
59
+ REGEX_INTERNAL_TAG = /
60
+ \[ # [
61
+ (#{INTERNAL_TAGS.keys.join('|')}) # 1, -- tag name
62
+ ( # 2, -- identity
63
+ (?:[\:\.\#][\w\-]*)* #
64
+ ) #
65
+ \s+ #
66
+ (.*) # 3, -- arguments
67
+ \] # ]
68
+ /x.freeze
69
+
70
+ def internal_tag( tag )
71
+ data = tag.match(REGEX_INTERNAL_TAG) or return
72
+ tag_name, identity, vars = data[1..-1]
73
+ args, opts = parse_vars vars
74
+ opts[:title] = detect_title( tag_name, args ) if opts[:title].blank?
75
+ detect_identity identity, opts
76
+ dispatch_element tag_name, args, opts
77
+ end
78
+
79
+ def external_tag( tag, flags )
80
+ tag_name, _, vars = tag[1..-2].partition ' '
81
+ code = Code.first( :slug => tag_name ) unless tag_name[0] == '/'
82
+ if code && code.is_single
83
+ args, opts = parse_vars vars
84
+ parse_code( code.html, args )
85
+ else
86
+ flags[:needs_capturing] = true if code
87
+ tag
88
+ end
89
+ end
90
+
91
+ REGEX_IDENTITY = /
92
+ [\:\.\#] # prefix :.#
93
+ [\w\-]* # identity name
94
+ /x.freeze
95
+
96
+ def detect_identity( identity, opts )
97
+ identity.to_s.scan(REGEX_IDENTITY).each do |attr|
98
+ prefix, name = attr[0], attr[1..-1]
99
+ case prefix
100
+ when '#'
101
+ opts[:id] ||= name
102
+ when '.'
103
+ if opts[:class].blank?
104
+ opts[:class] = name
105
+ else
106
+ opts[:class] << ' ' << name
107
+ end
108
+ when ':'
109
+ opts[:instance] = name
110
+ end
111
+ end
112
+ end
113
+
114
+ def detect_title( type, args )
115
+ newtitle = if type == 'element'
116
+ args[2..-1]||[]
117
+ else
118
+ args[1..-1]||[]
119
+ end.join(' ').strip
120
+ parse_content(newtitle) if newtitle.present?
121
+ end
122
+
123
+ def capture_tag_content( str, flags )
124
+ return str unless flags[:needs_capturing]
125
+ str.gsub( /\[([^\s]*)\s*(.*?)\](.*?)\[\/(\1)\]/m ) do |s|
126
+ args, _ = parse_vars $2
127
+ code = Code.by_slug $1 #!!! FIXME database call
128
+ parse_code code.html, args, $3
129
+ end
130
+ end
131
+
132
+ def limit_recursion
133
+ @parse_level ||= 0
134
+ @parse_level += 1
135
+ raise SystemStackError, 'parse level too deep' if @parse_level > MAX_PARSE_LEVEL
136
+ result = yield({})
137
+ @parse_level -= 1
138
+ result
139
+ end
140
+
141
+ REGEX_VARS = /
142
+ ( # 0
143
+ ([\S^,]+)\:\s* # 1
144
+ (?: #
145
+ ["']([^"']+)["'] | # 2
146
+ ([^,'"\s]+) # 3 "'
147
+ ) #
148
+ ),? | #
149
+ ( # 4
150
+ ([^'"\s]+) | # 5 "'
151
+ ["']([^"']+)["'],? # 6
152
+ )
153
+ /x.freeze
154
+
155
+ def parse_vars( vars )
156
+ args = []
157
+ opts = {}
158
+ vars.scan(REGEX_VARS).each do |v|
159
+ opts[v[1].to_sym] = ( v[2] || v[3] ) if v[0]
160
+ args << ( v[5] || v[6] ) if v[4]
161
+ end
162
+ [args, opts]
163
+ end
164
+
165
+ REGEX_EXTERNAL_TAG = /
166
+ \[ #
167
+ (\d+) # 1
168
+ (?: #
169
+ \: #
170
+ (.*?) # 2
171
+ )? #
172
+ \] #
173
+ | #
174
+ \[ #
175
+ (content) # 3
176
+ \] #
177
+ /x.freeze
178
+
179
+ def parse_code( html, args, content = '' )
180
+ html.gsub(REGEX_EXTERNAL_TAG) do |s|
181
+ idx = $1.to_i
182
+ if idx > 0
183
+ (args[idx-1] || $2).to_s
184
+ elsif $3 == 'content'
185
+ parse_content content
186
+ else
187
+ "[#{tag}]"
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,60 @@
1
+ module Tight
2
+ module Engine
3
+ module Template
4
+ def process_page
5
+ process_deferred_elements fragment( @page.fragment_id, :layout => :"layouts/#{@page.layout_id}" )
6
+ end
7
+
8
+ def element( name, *args )
9
+ opts = Hash === args.last ? args.pop : {}
10
+ defer_element( name, args, opts ) || process_element( name, args, opts )
11
+ end
12
+
13
+ def element_view( name, opts = {} )
14
+ fragment name, :elements, opts
15
+ end
16
+
17
+ def fragment( template, type = nil, opts = {} )
18
+ opts, type = type, nil if type.kind_of? Hash
19
+ opts[:layout] ||= false
20
+ type ||= :fragments
21
+ render :slim, :"#{type}/#{template}", opts
22
+ rescue Padrino::Rendering::TemplateNotFound, Errno::ENOENT => e
23
+ name = template.partition('/').first
24
+ report_error e, "EngineHelpers##{__method__}@#{__LINE__}", "[#{type.to_s.singularize.camelize} '#{name}' error: #{e.to_s.strip}]"
25
+ end
26
+
27
+ private
28
+
29
+ def process_element( name, args, opts )
30
+ core, view = find_element name, opts[:instance]
31
+ fill_identity name, opts
32
+ catch :output do
33
+ @args, @opts = args, opts
34
+ binding.eval File.read(core), core if File.exists?(core)
35
+ render :slim, view.to_sym, :layout => false, :views => Swift::Application.views
36
+ end
37
+ rescue Padrino::Rendering::TemplateNotFound => e
38
+ report_error e, "EngineHelpers##{__method__}@#{__LINE__}", "[Element '#{name}' error: #{e.strip}]"
39
+ rescue Exception => e
40
+ report_error e, "EngineHelpers##{__method__}@#{__LINE__} '#{name}'"
41
+ end
42
+
43
+ def find_element( name, instance = nil )
44
+ view = "elements/#{name}/view"
45
+ if instance
46
+ instance_view = "#{view}-#{instance}"
47
+ view = instance_view if File.file?( "#{Swift::Application.views}/#{instance_view}.slim" )
48
+ end
49
+ [ "#{Swift::Application.views}/elements/#{name}/core.rb", view ]
50
+ end
51
+
52
+ def fill_identity( element_name, opts = {} )
53
+ @identity = { :class => element_name.dup }
54
+ @identity[:id] = opts[:id] if opts[:id]
55
+ @identity[:class] << " #{opts[:class]}" if opts[:class]
56
+ @identity[:class] << " #{element_name}-#{opts[:instance]}" if opts[:instance]
57
+ end
58
+ end
59
+ end
60
+ end