jsus 0.3.6 → 0.4.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.
Files changed (71) hide show
  1. data/.travis.yml +2 -0
  2. data/CHANGELOG +5 -0
  3. data/Gemfile +7 -4
  4. data/VERSION +1 -1
  5. data/bin/jsus +1 -5
  6. data/cucumber.yml +1 -1
  7. data/features/command-line/external_dependency_resolution.feature +4 -4
  8. data/features/command-line/generate_includes.feature +34 -0
  9. data/features/command-line/mooforge_compatibility_layer.feature +1 -1
  10. data/features/command-line/postproc.feature +12 -3
  11. data/features/command-line/structure_json.feature +5 -40
  12. data/features/data/ExternalDependencyWithExternalDependency/Leonardo/Source/Core.js +2 -2
  13. data/features/step_definitions/cli_steps.rb +15 -9
  14. data/features/support/env.rb +1 -1
  15. data/jsus.gemspec +25 -7
  16. data/lib/extensions/rgl.rb +2 -1
  17. data/lib/jsus/cli.rb +43 -27
  18. data/lib/jsus/container.rb +71 -51
  19. data/lib/jsus/middleware.rb +2 -3
  20. data/lib/jsus/package.rb +27 -129
  21. data/lib/jsus/packager.rb +10 -6
  22. data/lib/jsus/pool.rb +48 -20
  23. data/lib/jsus/source_file.rb +119 -198
  24. data/lib/jsus/tag.rb +72 -106
  25. data/lib/jsus/util.rb +14 -4
  26. data/lib/jsus/util/compressor.rb +1 -1
  27. data/lib/jsus/util/documenter.rb +1 -1
  28. data/lib/jsus/util/mixins.rb +7 -0
  29. data/lib/jsus/util/mixins/operates_on_sources.rb +29 -0
  30. data/lib/jsus/util/post_processor.rb +35 -0
  31. data/lib/jsus/util/post_processor/base.rb +32 -0
  32. data/lib/jsus/util/post_processor/moocompat12.rb +19 -0
  33. data/lib/jsus/util/post_processor/mooltie8.rb +19 -0
  34. data/lib/jsus/util/post_processor/semicolon.rb +18 -0
  35. data/lib/jsus/util/validator/base.rb +3 -23
  36. data/lib/jsus/util/watcher.rb +8 -6
  37. data/spec/data/Extensions/app/javascripts/Core/Source/Hash.js +13 -0
  38. data/spec/data/Extensions/app/javascripts/Core/Source/Mash.js +13 -0
  39. data/spec/data/Extensions/app/javascripts/Core/package.yml +3 -1
  40. data/spec/data/Extensions/app/javascripts/Orwik/Extensions/Mash.js +16 -0
  41. data/spec/data/Extensions/app/javascripts/Orwik/package.yml +2 -1
  42. data/spec/data/OutsideDependencies/app/javascripts/Orwik/package.yml +3 -3
  43. data/spec/data/SimpleSources/dependent_source_one.js +15 -0
  44. data/spec/data/SimpleSources/replacement_source_one.js +13 -0
  45. data/spec/data/SimpleSources/simple_source_one.js +13 -0
  46. data/spec/data/SimpleSources/simple_source_two.js +13 -0
  47. data/spec/data/extension_1.js +15 -0
  48. data/spec/data/extension_2.js +15 -0
  49. data/spec/data/replacement.js +15 -0
  50. data/spec/jsus/container_spec.rb +72 -14
  51. data/spec/jsus/package_spec.rb +10 -128
  52. data/spec/jsus/packager_spec.rb +11 -11
  53. data/spec/jsus/pool_spec.rb +13 -22
  54. data/spec/jsus/source_file_spec.rb +66 -215
  55. data/spec/jsus/tag_spec.rb +24 -69
  56. data/spec/jsus/util/documenter_spec.rb +1 -1
  57. data/spec/jsus/util/post_processor/moocompat12_spec.rb +23 -0
  58. data/spec/jsus/util/post_processor/mooltie8_spec.rb +23 -0
  59. data/spec/jsus/util/post_processor/semicolon_spec.rb +21 -0
  60. data/spec/jsus/util/post_processors/base_spec.rb +6 -0
  61. data/spec/jsus/util/tree_spec.rb +3 -3
  62. data/spec/jsus/util/validator/base_spec.rb +4 -2
  63. data/spec/jsus/util/watcher_spec.rb +12 -2
  64. data/spec/shared/mixins_segs.rb +38 -0
  65. data/spec/spec_helper.rb +6 -0
  66. metadata +28 -10
  67. data/features/data/tmp2/package.js +0 -35
  68. data/features/data/tmp2/scripts.json +0 -13
  69. data/features/data/tmp2/tree.json +0 -26
  70. data/lib/jsus/compiler.rb +0 -28
  71. data/spec/shared/class_stubs.rb +0 -31
