lib/bower-rails/dsl.rb
require 'json'
require 'fileutils'
module BowerRails
class Dsl
DEFAULT_DEPENDENCY_GROUP = :dependencies
def self.evalute(root_path, filename)
new(root_path).tap do |dsl|
dsl.send(eval_file_method, File.join(root_path, filename))
end
end
def self.eval_file_method
BowerRails.use_gem_deps_for_bowerfile ? :eval_file_with_deps : :eval_file
end
attr_reader :dependencies, :root_path
def initialize(root_path)
@dependency_groups = []
@root_path = root_path
@bower_dependencies_list = []
@dependencies = {}
@resolutions = {}
@ignored_dependencies = []
@assets_path ||= "assets"
@main_files = {}
@current_group = nil
end
def asset(name, *args, &block)
@asset_name = name
group = @current_group || default_group
options = Hash === args.last ? args.pop.dup : {}
version = args.last || "latest"
version = options[:ref] if options[:ref]
options[:git] = "https://github.com/#{options[:github]}.git" if options[:github]
if options[:git]
version = if version == 'latest'
options[:git]
else
options[:git] + "#" + version
end
end
if options[:main_files]
main_files(options[:main_files])
end
instance_eval(&block) if block_given?
normalized_group_path = normalize_location_path(group.first, group_assets_path(group))
@dependencies[normalized_group_path] ||= {}
@dependencies[normalized_group_path][current_dependency_group_normalized] ||= {}
@dependencies[normalized_group_path][current_dependency_group_normalized][name] = version
end
def dependency_group(name, options = {}, &block)
assert_dependency_group_name name
add_dependency_group name
yield if block_given?
remove_dependency_group!
end
def directories
@dependencies.keys
end
def eval_file(file)
instance_eval(File.open(file, 'rb') { |f| f.read }, file.to_s)
end
def eval_file_with_deps(file)
Gem::Specification.map do |dep|
bowerfile_in_dep = File.join(dep.gem_dir, 'Bowerfile')
eval_file(bowerfile_in_dep) if bowerfile_in_dep != file && File.exist?(bowerfile_in_dep)
end
eval_file(file)
end
def final_assets_path
groups.map do |group|
[group.first.to_s, group_assets_path(group)]
end.uniq
end
def generate_dotbowerrc
contents = JSON.parse(File.read(File.join(root_path, '.bowerrc'))) rescue {}
contents["directory"] = "bower_components"
contents["ignoredDependencies"] = @ignored_dependencies
JSON.pretty_generate(contents)
end
def group_assets_path group
group.last[:assets_path]
end
def group(name, options = {}, &block)
options[:assets_path] ||= @assets_path
assert_asset_path options[:assets_path]
assert_group_name name
@current_group = add_group name, options
yield if block_given?
end
def resolution(name, version)
@resolutions[name] = version
end
def resolutions_with_root
{ :resolutions => @resolutions }
end
def ignored_dependency(name)
@ignored_dependencies.push(name)
end
def write_bower_json
@dependencies.each do |dir, data|
FileUtils.mkdir_p dir unless File.directory? dir
File.open(File.join(dir, "bower.json"), "w") do |f|
data.merge!(resolutions_with_root)
f.write(dependencies_to_json(data))
end
end
end
def write_dotbowerrc
groups.map do |group|
normalized_group_path = normalize_location_path(group.first, group_assets_path(group))
File.open(File.join(normalized_group_path, ".bowerrc"), "w") do |f|
f.write(generate_dotbowerrc)
f.write("\n")
end
end
end
# accepts string or array to alter main option for bower.json
def main_files(value = nil)
if value
@main_files[@asset_name] = Array(value)
end
@main_files
end
private
# Stores the dependency group name in the stack
#
def add_dependency_group(dependency_group)
@dependency_groups.push dependency_group.to_sym
dependency_group
end
def add_group(*group)
@groups = (groups << group) and return group
end
def assert_dependency_group_name(name)
unless [:dependencies, :devDependencies].include?(normalize_dependency_group_name(name))
raise ArgumentError, "Dependency group should be either dependencies or dev_dependencies, provided: #{name}"
end
end
def assets_path(assets_path)
assert_asset_path assets_path
@assets_path = assets_path
end
def assert_asset_path(path)
unless path.start_with?('assets', '/assets')
raise ArgumentError, "Assets should be stored in /assets directory, try assets_path 'assets/#{path}' instead"
end
end
def assert_group_name name
raise ArgumentError, "Group name should be :lib or :vendor only" unless [:lib, :vendor].include?(name)
end
# Returns name for the current dependency from the stack
#
def current_dependency_group
@dependency_groups.last || DEFAULT_DEPENDENCY_GROUP.to_sym
end
# Returns normalized current dependency group name
#
def current_dependency_group_normalized
normalize_dependency_group_name current_dependency_group
end
def default_group
[:vendor, { :assets_path => @assets_path }]
end
# Attempts to parse data from @dependencies to JSON
#
def dependencies_to_json(data)
JSON.pretty_generate({
:name => "dsl-generated-dependencies"
}.merge(data))
end
def groups
@groups ||= [default_group]
end
# Implementing ActiveSupport::Inflector camelize(:lower)
#
def normalize_dependency_group_name(name)
segments = name.to_s.dup.downcase.split(/_/)
[segments.shift, *segments.map{ |word| word.capitalize }].join('').to_sym
end
def normalize_location_path(loc, assets_path)
File.join(root_path, loc.to_s, assets_path)
end
# Removes the dependency group name in the stack
#
def remove_dependency_group!
@dependency_groups.pop
end
end
end