manatee 0.0.1.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +23 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.mdown +55 -0
  5. data/Rakefile +7 -0
  6. data/app/assets/javascripts/manatee.js +1 -0
  7. data/app/assets/javascripts/manatee/helpers.js +4 -0
  8. data/app/assets/javascripts/manatee/helpers/asset_tag.jsh.coffee +138 -0
  9. data/app/assets/javascripts/manatee/helpers/asset_url.jsh.coffee +90 -0
  10. data/app/assets/javascripts/manatee/helpers/csrf.jsh.coffee +5 -0
  11. data/app/assets/javascripts/manatee/helpers/date.jsh.coffee +54 -0
  12. data/app/assets/javascripts/manatee/helpers/debug.jsh.coffee +2 -0
  13. data/app/assets/javascripts/manatee/helpers/form.jsh.coffee +25 -0
  14. data/app/assets/javascripts/manatee/helpers/form_builder.jsh.coffee +24 -0
  15. data/app/assets/javascripts/manatee/helpers/form_options.jsh.coffee +267 -0
  16. data/app/assets/javascripts/manatee/helpers/form_tag.jsh.coffee +204 -0
  17. data/app/assets/javascripts/manatee/helpers/javascript.jsh.coffee +15 -0
  18. data/app/assets/javascripts/manatee/helpers/number.jsh.coffee +58 -0
  19. data/app/assets/javascripts/manatee/helpers/sanitize.jsh.coffee +5 -0
  20. data/app/assets/javascripts/manatee/helpers/tag.jsh.coffee +58 -0
  21. data/app/assets/javascripts/manatee/helpers/text.jsh.coffee +12 -0
  22. data/app/assets/javascripts/manatee/helpers/translation.jsh.coffee +7 -0
  23. data/app/assets/javascripts/manatee/helpers/url.jsh.coffee +36 -0
  24. data/app/assets/javascripts/manatee/helpers/util.jsh.coffee +41 -0
  25. data/app/assets/javascripts/manatee/rails_helpers.js +2 -0
  26. data/app/assets/javascripts/manatee/rails_helpers/routes.js.erb +195 -0
  27. data/app/assets/javascripts/manatee/rails_routes.js +1 -0
  28. data/app/assets/javascripts/manatee/renderer.js.erb +53 -0
  29. data/app/assets/javascripts/manatee_railsless.js +1 -0
  30. data/lib/manatee.rb +87 -0
  31. data/lib/manatee/config.rb +51 -0
  32. data/lib/manatee/handler.rb +45 -0
  33. data/lib/manatee/rails.rb +6 -0
  34. data/lib/manatee/rails/extensions.rb +23 -0
  35. data/lib/manatee/rails/handler.rb +16 -0
  36. data/lib/manatee/rails/hash_visitor.rb +35 -0
  37. data/lib/manatee/rails/helper.rb +9 -0
  38. data/lib/manatee/rails/resolver.rb +70 -0
  39. data/lib/manatee/rails/routes_compiler.rb +34 -0
  40. data/lib/manatee/sprockets.rb +8 -0
  41. data/lib/manatee/sprockets/jsh_processor_2x.rb +32 -0
  42. data/lib/manatee/sprockets/jsh_processor_3x.rb +31 -0
  43. data/lib/manatee/version.rb +3 -0
  44. data/manatee.gemspec +32 -0
  45. data/test/example/renderer.js.erb +8 -0
  46. data/test/example/translations.en.yml +410 -0
  47. data/test/example/views/index.jst.eco +12 -0
  48. data/test/helpers/asset_tag_test.rb +175 -0
  49. data/test/helpers/asset_url_test.rb +349 -0
  50. data/test/helpers/form_options_test.rb +718 -0
  51. data/test/helpers/form_tag_test.rb +387 -0
  52. data/test/helpers/javascript_test.rb +39 -0
  53. data/test/helpers/number_test.rb +111 -0
  54. data/test/helpers/tag_test.rb +71 -0
  55. data/test/support/dom_assertion.rb +49 -0
  56. data/test/support/view_test.rb +91 -0
  57. data/test/test_helper.rb +17 -0
  58. metadata +213 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5b3ae1dccef7459bc5825b9dabd5b4db64da0363
