manatee 0.0.1.pre1

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