grass 0.0.1

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.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.sass-cache/e3d4c2039fc7a8446e752aad5ac08f85d7457f92/(__TEMPLATE__)c +0 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +29 -0
  8. data/Rakefile +10 -0
  9. data/bin/grass +89 -0
  10. data/config/grass.rb +27 -0
  11. data/db/migrate/1_create_grass_sources.rb +24 -0
  12. data/grass.gemspec +43 -0
  13. data/lib/grass.rb +68 -0
  14. data/lib/grass/cache.rb +52 -0
  15. data/lib/grass/core_ext/kernel.rb +8 -0
  16. data/lib/grass/endpoints/api.rb +70 -0
  17. data/lib/grass/endpoints/front.rb +122 -0
  18. data/lib/grass/file_sync.rb +70 -0
  19. data/lib/grass/goliath/rack/auth_barrier.rb +109 -0
  20. data/lib/grass/goliath/rack/cache.rb +37 -0
  21. data/lib/grass/goliath/rack/cors.rb +45 -0
  22. data/lib/grass/goliath/rack/secure_headers.rb +20 -0
  23. data/lib/grass/goliath/rack/validator.rb +52 -0
  24. data/lib/grass/helpers/i18n_helper.rb +91 -0
  25. data/lib/grass/helpers/render_helper.rb +35 -0
  26. data/lib/grass/key.rb +137 -0
  27. data/lib/grass/render.rb +27 -0
  28. data/lib/grass/render/layout.rb +11 -0
  29. data/lib/grass/render/page.rb +31 -0
  30. data/lib/grass/render/renderer.rb +35 -0
  31. data/lib/grass/render/script.rb +27 -0
  32. data/lib/grass/render/stylesheet.rb +13 -0
  33. data/lib/grass/render/text.rb +11 -0
  34. data/lib/grass/render/view.rb +34 -0
  35. data/lib/grass/render/yui_renderer.rb +27 -0
  36. data/lib/grass/source.rb +107 -0
  37. data/lib/grass/tasks/db.rake +67 -0
  38. data/lib/grass/version.rb +3 -0
  39. data/lib/templates/app/Gemfile +9 -0
  40. data/lib/templates/app/Procfile +3 -0
  41. data/lib/templates/app/Rakefile +7 -0
  42. data/lib/templates/app/app/assets/scripts/application.en.js.coffee +1 -0
  43. data/lib/templates/app/app/assets/stylesheets/application.en.css.scss +4 -0
  44. data/lib/templates/app/app/content/pages/about.en.html.erb +3 -0
  45. data/lib/templates/app/app/content/pages/index.en.md.erb +5 -0
  46. data/lib/templates/app/app/views/layouts/application.en.html.erb +14 -0
  47. data/lib/templates/app/app/views/pages/show.en.html.erb +4 -0
  48. data/lib/templates/app/app/views/shared.en.html.erb +9 -0
  49. data/lib/templates/app/config/cache.yml +20 -0
  50. data/lib/templates/app/config/database.yml +35 -0
  51. data/lib/templates/app/config/grass.rb +27 -0
  52. data/lib/templates/app/db/migrate/1_create_grass_sources.rb +24 -0
  53. data/lib/templates/app/haproxy.cfg +43 -0
  54. data/lib/templates/app/public/favicon.ico +0 -0
  55. data/lib/templates/app/public/robots.txt +2 -0
  56. data/lib/templates/app/server.rb +7 -0
  57. data/test/dummy/app/content/texts/testapi.en.txt +1 -0
  58. data/test/dummy/config/cache.yml +23 -0
  59. data/test/dummy/config/database.yml +35 -0
  60. data/test/dummy/config/dummy.rb +1 -0
  61. data/test/dummy/config/haproxy.cfg +37 -0
  62. data/test/dummy/public/favicon.ico +0 -0
  63. data/test/dummy/public/robots.txt +2 -0
  64. data/test/minitest_helper.rb +38 -0
  65. data/test/support/grass.rb +21 -0
  66. data/test/support/test.jpg +0 -0
  67. data/test/test_api.rb +74 -0
  68. data/test/test_front.rb +52 -0
  69. data/test/test_key.rb +118 -0
  70. data/test/test_source.rb +51 -0
  71. data/test/test_source_file.rb +47 -0
  72. data/test/test_source_render.rb +54 -0
  73. data/vendor/yuicompressor-2.4.8.jar +0 -0
  74. metadata +399 -0