@@ -11,6 +11,10 @@ module Jsus
11
11
  #
12
12
  # @param [*SourceFile] sources
13
13
  def initialize(*sources)
14
+ @sources = []
15
+ @normal_sources = []
16
+ @extensions = []
17
+ @replacements = []
14
18
  sources.each do |source|
15
19
  push(source)
16
20
  end
@@ -23,10 +27,18 @@ module Jsus
23
27
  # @param [SourceFile] source source pushed file
24
28
  def push(source)
25
29
  if source
26
- if source.kind_of?(Array) || source.kind_of?(Container)
30
+ if source.kind_of?(Array)
27
31
  source.each {|s| self.push(s) }
32
+ elsif source.kind_of?(Container)
33
+ source.all_sources.each {|s| self.push(s) }
28
34
  else
29
- sources.push(source) unless sources.include?(source)
35
+ if source.extension?
36
+ @extensions << source unless @extensions.include?(source)
37
+ elsif source.replacement?
38
+ @replacements << source unless @replacements.include?(source)
39
+ else
40
+ @normal_sources << source unless @normal_sources.include?(source)
41
+ end
30
42
  end
31
43
  end
32
44
  clear_cache!
@@ -41,23 +53,24 @@ module Jsus
41
53
  map {|item| item.respond_to?(:flatten) ? item.flatten : item }.flatten
42
54
  end
43
55
 
44
- # Contains the source files. Please, don't use sources directly, if you
45
- # depend on them to be topologically sorted. Use collection methods like
46
- # inject/reject/map directly on the container instead.
56
+ # Contains the source files.
47
57
  #
48
58
  # @return [Array]
49
- # @api semipublic
59
+ # @api public
50
60
  def sources
51
- @sources ||= []
61
+ sort!
62
+ @sources
52
63
  end
53
64
  alias_method :to_a, :sources
54
65
 
55
- # Sets sources to new value.
66
+ # Includes all sources, even those that would normally be replaced.
67
+ # Without any order.
56
68
  #
69
+ # @return [Array]
57
70
  # @api semipublic
58
- def sources=(new_value) # :nodoc:
59
- @sources = new_value
60
- end
71
+ def all_sources
72
+ @normal_sources + @extensions + @replacements
73
+ end # all_sources
61
74
 
62
75
  # Topologically sorts items in container if required.
63
76
  #
@@ -65,8 +78,10 @@ module Jsus
65
78
  # @api semipublic
66
79
  def sort!
67
80
  unless sorted?
68
- remove_replaced_files!
69
- self.sources = topsort
81
+ @sources = topsort
82
+ insert_extensions!
83
+ insert_replacements!
84
+ @sources.uniq!
70
85
  @sorted = true
71
86
  end
72
87
  self
@@ -106,6 +121,23 @@ module Jsus
106
121
  "#<#{self.class.name}:#{self.object_id} #{self.sources.inspect}>"
107
122
  end
108
123
 
124
+ # Returns all the tags provided by source files.
125
+ # @return [Array]
126
+ # @api public
127
+ def provides
128
+ sort!
129
+ sources.map {|s| s.provides }.flatten
130
+ end # provides
131
+
132
+ # Returns all the tags required by source files, except for those which are
133
+ # provided by other files in the container (i.e. unresolved dependencies)
134
+ # @return [Array]
135
+ # @api public
136
+ def requires
137
+ sort!
138
+ sources.map {|s| s.requires }.flatten - provides
139
+ end # requires
140
+
109
141
  # Private API
110
142
 
111
143
  # Performs topological sort inside current container.
@@ -114,7 +146,7 @@ module Jsus
114
146
  def topsort
115
147
  graph = RGL::DirectedAdjacencyGraph.new
116
148
  # init vertices
117
- items = sources
149
+ items = @normal_sources
118
150
  items.each {|item| graph.add_vertex(item) }
119
151
  # init edges
120
152
  items.each do |item|
@@ -177,58 +209,47 @@ module Jsus
177
209
  def provides_tree!
