stylish 0.0.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,97 @@
1
+ command "import external theme" do |c|
2
+ c.syntax = "stylish import SOURCE DESTINATION [OPTIONS]"
3
+ c.description = "import a theme"
4
+
5
+ c.option '--importer IMPORTER', String, 'The importer class to use'
6
+ c.option '--output-path PATH', String, 'Which folder to store the resulting theme in'
7
+ c.option '--clean', 'Whether or not to remove the output path'
8
+
9
+ c.action do |args, options|
10
+ require 'stylish/theme/importer'
11
+
12
+ source = Pathname(args.shift)
13
+ destination = args.shift
14
+ destination ||= "stylish-export"
15
+
16
+ options.default(importer: "startup_framework",
17
+ output_path: Stylish.pwd.join("vendor/themes/startup_framework"))
18
+
19
+ importer = Stylish::Theme::Importer.load_by_type(options.importer)
20
+
21
+ job = importer.new(source, output_path: options.output_path, clean: options.clean)
22
+
23
+ job.prompt_for_settings()
24
+
25
+ job.run()
26
+ end
27
+ end
28
+
29
+ command "prepare theme" do |c|
30
+ c.syntax = "stylish prepare theme THEME [OPTIONS]"
31
+ c.description = "take a stylish theme and load it into the desired format for your current project"
32
+
33
+ c.option '--target TARGET', String, 'What type of project will use the theme?'
34
+ c.option '--output-path FILE', String, 'Where to put the theme?'
35
+
36
+ c.action do |args, options|
37
+ require 'stylish/theme/importer'
38
+ require 'stylish/theme/exporter'
39
+
40
+ options.default(output_path: Pathname(Dir.pwd).join('vendor'))
41
+
42
+ theme_name = args.first
43
+ theme = Stylish::Theme.new(Stylish.lib_root.join('../vendor/themes', theme_name))
44
+ output_path = Pathname(options.output_path)
45
+ importer = Stylish::Theme::Importer.load_by_type(theme_name)
46
+
47
+ FileUtils.cp_r(theme.root, output_path)
48
+
49
+ theme = Stylish::Theme.new(output_path.join(theme_name))
50
+
51
+ target = options.target.to_s.to_sym
52
+
53
+ if target == :middleman
54
+ helper = theme.root.join('assets/stylesheets/startup-framework/dependencies/startup-framework/helper.less')
55
+
56
+ helper_content = helper.read
57
+
58
+ helper_content.sub!("../fonts/", "/fonts/")
59
+
60
+ helper.open("w+") {|fh| fh.write(helper_content)}
61
+
62
+ files = Dir[theme.root.join('views','components','**/*.html')]
63
+
64
+ src_values = []
65
+
66
+ files.map! {|file| Pathname(file) }
67
+
68
+ files.each do |path|
69
+ fragment = Nokogiri::HTML.fragment(path.read)
70
+ image_tags = fragment.css("img")
71
+
72
+ src_values += image_tags.map do |tag|
73
+ src = tag.attr('src')
74
+ original = src.dup
75
+
76
+ conversions = importer.const_get(:ImagePathConversions)
77
+
78
+ conversions.each do |pattern, replacement|
79
+ src.gsub! pattern, replacement
80
+ end
81
+
82
+ puts "== Replacing #{ original } with #{ src } value"
83
+ tag['src'] = "<%= image_path('#{src}') %>".html_safe
84
+ end
85
+
86
+ path.open("w+") do |f|
87
+ html = fragment.to_html
88
+ html.gsub! '&lt;%=%20', '<%= '
89
+ html.gsub! '%20%&gt;', ' %>'
90
+ f.write(html)
91
+ end
92
+
93
+ FileUtils.mv(path, path.to_s + '.erb')
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,31 @@
1
+ class Stylish::Server
2
+ def self.start(options={})
3
+ require 'rack'
4
+ require 'rack/server'
5
+ require 'pry'
6
+
7
+
8
+ Stylish.config.apply(:library_path => Pathname(options.library_path))
9
+
10
+ config = Stylish.lib_root.join 'stylish', 'api.ru'
11
+
12
+ Rack::Server.start :config => config.to_s,
13
+ :Port => options.port,
14
+ :Host => options.host
15
+
16
+ end
17
+ end
18
+
19
+ command "server" do |c|
20
+ c.syntax = "stylish server [OPTIONS]"
21
+ c.description = "start the stylish server"
22
+
23
+ c.option '--port PORT', String, 'Which port to listen on?'
24
+ c.option '--host HOSTNAME', String, 'Which host to listen on?'
25
+ c.option '--library-path PATH', nil, 'Which library should we load?'
26
+
27
+ c.action do |args, options|
28
+ options.default(:port => 8090, :host => "0.0.0.0", :library_path => Dir.pwd)
29
+ Stylish::Server.start(options)
30
+ end
31
+ end
@@ -2,36 +2,42 @@ module Stylish
2
2
  class Configuration