@@ -0,0 +1,91 @@
1
+ require "i18n"
2
+ require "ip_country"
3
+
4
+ module Grass
5
+ module Helpers
6
+ module I18nHelper
7
+
8
+ LNG_EXP_PATH = /^\/[a-z]{2}\-[A-Z]{2}\/|^\/[a-z]{2}\-[A-Z]{2}$|^\/[a-z]{2}\/|^\/[a-z]{2}$/
9
+ LNG_EXP_HTTP = /[a-z]{2}\-[A-Z]{2}|[a-z]{2}/
10
+ DEV_IP = ENV['DEV_IP'] || "86.185.186.43"
11
+
12
+ module_function
13
+
14
+ # Hash to collect all language related info
15
+ def language_info
16
+ { current: I18n.locale, path: path_locale,
17
+ browser: browser_locale, country: country_locales
18
+ }
19
+ end
20
+
21
+ # ======================
22
+ # Language Detection
23
+ # ======================
24
+
25
+ # Set current locale
26
+ def set_locale
27
+ I18n.locale = locale
28
+ end
29
+
30
+ # Set locale by precedence path, browser, country, default
31
+ def locale
32
+ return path_locale if !path_locale.nil? && I18n.available_locales.include?(path_locale)
33
+
34
+ country_locales.each do |country_locale|
35
+ return country_locale if I18n.available_locales.include? country_locale
36
+ end
37
+
38
+ country_locales.each do |country_locale|
39
+ country_locale = country_locale.split("-").first.to_sym
40
+ return country_locale if I18n.available_locales.include? country_locale
41
+ end
42
+
43
+ return browser_locale if I18n.available_locales.include? browser_locale
44
+
45
+ I18n.default_locale
46
+ end
47
+
48
+ # Extract locale from request path
49
+ def path_locale
50
+ if path_locale = env['REQUEST_PATH'].scan(LNG_EXP_PATH).first
51
+ path_locale.gsub("/","").to_sym
52
+ end
53
+ end
54
+
55
+ # Extract locale from accept language header
56
+ def browser_locale
57
+ env['HTTP_ACCEPT_LANGUAGE'].scan(LNG_EXP_HTTP).first.to_sym unless env['HTTP_ACCEPT_LANGUAGE'].nil?
58
+ end
59
+
60
+ # Get all spoken languages in a country sorted by speakers count
61
+ def country_locales
62
+ country_info ? country_info[:languages].split(",") : []
63
+ end
64
+
65
+ # ======================
66
+ # Ip to Country
67
+ # ======================
68
+
69
+ # Detailed country info
70
+ def country_info
71
+ begin
72
+ config['ip_country'].info(remote_ip)
73
+ rescue
74
+ nil
75
+ end
76
+ end
77
+
78
+ # Client's IP4 Address
79
+ # Stolen from Rack::Request
80
+ def remote_ip
81
+ return (ENV["REMOTE_IP"]||=DEV_IP) if Goliat.env != :production
82
+ if addr = env['HTTP_X_FORWARDED_FOR']
83
+ (addr.split(',').grep(/\d\./).first || env['REMOTE_ADDR']).to_s.strip
84
+ else
85
+ env['REMOTE_ADDR']
86
+ end
87
+ end
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,35 @@
1
+ require 'grass/source'
2
+
3
+ module Grass
4
+ module Helpers
5
+
6
+ module RenderHelper
7
+
8
+ def include_partial key
9
+ source = Source[key].first || init_source_if_file_exists(key)
10
+ source.try(:raw)
11
+ end
12
+
13
+ def render_partial key, data = nil
14
+ data ||= @data
15
+ source = Source[key].first || init_source_if_file_exists(key)
16
+ source.try(:render,data)
17
+ end
18
+
19
+ def render_content
20
+ @data[:source].try(:render,@data)
21
+ end
22
+
23
+ private
24
+
25
+ def init_source_if_file_exists key
26
+ key = Grass::Key.new(id: key)
27
+ return unless File.exists?(key.filepath)
28
+ source = Grass::Source.find_or_create_by!(filepath: key.filepath)
29
+ source.raw = File.read(source.filepath)
30
+ source.commit!
31
+ source
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,137 @@
1
+ require 'active_model'
2
+ require 'active_support/core_ext/object/try'
3
+
4
+ module Grass
5
+
6
+ ##
7
+ # Key parses path info into template meta data
8
+
9
+ class Key
10
+
11
+ include ActiveModel::Model
12
+ include ActiveModel::Validations
13
+ include ActiveModel::Validations::Callbacks
14
+
15
+ KEY_REGEX = {
16
+ locale: /\/([a-z]{2}|[a-z]{2}\-[A-Z]{2})\//,
17
+ dir: /\/(views\/layouts|views)\/|\/(scripts|stylesheets|images|fonts|statics)\/|\/(pages|texts)\//,
18
+ path: /\/([\w|\-|\/]+)/,
19
+ ext: /\.(:?[a-z]{1,})/
20
+ }
21
+ FILE_REGEX = {
22
+ # dir: /\/(views\/layouts|views)\/|\/assets\/(scripts|stylesheets|statics)\/|\/content\/(pages|texts)\//,
23
+ dir: /\/(views\/layouts|views)\/|\/(scripts|stylesheets|images|fonts|statics)\/|\/(pages|texts)\//,
24
+ path: /\/([\w|\-|\/]+)/,
25
+ locale: /\.([a-z]{2}|[a-z]{2}\-[A-Z]{2})\./,
26
+ ext: /\.(:?[a-z]{1,})/
27
+ }
28
+
29
+ ATTR_TYPES = [:id, :dir, :path, :locale, :format, :handler, :filepath]
30
+
31
+ FORMATS = {
32
+ 'texts' => 'txt',
33
+ 'views' => 'html',
34
+ 'views/layouts' => 'html',
35
+ 'pages' => 'html',
36
+ 'scripts' => 'js',
37
+ 'stylesheets' => 'css'
38
+ }.freeze
39
+
40
+ attr_accessor *ATTR_TYPES
41
+
42
+ validates_presence_of :id,:dir,:path,:locale,:filepath
43
+
44
+ # before_validation :parse
45
+
46
+ def initialize attributes = {}
47
+ super(attributes)
48
+ parse()
49
+ end
50
+
51
+ def serializable_hash
52
+ {id: self.id, dir: dir, path: path, locale: locale, format: format, handler: handler, filepath: filepath}
53
+ end
54
+
55
+ def to_s
56
+ self.id
57
+ end
58
+
59
+ def fullpath
60
+ @fullpath ||= @dir =~ /pages/ ? "/#{@locale}/#{@dir}/#{@path}" : "/#{@locale}/#{@dir}/#{@path}.#{@format}"
61
+ end
62
+
63
+ private
64
+
65
+ ##
66
+ # parses file or uri path to meta data
67
+
68
+ def parse
69
+ # we need one of them at least
70
+ return nil if !@id.present? && !@filepath.present?
71
+ # get key from filepath if needs
72
+ @id.nil? ? filepath_to_id : id_to_filepath
73
+ # set default locale if null
74
+ @locale ||= I18n.locale.to_s
75
+ # cleanup blanks
76
+ @handler = nil if @handler.blank?
77
+ # set default format
78
+ @format ||= FORMATS[@dir]
79
+ # rebuild key
80
+ @id = ["",@dir,@path].compact.join("/")
81
+ # rebuild filepath
82
+ dir = (is_asset? ? "assets/#{@dir}" : (is_content? ? "content/#{@dir}" : @dir))
83
+
84
+ @filepath = Grass.app_root + ["",dir,@path].compact.join("/") + ["",@locale,@format,@handler].compact.join(".")
85
+
86
+ end
87
+
88
+ ##
89
+ # parsing based on uri path
90
+
91
+ def id_to_filepath
92
+ # parse attrs
93
+ KEY_REGEX.each do |attr,regex|
94
+ if match = @id.scan(regex).try(:flatten).try(:compact)
95
+ if attr == :ext
96
+ @format = match.shift
97
+ @handler = match.join(".")
98
+ else
99
+ self.instance_variable_set "@#{attr}", match=match[0]
100
+ @id.gsub!(/\/#{match}/,"") unless match.nil?
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ ##
107
+ # parsing based on file path
108
+
109
+ def filepath_to_id
110
+ @filepath = @filepath[@filepath.index(FILE_REGEX[:dir])..-1]
111
+ FILE_REGEX.each do |attr,regex|
112
+ if match = @filepath.scan(regex).try(:flatten).try(:compact)
113
+ if attr == :ext
114
+ @format = match.shift
115
+ @handler = match.join(".")
116
+ else
117
+ self.instance_variable_set "@#{attr}", match=match[0]
118
+ unless match.nil?
119
+ attr == :locale ? @filepath.gsub!(/\.#{match}/,"") : @filepath.gsub!(/\/#{match}/,"")
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+
127
+ def is_asset?
128
+ @dir =~ /scripts|stylesheets|images|fonts|statics/
129
+ end
130
+
131
+ def is_content?
132
+ @dir =~ /pages|texts/
133
+ end
134
+
135
+
136
+ end
137
+ end
@@ -0,0 +1,27 @@
1
+ require 'grass/render/text'
2
+ require 'grass/render/view'
3
+ require 'grass/render/layout'
4
+ require 'grass/render/page'
5
+ require 'grass/render/stylesheet'
6
+ require 'grass/render/script'
7
+
8
+ module Grass
9
+
10
+ module Render
11
+
12
+ def render data = {}
13
+ @data = data
14
+ result = "grass/render/#{self.type}".classify.constantize.new(self,@data).render
15
+ commit! result if self.type == "page"
16
+ result
17
+ end
18
+
19
+ def commit! result = nil
20
+ self.result = result || (self.type =~ /script|stylesheet/ ? self.render : self.raw)
21
+ self.save!
22
+ self
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,11 @@
1
+ require 'grass/render/renderer'
2
+
3
+ module Grass
4
+ module Render
5
+ class Layout
6
+
7
+ include Renderer
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ require 'grass/render/renderer'
2
+ require 'grass/render/view'
3
+ require 'tilt/redcarpet'
4
+
5
+ module Grass
6
+ module Render
7
+ class Page
8
+
9
+ include Renderer
10
+
11
+ def render
12
+ if @data[:view].nil? && !self.view.nil?
13
+ @data[:view] = @view
14
+ @data[:source] = @source
15
+ @view.render @data
16
+ else
17
+ super
18
+ end
19
+ end
20
+
21
+ def view
22
+ @view ||= begin
23
+ keys = ["/views/#{@source.dir}/#{@source.path}","/views/#{@source.dir}/#{Grass::Render::View::DEFAULT_VIEW}"]
24
+ Source[keys].first
25
+ # keys.map{|id| Source.find_by(key: Key.new(id: id))}.compact.first
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ require 'tilt'
2
+ require 'tilt/plain'
3
+ require 'tilt/string'
4
+ require 'tilt/erb'
5
+ require 'grass/helpers/render_helper'
6
+
7
+ Tilt.register Tilt::ERBTemplate, 'html'
8
+ Tilt.register Tilt::StringTemplate, 'txt'
9
+
10
+ module Grass
11
+ module Render
12
+ module Renderer
13
+
14
+ include Grass::Helpers::RenderHelper
15
+
16
+ def initialize source, data = {}
17
+ @source = source
18
+ @data = data
19
+ end
20
+
21
+ def render
22
+ result = @source.raw
23
+ templates.each do |template|
24
+ result = template.new{result}.render(self,@data)
25
+ end
26
+ result
27
+ end
28
+
29
+ def templates
30
+ @templates ||= Tilt.templates_for(@source.handler || @source.format)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ require 'grass/render/renderer'
2
+ require 'tilt/coffee'
3
+ require 'uglifier'
4
+
5
+ module Grass
6
+ module Render
7
+ class Script
8
+
9
+ JS = Uglifier.new
10
+
11
+ include Renderer
12
+
13
+ def render
14
+ compress(super)
15
+ end
16
+
17
+ def compress source
18
+ compressor.compress(source)
19
+ end
20
+
21
+ def compressor
22
+ JS
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'grass/render/yui_renderer'
2
+ require 'tilt/sass'
3
+
4
+ module Grass
5
+ module Render
6
+ class Stylesheet
7
+
8
+ include YuiRenderer
9
+ def compressor; CSS end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require 'grass/render/renderer'
2
+
3
+ module Grass
4
+ module Render
5
+ class Text
6
+
7
+ include Renderer
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ require 'grass/render/renderer'
2
+
3
+ module Grass
4
+ module Render
5
+ class View
6
+
7
+ DEFAULT_LAYOUT = "application"
8
+ DEFAULT_VIEW = "show"
9
+
10
+ include Renderer
11
+
12
+ def render
13
+ if @data[:layout].nil? && !self.layout.nil?
14
+ @data[:layout] = @layout
15
+ templates.last.new{ @layout.raw }.render(self,@data) { super }
16
+ else
17
+ super
18
+ end
19
+ end
20
+
21
+ def layout
22
+ @layout ||= begin
23
+ keys = ["/views/layouts/#{DEFAULT_LAYOUT}"]
24
+ unless @data[:source].nil?
25
+ keys.unshift "/views/layouts/#{@data[:source].dir}/#{@data[:source].path}",
26
+ "/views/layouts/#{@data[:source].dir}/#{DEFAULT_VIEW}"
27
+ end
28
+ Source[keys].first
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end