4
+ data.tar.gz: d018454a024148225f218275b15f551a5ec22a01
5
+ SHA512:
6
+ metadata.gz: 4aca549a3f05eda647491d3b56ca2547c83e52cf576a73ff09c9f80f05ef1dfe5964b243d1f978b231e50e221d2cd7ddf66ec1843f0f457cf798b8fc70602af1
7
+ data.tar.gz: 717fd9676054a86710b75e9e25402312adcd9213e384a195888b1f64842939387b40fcfbd3ec46c014fa9ae34c32af371203fb0374fb8082561201f6bcfa9e04
data/Gemfile ADDED
@@ -0,0 +1,23 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in manatee.gemspec
4
+ gemspec
5
+
6
+ gem 'i18n-js', path: '../i18n-js'
7
+
8
+ gem 'sprockets', '~> 2'
9
+ # gem 'sprockets', '~> 3'
10
+
11
+ platform :ruby do
12
+ gem 'therubyracer'
13
+ end
14
+
15
+ platform :jruby do
16
+ gem 'therubyrhyno'
17
+ end
18
+
19
+ group :development, :test do
20
+ gem 'eco'
21
+ gem 'pry'
22
+ gem 'test-unit'
23
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Dalton
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.mdown ADDED
@@ -0,0 +1,55 @@
1
+ # Manatee
2
+
3
+ Javascript Template Render [for Rails]?
4
+
5
+ ## Installation
6
+
7
+ Use only with TheRubyRacer or TheRubyRhyno gem.
8
+
9
+ Other ExecJS adapter like Node.js, doesn't translates Ruby lambdas to Javascript functions, and some have unicode characters issues
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'manatee'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install manatee
24
+
25
+ ## Usage
26
+
27
+ __It's unstable, may change.__
28
+
29
+ ### With Rails
30
+
31
+ Create an initializer file, for example, config/initializers/manatee.rb with:
32
+
33
+ ```ruby
34
+ Manatee.subscribe_on_rails
35
+ # Manatee.config do |config|
36
+ # config.some_conf = 'value'
37
+ # config.lambda_conf{ |c| 'value' }
38
+ # end
39
+ ```
40
+
41
+ If need to change an option, for now, check out at lib/manatee.rb to see all options, I dont want to document that right now.
42
+
43
+ As you see, I'm lazy enough to not explain how to use it... Figure it out by yourself.
44
+
45
+ ### Without Rails
46
+
47
+ Check out test file test/test\_helper.rb, and lib/manatee.rb to see all options and understand how to use it with any app that uses sprockets.
48
+
49
+ ## Contributing
50
+
51
+ 1. Fork it ( https://github.com/akidog/manatee/fork )
52
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
53
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
54
+ 4. Push to the branch (`git push origin my-new-feature`)
55
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :test do
4
+ Dir.glob('test/{helpers}/**/*_test.rb').each do |file|
5
+ load file
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ //= require ./manatee/rails_helpers
@@ -0,0 +1,4 @@
1
+ //= require i18n
2
+ //= require i18n/translations
3
+ //= require ./renderer
4
+ //= require_tree ./helpers
@@ -0,0 +1,138 @@
1
+ helper 'audioTag', (paths...) ->
2
+ options = paths[paths.length - 1]
3
+ if typeof options == 'object'
4
+ paths.pop()
5
+ else
6
+ options = {}
7
+ path_options = {}
8
+ path_options['format'] = options['format']
9
+ options['format'] = undefined
10
+ if paths.length == 1
11
+ options['src'] = @audioPath paths[0], path_options
12
+ @tag 'audio', options
13
+ else
14
+ content = ''
15
+ source_options
16
+ if options['source']
17
+ source_options = options['source']
18
+ options['source'] = undefined
19
+ else
20
+ source_options = {}
21
+ for i, path of paths
22
+ source_options['src'] = @audioPath path, path_options
23
+ content += @tag 'source', source_options
24
+ @contentTag 'audio', content, options
25
+
26
+ helper 'autoDiscoveryLinkTag', (type = 'rss', url = @_context.domain.app, options = {}) ->
27
+ options = @_clone options
28
+ options['href'] = @assetUrl(url || @_context.domain.app)
29
+ options['rel'] ||= 'alternate'
30
+ unless options['type']
31
+ if type == 'rss'
32
+ options['type'] = 'application/rss+xml'
33
+ options['title'] ||= 'RSS'
34
+ if type == 'atom'
35
+ options['type'] = 'application/atom+xml'
36
+ options['title'] ||= 'ATOM'
37
+ options['title'] ||= ''
38
+ @tag 'link', options
39
+
40
+ helper 'faviconLinkTag', (path = 'favicon.ico', options = {}) ->
41
+ options['rel'] ||= 'shortcut icon'
42
+ options['type'] ||= 'image/x-icon'
43
+ options['href'] = path
44
+ @tag 'link', options
45
+
46
+ helper 'imageAlt', (source) ->
47
+ basename = new String(source).substring(source.lastIndexOf('/') + 1);
48
+ basename = basename.substring(0, basename.lastIndexOf(".")) if basename.lastIndexOf(".") != -1
49
+ basename = basename[0..-34] if (/\-[0-9a-z]{32}/ig).test(basename[-33..-1])
50
+ basename = basename.replace(/[-_\s]+/g, ' ')
51
+ basename[0].toUpperCase() + basename.slice(1);
52
+
53
+ handleSizeAttribute = (options) ->
54
+ if typeof options['size'] == 'string'
55
+ size = options['size'].toLowerCase()
56
+ options['size'] = undefined
57
+ if (/\d+X\d+/i).test(size)
58
+ xIndex = size.lastIndexOf('x');
59
+ options['width'] ||= size.substring 0, xIndex
60
+ options['height'] ||= size.substring xIndex+1
61
+ if (new RegExp("\\d{" + size.length + "}")).test(size)
62
+ options['width'] ||= size
63
+ options['height'] ||= size
64
+
65
+ helper 'imageTag', (source, options = {}) ->
66
+ options = @_clone options
67
+ handleSizeAttribute options
68
+
69
+ if source == '' || source[0..4] == 'data:'
70
+ options['src'] = source
71
+ options['alt'] = undefined if options['alt'] == null
72
+ else
73
+ options['src'] = @imagePath source
74
+ if options['alt'] == null
75
+ options['alt'] = undefined
76
+ else
77
+ options['alt'] = @imageAlt(source) if options['alt'] == undefined
78
+ @tag 'img', options
79
+
80
+ helper 'javascriptIncludeTag', (sources...) ->
81
+ path_options = if typeof sources[sources.length-1] == 'object'
82
+ sources.pop()
83
+ else
84
+ {}
85
+
86
+ result = ''
87
+ for index, source of sources
88
+ options = { src: @javascriptPath(source, path_options) }
89
+ result += @contentTag 'script', '', options
90
+ result
91
+
92
+ helper 'stylesheetLinkTag', (sources...) ->
93
+ path_options = {}
94
+ options = if typeof sources[sources.length - 1] == 'object'
95
+ opts = sources.pop()
96
+ path_options['format'] = opts['format']
97
+ path_options['type'] = opts['type']
98
+ opts['format'] = undefined
99
+ opts['type'] = undefined
100
+ opts['rel'] ||= 'stylesheet'
101
+ opts['media'] ||= 'screen'
102
+ opts
103
+ else
104
+ { rel: 'stylesheet', media: 'screen' }
105
+
106
+ result = ''
107
+ for index, source of sources
108
+ options['href'] = @stylesheetPath source, path_options
109
+ result += @tag 'link', options
110
+ result
111
+
112
+ helper 'videoTag', (paths...) ->
113
+ options = if typeof paths[paths.length - 1] == 'object'
114
+ @_clone paths.pop()
115
+ else
116
+ {}
117
+
118
+ handleSizeAttribute options
119
+ options['poster'] = @imagePath(options['poster']) if options['poster']
120
+
121
+ path_options = {}
122
+ path_options['format'] = options['format']
123
+ options['format'] = undefined
124
+ if paths.length == 1
125
+ options['src'] = @videoPath paths[0], path_options
126
+ @tag 'video', options
127
+ else
128
+ content = ''
129
+ source_options
130
+ if options['source']
131
+ source_options = options['source']
132
+ options['source'] = undefined
133
+ else
134
+ source_options = {}
135
+ for i, path of paths
136
+ source_options['src'] = @videoPath path, path_options
137
+ content += @tag 'source', source_options
138
+ @contentTag 'video', content, options
@@ -0,0 +1,90 @@
1
+ # TODO: Think about digests
2
+ helper 'computeAssetPath', (source, options = {}) ->
3
+ options['type'] ||= 'asset'
4
+
5
+ if @_context.forceAssetDomain
6
+ options['domain'] ||= @_context.domain[ options['type'] ]
7
+
8
+ format = if options['format'] == undefined
9
+ @_context.defaultFormat[ options['type'] ]
10
+ else
11
+ options['format']
12
+
13
+ if format
14
+ sharp_index = source.lastIndexOf '#'
15
+ query_index = source.lastIndexOf '?'
16
+
17
+ index = if sharp_index == -1
18
+ query_index
19
+ else
20
+ if query_index == -1
21
+ sharp_index
22
+ else
23
+ if query_index > sharp_index
24
+ sharp_index
25
+ else
26
+ query_index
27
+
28
+ if index == -1
29
+ source += '.' + format unless source[(-format.length-1)..-1] == ('.' + format)
30
+ else
31
+ prefix = source.slice 0, index
32
+ sufix = source.slice index
33
+ if prefix[(-format.length-1)..-1] == ('.' + format)
34
+ source = prefix + sufix
35
+ else
36
+ source = prefix + '.' + format + sufix
37
+
38
+ source = if source[0] == '/'
39
+ source
40
+ else
41
+ prefix_path = @_context.defaultPath[options['type']]
42
+ if prefix_path[prefix_path.length-1] == '/'
43
+ prefix_path + source
44
+ else
45
+ prefix_path + '/' + source
46
+
47
+ source = '/' + source if source[0] != '/'
48
+
49
+ if options['domain']
50
+ source = options['domain'] + source
51
+
52
+ source
53
+
54
+ helper 'assetPath', (source, options = {}) ->
55
+ source = source.toString()
56
+ options = @_clone options
57
+
58
+ fullDoaminPath = /[\w\d]+\:\/\//i
59
+ return source if fullDoaminPath.test(source) || source[0..1] == '//'
60
+
61
+ @computeAssetPath source, options
62
+
63
+ helper 'assetUrl', (source, options = {}) ->
64
+ source = source.toString()
65
+ options = @_clone options
66
+
67
+ options['domain'] ||= if options['type']
68
+ @_context.domain[options['type']] || @_context.domain.asset
69
+ else
70
+ @_context.domain.asset
71
+
72
+ @assetPath source, options
73
+
74
+ assetPathBuilder = (_type) ->
75
+ type_built = _type
76
+ (source, options = {}) ->
77
+ options = @_clone options
78
+ options['type'] = type_built if options['type'] == undefined
79
+ @assetPath source, options
80
+
81
+ assetUrlBuilder = (_type) ->
82
+ type_built = _type
83
+ (source, options = {}) ->
84
+ options = @_clone options
85
+ options['type'] = type_built if options['type'] == undefined
86
+ @assetUrl source, options
87
+
88
+ for index, type of ['audio', 'font', 'image', 'video', 'javascript', 'stylesheet']
89
+ helper (type+'Path'), assetPathBuilder.call(this,type)
90
+ helper (type+'Url'), assetUrlBuilder.call(this,type)
@@ -0,0 +1,5 @@
1
+ helper 'csrfMetaTags', () ->
2
+ if @csrfToken
3
+ @contentTag('meta', name: 'csrf-param', content: 'authenticity_token') + @contentTag('meta', name: 'csrf-token', content: @csrfToken)
4
+ else
5
+ ''
@@ -0,0 +1,54 @@
1
+ helper 'strftime', (date, pattern) ->
2
+ I18n.strftime date, pattern
3
+
4
+ helper 'distanceOfTimeInWords', (from_time, to_time, withSeconds = false) ->
5
+ distance_in_minutes = Math.abs((to_time - from_time) / 60000)
6
+ [key, options] = switch
7
+ when distance_in_minutes < 1
8
+ if withSeconds
9
+ distance_in_seconds = Math.abs((to_time - from_time) / 1000)
10
+ switch
11
+ when distance_in_seconds < 5 then [ 'less_than_x_seconds', { count: 5 } ]
12
+ when distance_in_seconds < 10 then [ 'less_than_x_seconds', { count: 10 } ]
13
+ when distance_in_seconds < 20 then [ 'less_than_x_seconds', { count: 20 } ]
14
+ when distance_in_seconds < 40 then [ 'half_a_minute', { } ]
15
+ when distance_in_seconds < 60 then [ 'less_than_x_minutes', { count: 1 } ]
16
+ else ['x_minutes', { count: 1 }]
17
+ else
18
+ ['less_than_x_minutes', { count: 1 }]
19
+ when distance_in_minutes < 45 then [ 'x_minutes', { count: Math.floor(distance_in_minutes) } ]
20
+ when distance_in_minutes < 90 then [ 'about_x_hours', { count: 1 } ]
21
+ when distance_in_minutes < 1440 then [ 'about_x_hours', { count: Math.floor(distance_in_minutes / 60.0) } ]
22
+ when distance_in_minutes < 2520 then [ 'x_days', { count: 1 } ]
23
+ when distance_in_minutes < 43200 then [ 'x_days', { count: Math.floor(distance_in_minutes / 1440.0) } ]
24
+ when distance_in_minutes < 86400 then [ 'about_x_months', { count: Math.floor(distance_in_minutes / 43200.0) } ]
25
+ when distance_in_minutes < 525600 then [ 'x_months', { count: Math.floor(distance_in_minutes / 43200.0) } ]
26
+ else
27
+ remainder = distance_in_minutes % 525600
28
+ distance_in_years = Math.floor( distance_in_minutes / 525600 )
29
+ if remainder < 131400
30
+ ['about_x_years', { count: distance_in_years } ]
31
+ else if remainder < 394200
32
+ ['over_x_years', { count: distance_in_years } ]
33
+ else
34
+ ['almost_x_years', { count: distance_in_years + 1 } ]
35
+ location_key = 'datetime.distance_in_words.' + key
36
+ @translate location_key, options
37
+
38
+ helper 'distanceOfTimeInWordsToNow', (from_time, withSeconds = false) ->
39
+ @distanceOfTimeInWords from_time, new Date(), withSeconds
40
+ alias 'timeAgoInWords', 'distanceOfTimeInWordsToNow'
41
+
42
+ # date_select
43
+ # datetime_select
44
+ # select_date
45
+ # select_datetime
46
+ # select_day
47
+ # select_hour
48
+ # select_minute
49
+ # select_month
50
+ # select_second
51
+ # select_time
52
+ # select_year
53
+ # time_select
54
+ # time_tag
@@ -0,0 +1,2 @@
1
+ helper 'debug', (block, value) ->
2
+ [(typeof block), block(value)]
@@ -0,0 +1,25 @@
1
+
2
+ # check_box
3
+ # color_field
4
+ # date_field
5
+ # datetime_field
6
+ # datetime_local_field
7
+ # email_field
8
+ # fields_for
9
+ # file_field
10
+ # form_for
11
+ # hidden_field
12
+ # label
13
+ # month_field
14
+ # number_field
15
+ # password_field
16
+ # phone_field
17
+ # radio_button
18
+ # range_field
19
+ # search_field
20
+ # telephone_field
21
+ # text_area
22
+ # text_field
23
+ # time_field
24
+ # url_field
25
+ # week_field