178
210
  tree = Util::Tree.new
179
211
  # Provisions
180
- sources.each do |file|
181
- file.provides.each do |tag|
182
- tree[tag] = file
212
+ @normal_sources.each do |file|
213
+ provisions = file.provides
214
+ if replacement = @replacements.detect {|r| provisions.any? {|tag| tag == r.replaces } }
215
+ file = replacement
183
216
  end
184
- end
185
- # Replacements
186
- sources.each do |file|
187
- if file.replaces
188
- tree[file.replaces] = file
217
+ provisions.each do |tag|
218
+ tree[tag] = file
189
219
  end
190
220
  end
191
221
  tree
192
222
  end
193
223
 
194
- # Removes files which are marked as replaced by other sources.
195
- #
196
224
  # @api private
197
- def remove_replaced_files!
198
- sources.reject! do |sf|
199
- !sf.provides.empty? && sf.provides.any? { |tag| replacements_tree[tag] && replacements_tree[tag] != sf }
225
+ def insert_extensions!
226
+ @extensions.each do |ext|
227
+ ext_tag = ext.extends
228
+ @sources.dup.each_with_index do |src, i|
229
+ if src.provides.any? {|tag| tag == ext_tag }
230
+ @sources.insert(i+1, ext)
231
+ break
232
+ end
233
+ end
200
234
  end
201
- end
202
-
203
- # Cached tree of what source files replace.
204
- #
205
- # @api private
206
- # @return [Jsus::Util::Tree]
207
- def replacements_tree
208
- @replacements_tree ||= replacements_tree!
209
- end
235
+ end # insert_extensions!
210
236
 
211
- # Returns tree of what source files replace.
212
- #
213
237
  # @api private
214
- # @return [Jsus::Util::Tree]
215
- def replacements_tree!
216
- tree = Util::Tree.new
217
- sources.each do |file|
218
- if file.replaces
219
- tree[file.replaces] = file
238
+ def insert_replacements!
239
+ @replacements.each do |repl|
240
+ @sources.each_with_index do |src, i|
241
+ if src.provides.any? {|tag| tag == repl.replaces }
242
+ @sources[i] = repl
243
+ break
244
+ end
220
245
  end
221
246
  end
222
- tree
223
- end
247
+ end # insert_replacements!
224
248
 
225
249
  # Clears all caches for given container.
226
250
  #
227
251
  # @api private
228
252
  def clear_cache!
229
- @provides_tree = nil
230
- @replacements_tree = nil
231
- @dependency_cache = nil
232
253
  @sorted = false
233
254
  end
234
255
 
@@ -246,9 +267,8 @@ module Jsus
246
267
  ]
247
268
 
248
269
  (DELEGATED_METHODS).each do |m|
249
- class_eval <<-EVAL
270
+ class_eval(<<-EVAL, __FILE__, __LINE__ + 1)
250
271
  def #{m}(*args, &block)
251
- sort!
252
272
  #{"clear_cache!" if CACHE_CLEAR_METHODS.include?(m)}