3
3
  include Singleton
4
4
 
5
+ Defaults = {}
6
+
5
7
  def self.method_missing(meth, *args, &block)
8
+ binding.pry
9
+
6
10
  if instance.respond_to?(meth)
7
- instance.send(meth, *args, &block)
8
- else
9
- super
11
+ return instance.send meth, *args, &block
10
12
  end
13
+
14
+ nil
15
+ end
16
+
17
+ def respond_to?(*args)
18
+ current.send(:key?, args.first) || current.send(:respond_to?, *args)
11
19
  end
12
20
 
13
21
  def method_missing(meth, *args, &block)
14
- if config.respond_to?(meth)
15
- config.send(meth, *args, &block)
16
- else
17
- super
18
- end
22
+ return current[meth] if current.key?(meth)
23
+ super
19
24
  end
20
25
 
21
- def config
22
- @config ||= Hashie::Mash.new
26
+ def apply(object={})
27
+ current.tap {|c| c.merge!(object) }
23
28
  end
24
29
 
25
- def load_from_file(path)
26
- path = path.to_s.to_pathname
30
+ def public
31
+ current.slice(:library_path)
32
+ end
27
33
 
28
- payload = if path.extname == ".yml"
29
- YAML.load path.read
30
- elsif path.extname == ".json"
31
- JSON.parse path.read
34
+ def current
35
+ @current ||= Hashie::Mash.new(Defaults).tap do |c|
36
+ ENV.each do |key, value|
37
+ key = key.to_s.downcase.to_sym
38
+ c[key] = value
39
+ end
32
40
  end
33
-
34
- config.merge!(payload)
35
41
  end
36
42
  end
37
43
  end
@@ -1,10 +1,47 @@
1
- require 'hashie'
2
- require 'pathname'
1
+ require 'set'
2
+
3
+ class Set
4
+ def push(*args)
5
+ send(:<<, *args)
6
+ end
7
+ end
3
8
 
4
9
  class Hash
5
10
  def to_mash
6
11
  Hashie::Mash.new(self)
7
12
  end
13
+
14
+ def lookup_path dot_separated_path, create=false
15
+ parts = dot_separated_path.to_s.split('.')
16
+
17
+ parts.reduce(self) do |memo, part|
18
+ memo && memo[part.to_sym] ||= memo[part.to_s] || (create if create)
19
+ end
20
+ end
21
+
22
+ def make_path dot_separated_path, blank_value, overwrite=false
23
+ dot_separated_path = dot_separated_path.to_s
24
+ existing = lookup_path(dot_separated_path)
25
+
26
+ return existing if existing && !overwrite
27
+
28
+ parts = dot_separated_path.split(".")
29
+
30
+ current = self
31
+
32
+ if parts.length == 1
33
+ return self[parts.first] ||= blank_value
34
+ end
35
+
36
+ key = parts.pop
37
+
38
+ parts.each do |part|
39
+ current = current[part.to_sym] = current.delete(part.to_s) || {}
40
+ end
41
+
42
+ current[key.to_sym] = blank_value
43
+ lookup_path(dot_separated_path, false)
44
+ end
8
45
  end
9
46
 
10
47
  class Pathname
@@ -1,12 +1,4 @@
1
1
  module Stylish
2
2
  class Engine < ::Rails::Engine
3
- initializer 'stylish.developer_mode' do |app|
4
- sprockets = app.assets
5
-
6
- Stylish::Developer.config do |cfg|
7
- cfg.root = app.root
8
- cfg.environment = sprockets
9
- end
10
- end
11
3
  end
12
4
  end
@@ -0,0 +1,33 @@
1
+ module Stylish
2
+ class MiddlemanExtension < ::Middleman::Extension
3
+ def self.activate_stylish_extension
4
+ ::Middleman::Extensions.register(:stylish, Stylish::MiddlemanExtension)
5
+ end
6
+
7
+ option :theme, String, 'The name of the theme'
8
+
9
+ helpers do
10
+ def stylish
11
+ theme_root = Pathname(root).join('vendor').join extensions[:stylish].options[:theme]
12
+ @stylish ||= Stylish::Theme.new(theme_root)
13
+ end
14
+ end
15
+
16
+ def after_configuration
17
+ import_stylish_images_and_fonts
18
+ end
19
+
20
+ def import_stylish_images_and_fonts
21
+ environment = app.stylish.compiler
22
+ trusted_paths = environment.paths.select { |p| p.end_with?('images') || p.end_with?('fonts') }
23
+ trusted_paths.each do |load_path|
24
+ environment.each_entry(load_path) do |path|
25
+ if path.file? && !path.basename.to_s.start_with?('_')
26
+ logical_path = path.sub(/^#{load_path}/, '')
27
+ environment.imported_assets << Middleman::Sprockets::ImportedAsset.new(logical_path)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ module Stylish
2
+ module Rails
3
+ module ViewHelpers
4
+ # used by a layout to load the layout confiig it depends on.
5
+ # this will help build the dependency manifests properly.
6
+ #
7
+ # @param [String] the identifier for the layout
8
+ def load_theme_layout layout_identifier
9
+ end
10
+
11
+ # used by a page to load the components it will depend on. this
12
+ # helps us build the dependency manifests properly
13
+ def load_theme_components *component_identifiers
14
+ end
15
+
16
+ # sequentially renders every component for this page
17
+ def render_all_components
18
+ end
19
+
20
+ # renders a component. used in the page layouts.
21
+ def render_component component_identifier
22
+ end
23
+
24
+ # (see #javascript_imports_for)
25
+ def stylesheet_imports_for(page_identifier, options={})
26
+ end
27
+
28
+ # Returns a list of sprockets logical paths to satisfy
29
+ # the dependencies for a given stylish layout. These values can
30
+ # be used to dynamically generate JS content using ERB, or in a generator
31
+ # which will just output a sprockets manifest.
32
+ #
33
+ # This method will depend on the page having declared its dependency on
34
+ # a layout, and the components it depends on.
35
+ #
36
+ # @param [String] the slug for the stylish page which will be using the requested components
37
+ # @return [Array] a list of values which can be required in a sprockets manifest
38
+ def javascript_imports_for(page_identifier, options={})
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ module Stylish
2
+ class Theme
3
+ attr_accessor :root, :options, :manifest, :manifest_path, :compiler
4
+
5
+ def initialize(root, options={})
6
+ @options = options
7
+ @root = Pathname(root).expand_path
8
+ @manifest_path = options.fetch(:manifest_path) { root.join('stylish.yml') }
9
+
10
+ raise 'Manifest file not found. Please provide a root directory in which stylish.json can be found' unless manifest_path.exist?
11
+
12
+ @manifest = Hashie::Mash.new YAML.load_file(manifest_path)
13
+
14
+ setup_compiler rescue nil
15
+ end
16
+
17
+ def apply_manifest values={}
18
+ @manifest.merge!(values)
19
+ end
20
+
21
+ def dependency_paths
22
+ Array(manifest.dependency_paths).map {|path| root.join(path) }
23
+ end
24
+
25
+ def import_paths
26
+ Array(manifest.import_paths).map {|path| root.join(path) }
27
+ end
28
+
29
+ def setup_compiler
30
+ @compiler ||= Stylish.sprockets.dup.tap do |env|
31
+ dependency_paths.each {|p| env.append_path(p) }
32
+
33
+ if defined?(::Less)
34
+ (import_paths.map(&:to_s) - Less.paths.map(&:to_s)).each do |path|
35
+ Less.paths << path
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ module Stylish
2
+ class Theme::Component
3
+ end
4
+ end
@@ -0,0 +1,15 @@
1
+ module Stylish
2
+ class Theme::Exporter
3
+ attr_accessor :options, :source, :destination, :theme
4
+
5
+ def initialize(source, options={})
6
+ @source = Pathname(source)
7
+ @options = options
8
+ end
9
+
10
+ def run
11
+ replace_path_placeholders
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module Stylish
2
+ module Exporters
3
+ class Middleman < Stylish::Theme::Exporter
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,175 @@
1
+ require 'set'
2
+
3
+ module Stylish
4
+ class Theme::Importer
5
+
6
+ attr_reader :options, :exports, :paths, :config
7
+ attr_accessor :source, :destination, :name, :dependency_paths, :import_paths
8
+
9
+ def initialize(source, options={})
10
+ @source = Pathname(source)
11
+ @options = options
12
+
13
+
14
+ @name = options.fetch(:name, "stylish-theme")
15
+ @paths = {
16
+ output_path: Pathname(options[:output_path] || "#{ Dir.pwd }#{ name }")
17
+ }
18
+
19
+ clean if options[:clean]
20
+
21
+ @exports = {}.to_mash
22
+ @dependency_paths = Set.new
23
+ @import_paths = Set.new
24
+
25
+ @config = {}.to_mash
26
+
27
+ map_source_paths
28
+ prepare_output_paths
29
+
30
+ if(options[:run] == true)
31
+ run
32
+ end
33
+
34
+ end
35
+
36
+ DefaultRunCommands = %w(
37
+ move_common_javascript_dependencies_into_global_theme
38
+ move_common_stylesheet_dependencies_into_global_theme
39
+ extract_component_dependency_stylesheets
40
+ extract_component_dependency_javascripts
41
+ extract_component_markup
42
+ write_manifest
43
+ )
44
+
45
+ DefaultRunCommands.each do |meth|
46
+ define_method(meth) { raise NotImplementedError, "Importer class responsible for implementing: #{ meth }" } unless method_defined?(meth)
47
+ end
48
+
49
+ def run
50
+ DefaultRunCommands.each do |cmd|
51
+ send(cmd)
52
+ end
53
+ end
54
+
55
+ def manifest
56
+ exports.to_hash.merge(
57
+ dependency_paths: dependency_paths.to_a.map {|path| Pathname(path).relative_path_from(output_path).to_s },
58
+ import_paths: import_paths.to_a.map {|path| Pathname(path).relative_path_from(output_path).to_s }
59
+ )
60
+ end
61
+
62
+
63
+ def prepare
64
+ map_source_paths
65
+ prepare_output_paths
66
+ end
67
+
68
+ def prompts
69
+ {}
70
+ end
71
+
72
+ def clean
73
+ FileUtils.rm_rf(output_path)
74
+ end
75
+
76
+ def prompt_for_settings
77
+ if respond_to?(:ask)
78
+ prompts.each do |var, question|
79
+ self.options[var] = ask(question, String)
80
+ end
81
+ end
82
+ end
83
+
84
+ def method_missing meth, *args, &block
85
+ return paths[meth.to_sym] if meth.to_s.match(/_path$/) && paths.key?(meth.to_sym)
86
+ config.lookup_path(meth.to_s) || exports.lookup_path(meth.to_s) || super
87
+ end
88
+
89
+ def respond_to?(*args)
90
+ paths.key?(args.first.to_sym) || super
91
+ end
92
+
93
+ def set key, value
94
+ key = key.to_s
95
+ config.make_path(key, value)
96
+ end
97
+
98
+ def export key, value
99
+ key = key.to_s
100
+ exports.make_path(key, value, true)
101
+ end
102
+
103
+ def push key, value
104
+ key = key.to_s
105
+ exports.make_path(key, [])
106
+ exports.lookup_path(key) << value
107
+ end
108
+
109
+ def define_path identifier, value
110
+ value = Pathname(value)
111
+ @paths[identifier.to_sym] = value
112
+ value
113
+ end
114
+
115
+ def prepare_output_paths
116
+ FileUtils.mkdir_p output_path
117
+ FileUtils.mkdir_p components_output_path
118
+ FileUtils.mkdir_p layouts_output_path
119
+ FileUtils.mkdir_p javascripts_output_path.join('dependencies')
120
+ FileUtils.mkdir_p stylesheets_output_path.join('dependencies')
121
+ FileUtils.mkdir_p images_output_path
122
+ FileUtils.mkdir_p fonts_output_path
123
+
124
+ exports.component_categories.each do |category|
125
+ FileUtils.mkdir_p components_output_path.join(category.downcase.parameterize)
126
+ FileUtils.mkdir_p javascripts_output_path.join(category.downcase.parameterize)
127
+ FileUtils.mkdir_p stylesheets_output_path.join(category.downcase.parameterize)
128
+ end
129
+ end
130
+
131
+ def write_manifest
132
+ output_path.join("stylish.yml").open("w+") {|fh| fh.write(manifest.to_yaml) }
133
+ end
134
+
135
+ def components_output_path
136
+ output_path.join('views/components')
137
+ end
138
+
139
+ def layouts_output_path
140
+ output_path.join('views/layouts')
141
+ end
142
+
143
+ def stylesheets_output_path
144
+ output_path.join("assets/stylesheets/#{name}")
145
+ end
146
+
147
+ def javascripts_output_path
148
+ output_path.join("assets/javascripts/#{name}")
149
+ end
150
+
151
+ def images_output_path
152
+ output_path.join("assets/images/#{name}")
153
+ end
154
+
155
+ def fonts_output_path
156
+ output_path.join("assets/fonts/#{name}")
157
+ end
158
+
159
+ # Loads a specific importer configuration class
160
+ #
161
+ # @param [String] which importer config to use
162
+ # will reference a .rb file in the configured importer definition paths
163
+ def self.load_by_type(type)
164
+ importer_file = Stylish.lib_root
165
+ .join("stylish/theme/importers")
166
+ .join(type.to_s + '.rb')
167
+
168
+ raise "Invalid Importer" unless importer_file.exist?
169
+
170
+ require(importer_file)
171
+
172
+ (Stylish::Importers.const_get("#{ type }_importer".camelize) rescue nil) || (Stylish::Importers.const_get("#{ type }".camelize))
173
+ end
174
+ end
175
+ end