253
273
  self.sources.send(:#{m}, *args, &block)
254
274
  end
@@ -175,7 +175,7 @@ module Jsus
175
175
  def generate_requires(path_string)
176
176
  files = path_string_to_files(path_string)
177
177
  if !files.empty?
178
- response = Container.new(*files).map {|f| f.content }.join("\n")
178
+ response = Container.new(*files).map {|f| f.source }.join("\n")
179
179
  response = Jsus::Util::Compressor.compress(response, :method => self.class.settings[:compression_method]) if request_options[:compress]
180
180
  respond_with(response)
181
181
  else
@@ -262,8 +262,7 @@ module Jsus
262
262
  # @api semipublic
263
263
  def get_associated_files(source_file_or_package)
264
264
  if package = pool.packages.detect {|pkg| pkg.name == source_file_or_package}
265
- package.include_dependencies!
266
- package.linked_external_dependencies.to_a + package.source_files.to_a
265
+ pool.compile_package(package).to_a
267
266
  elsif source_file = pool.lookup(source_file_or_package)
268
267
  pool.lookup_dependencies(source_file).to_a << source_file
269
268
  else
@@ -3,6 +3,7 @@ module Jsus
3
3
  # Package is a (self-contained) unit with all the info required to build
4
4
  # a javascript package.
5
5
  #
6
+
6
7
  class Package
7
8
  # directory which this package resides in (full path)
8
9
  attr_accessor :directory
@@ -10,29 +11,27 @@ module Jsus
10
11
  attr_accessor :pool
11
12
 
12
13
  # Constructors
13
-
14
14
  #
15
15
  # Creates a package from given directory.
16
16
  #
17
17
  # @param [String] directory path to directory containing a package
18
- # @param [Hash] options
19
- # @option options [Jsus::Pool] :pool which pool the package should belong to.
20
- # @raise an error when the given directory doesn't contain a package.yml or package.json
21
- # file with meta info.
18
+ # @raise [RuntimeError] when the given directory doesn't contain a
19
+ # package.yml or package.json file with meta info.
22
20
  # @api public
23
- def initialize(directory, options = {})
21
+ def initialize(directory)
24
22
  self.directory = File.expand_path(directory)
25
- if File.exists?(File.join(directory, 'package.yml'))
26
- self.header = YAML.load_file(File.join(directory, 'package.yml'))
23
+ @header = if File.exists?(File.join(directory, 'package.yml'))
24
+ YAML.load_file(File.join(directory, 'package.yml'))
27
25
  elsif File.exists?(File.join(directory, 'package.json'))
28
- self.header = JSON.load(File.open(File.join(directory, 'package.json'), 'r:utf-8') {|f| f.read })
26
+ JSON.load(Jsus::Util.read_file(File.join(directory, 'package.json')))
29
27
  else
30
28
  Jsus.logger.fatal "Directory #{directory} does not contain a valid package.yml / package.json file!"
31
29
  raise "Directory #{directory} does not contain a valid package.yml / package.json file!"
32
30
  end
33
31
  Dir.chdir(directory) do
34
- files.each do |source|
35
- source_file = SourceFile.from_file(source, :package => self)
32
+ files.each do |filename|
33
+ source_file = SourceFile.from_file(filename, :namespace => name)
34
+ source_file.package = self
36
35
  if source_file
37
36
  if source_file.extension?
38
37
  extensions << source_file
@@ -40,14 +39,10 @@ module Jsus
40
39
  source_files << source_file
41
40
  end
42
41
  else
43
- Jsus.logger.warn "#{source} is not found for #{name}"
42
+ Jsus.logger.warn "#{filename} is not found for #{name}"
44
43
  end
45
44
  end
46
45
  end
47
- if options[:pool]
48
- self.pool = options[:pool]
49
- self.pool << self
50
- end
51
46
  end
52
47
 
53
48
 
@@ -82,115 +77,23 @@ module Jsus
82
77
  def files
83
78
  header["files"] = header["files"] || header["sources"] || []
84
79
  end
85
- alias_method :sources, :files
86
80
 
87
- # @return [Array] an array of provided tags including those provided by linked external dependencies.
81
+ # @return [Array] an array of provided tags
88
82
  # @api public
89
83
  def provides
90
- source_files.map {|s| s.provides }.flatten | linked_external_dependencies.map {|d| d.provides }.flatten
91
- end
92
-
93
- # @return [Array] an array of provided tags names including those provided by linked external dependencies.
94
- # @api public
95
- def provides_names
96
- source_files.map {|s| s.provides_names(:short => true) }.flatten |
97
- linked_external_dependencies.map {|d| d.provides_names }.flatten
84
+ source_files.map {|s| s.provides }.flatten
98
85
  end
86
+ alias_method :provisions, :provides
99
87
 
100
- # @return [Array] an array of unresolved dependencies' tags for the package.
88
+ # @return [Array] array dependencies tags for all source files in the package
101
89
  # @api public
102
90
  def dependencies
103
91
  result = source_files.map {|source| source.dependencies }.flatten
104
- result |= linked_external_dependencies.map {|d| d.dependencies}.flatten
105
92
  result -= provides
106
93
  result
107
94
  end
108
-
109
- # @return [Array] an array of unresolved dependencies' names.
110
- # @api public
111
- def dependencies_names
112
- dependencies.map {|d| d.name(:short => true) }
113
- end
114
-
115
- # @return [Array] an array of external dependencies' tags (including resolved ones).
116
- # @api public
117
- def external_dependencies
118
- source_files.map {|s| s.external_dependencies }.flatten
119
- end
120
-
121
- # @return [Array] an array of external dependencies' names (including resolved ones).
122
- # @api public
123
- def external_dependencies_names
124
- external_dependencies.map {|d| d.name }
125
- end
126
-
127
- # @return [Jsus::Container] source files with external dependencies in correct order.
128
- # @api public
129
- def linked_external_dependencies
130
- @linked_external_dependencies ||= Container.new
131
- end
132
-
133
- # Compiles source files and linked external source files into a given category.
134
- # @param [String, nil] directory directory to output the result into
135
- # @return [String] content of merged source files
136
- # @api public
137
- def compile(directory = ".")
138
- fn = directory ? File.join(directory, filename) : nil
139
- Packager.new(*(source_files.to_a + linked_external_dependencies.to_a)).pack(fn)
140
- end
141
-
142
- # Generates tree structure for files in package into a json file.
143
- # @param [String] directory directory to output the result
144
- # @param [String] filename resulting filename
145
- # @return [Hash] hash with tree structure
146
- # @api public
147
- def generate_tree(directory = ".", filename = "tree.json")
148
- FileUtils.mkdir_p(directory)
149
- result = ActiveSupport::OrderedHash.new
150
- source_files.each do |source|
151
- components = File.dirname(source.relative_filename).split(File::SEPARATOR)
152
- # deleting source dir by convention
153
- components.delete("Source")
154
- node = result
155
- components.each do |component|
156
- node[component] ||= ActiveSupport::OrderedHash.new
157
- node = node[component]
158
- end
159
- node[File.basename(source.filename, ".js")] = source.to_hash
160
- end
161
- File.open(File.join(directory, filename), "w") { |resulting_file| resulting_file << JSON.pretty_generate(result) }
162
- result
163
- end
164
-
165
- # Generates info about resulting compiled package into a json file.
166
- # @param [String] directory directory to output the result
167
- # @param [String] filename resulting filename
168
- # @return [Hash] hash with scripts info
169
- # @api public
170
- def generate_scripts_info(directory = ".", filename = "scripts.json")
171
- FileUtils.mkdir_p directory
172
- File.open(File.join(directory, filename), "w") { |resulting_file| resulting_file << JSON.pretty_generate(self.to_hash) }
173
- self.to_hash
174
- end
175
-
176
- # Looks up all the external dependencies in the pool.
177
- # @api semipublic
178
- def include_dependencies!
179
- source_files.each do |source|
180
- if pool
181
- deps = pool.lookup_dependencies(source).to_a - @source_files.to_a
182
- linked_external_dependencies << deps
183
- end
184
- end
185
- end
186
-
187
- # Executes #include_extensions for all the source files.
188
- # @api semipublic
189
- def include_extensions!
190
- source_files.each do |source|
191
- source.include_extensions!
192
- end
193
- end
95
+ alias_method :requires, :dependencies
96
+ alias_method :requirements, :dependencies
194
97
 
195
98
  # Lists the required files for the package.
196
99
  # @return [Array] ordered list of full paths to required files.
@@ -200,30 +103,31 @@ module Jsus
200
103
  end
201
104
 
202
105
  # Hash representation of the package.
106
+ # @return [Hash]
203
107
  # @api public
204
108
  def to_hash
205
109
  {
206
110
  name => {
207
- :desc => description,
208
- :provides => provides_names,
209
- :requires => dependencies_names
111
+ :description => description,
112
+ :provides => provides.map {|tag| tag.name },
113
+ :requires => requires.map {|tag| tag.namespace == name ? tag.name : tag.full_name }
210
114
  }
211
115
  }
212
116
  end
213
117
 
214
118
 
215
- # Container with source files
216
- # @return [Jsus::Container]
119
+ # Array with source files
120
+ # @return [Array]
217
121
  # @api semipublic
218
122
  def source_files
219
- @source_files ||= Container.new
123
+ @source_files ||= []
220
124
  end
221
125
 
222
- # Container with extensions (they aren't compiled or included into #reqired_files list)
223
- # @return [Jsus::Container]
126
+ # Array with extensions
127
+ # @return [Array]
224
128
  # @api semipublic
225
129
  def extensions
226
- @extensions ||= Container.new
130
+ @extensions ||= []
227
131
  end
228
132
 
229
133
  # Private API
@@ -234,11 +138,5 @@ module Jsus
234
138
  def header=(new_header)
235
139
  @header = new_header
236
140
  end
237
-
238
- # @param [Enumerable] new_value external dependencies
239
- # @api private
240
- def linked_external_dependencies=(new_value)
241
- @linked_external_dependencies = new_value
242
- end
243
141
  end
244
142
  end