prigner 0.1.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 (52) hide show
  1. data/CHANGELOG +248 -0
  2. data/COPYING +21 -0
  3. data/README.mkd +90 -0
  4. data/Rakefile +211 -0
  5. data/bin/prign +65 -0
  6. data/lib/prigner/builder.rb +51 -0
  7. data/lib/prigner/cli/copy.rb +0 -0
  8. data/lib/prigner/cli/list.rb +54 -0
  9. data/lib/prigner/cli/new.rb +96 -0
  10. data/lib/prigner/cli.rb +102 -0
  11. data/lib/prigner/extensions.rb +67 -0
  12. data/lib/prigner/model.rb +97 -0
  13. data/lib/prigner/project.rb +71 -0
  14. data/lib/prigner/template.rb +148 -0
  15. data/lib/prigner.rb +186 -0
  16. data/prigner.gemspec +120 -0
  17. data/share/templates/ruby/default/models/CHANGELOG +13 -0
  18. data/share/templates/ruby/default/models/COPYING +21 -0
  19. data/share/templates/ruby/default/models/README.rdoc +4 -0
  20. data/share/templates/ruby/default/models/module.rb +5 -0
  21. data/share/templates/ruby/default/specfile +16 -0
  22. data/share/templates/ruby/gem/models/CHANGELOG +13 -0
  23. data/share/templates/ruby/gem/models/COPYING +21 -0
  24. data/share/templates/ruby/gem/models/README.mkd +5 -0
  25. data/share/templates/ruby/gem/models/Rakefile +213 -0
  26. data/share/templates/ruby/gem/models/empty_test.rb +16 -0
  27. data/share/templates/ruby/gem/models/gemspec +67 -0
  28. data/share/templates/ruby/gem/models/module.rb +71 -0
  29. data/share/templates/ruby/gem/specfile +20 -0
  30. data/test/builder_test.rb +38 -0
  31. data/test/fixtures/model.rb.erb +12 -0
  32. data/test/fixtures/templates/shared/ruby/default/README.mkd +2 -0
  33. data/test/fixtures/templates/shared/ruby/default/models/README.mkd +2 -0
  34. data/test/fixtures/templates/shared/ruby/default/models/Rakefile +7 -0
  35. data/test/fixtures/templates/shared/ruby/default/models/empty_test.rb +21 -0
  36. data/test/fixtures/templates/shared/ruby/default/models/module.rb +6 -0
  37. data/test/fixtures/templates/shared/ruby/default/specfile +18 -0
  38. data/test/fixtures/templates/user/bash/default/specfile +0 -0
  39. data/test/fixtures/templates/user/ruby/program/models/README.erb +2 -0
  40. data/test/fixtures/templates/user/ruby/program/models/cli.rb.erb +5 -0
  41. data/test/fixtures/templates/user/ruby/program/models/module.rb.erb +6 -0
  42. data/test/fixtures/templates/user/ruby/program/models/program.rb.erb +3 -0
  43. data/test/fixtures/templates/user/ruby/program/specfile +18 -0
  44. data/test/fixtures/templates/user/vim/default/specfile +0 -0
  45. data/test/fixtures/templates/user/vim/plugin/specfile +0 -0
  46. data/test/fixtures/templates/user/vim/syntax/specfile +0 -0
  47. data/test/helpers.rb +22 -0
  48. data/test/model_test.rb +82 -0
  49. data/test/project_test.rb +46 -0
  50. data/test/spec_test.rb +64 -0
  51. data/test/template_test.rb +88 -0
  52. metadata +142 -0
@@ -0,0 +1,54 @@
1
+ # Copyright (c) 2010 Codigorama, Hallison Batista
2
+
3
+ require "prigner"
4
+
5
+ program = :prign
6
+ command = File.basename(__FILE__, ".rb")
7
+ @options = {}
8
+
9
+ def templates_by_namespace(indent)
10
+ templates = Prigner::Template.all
11
+ message = []
12
+ templates.map do |namespace, list|
13
+ message << "* #{namespace}"
14
+ list.map do |template|
15
+ message << ("#{indent}> %-16s %s" % [template.name, template.spec.description])
16
+ end
17
+ end
18
+ message.join("\n")
19
+ end
20
+
21
+ def show(message)
22
+ puts "#{Prigner::Version}"
23
+ puts "\n#{message}"
24
+ end
25
+
26
+ begin
27
+
28
+ ARGV.options do |arguments|
29
+
30
+ arguments.summary_indent = " "
31
+ arguments.summary_width = 24
32
+ arguments.banner = <<-end_banner.gsub /^[ ]{4}/, ''
33
+ #{Prigner::Version}
34
+
35
+ List all templates.
36
+
37
+ Usage:
38
+ #{program} #{command}
39
+
40
+ end_banner
41
+
42
+ if ARGV.empty?
43
+ show(templates_by_namespace(arguments.summary_indent))
44
+ exit 0
45
+ else
46
+ arguments.parse!
47
+ end
48
+ end
49
+
50
+ rescue => error
51
+ puts "#{program}: #{command}: #{error.message} (#{error.class})"
52
+ exit 1
53
+ end
54
+
@@ -0,0 +1,96 @@
1
+ # Copyright (c) 2010 Hallison Batista
2
+
3
+ require "prigner"
4
+
5
+ program = :prign
6
+ command = File.basename(__FILE__, ".rb")
7
+
8
+ begin
9
+ ARGV.options do |arguments|
10
+
11
+ arguments.summary_indent = " "
12
+ arguments.summary_width = 24
13
+ arguments.banner = <<-end_banner.gsub /^[ ]{6}/, ''
14
+ #{Prigner::Version}
15
+
16
+ Usage:
17
+ #{program} #{command} <namespace>[:template] <path> [options]
18
+
19
+ Templates:
20
+ #{Prigner::CLI.templates.sort.join("\n" + arguments.summary_indent)}
21
+
22
+ end_banner
23
+
24
+ unless ARGV.empty?
25
+ name = ARGV.shift
26
+ path = ARGV.shift unless name.nil?
27
+
28
+ template = Prigner::Template.load(*name.split(":"))
29
+
30
+ if template
31
+ arguments.banner = <<-end_banner.gsub /^[ ]{10}/, ''
32
+ #{Prigner::Version}
33
+
34
+ Usage:
35
+ #{program} #{command} #{template.mask} <path> [options]
36
+
37
+ end_banner
38
+
39
+ if template.options
40
+ arguments.separator "Options:"
41
+
42
+ template.options.members.map do |name|
43
+ option = template.options[name]
44
+ arguments.on("-#{name[0..0]}", "--#{name}", nil, option.description) do
45
+ option.enabled = true
46
+ end
47
+ end
48
+ end
49
+
50
+ else
51
+ raise RuntimeError, "unable to load template '#{name}'"
52
+ end
53
+ end
54
+
55
+ unless path
56
+ puts arguments
57
+ exit 0
58
+ end
59
+
60
+ arguments.parse!
61
+
62
+ project = Prigner::Project.new(path)
63
+ builder = Prigner::Builder.new(project, template)
64
+
65
+ status = Prigner::CLI::Status.new
66
+
67
+ status.setup do
68
+ state :success, :created
69
+ state :failure, :fail
70
+ state :error, :error
71
+ format :title, "\n* %-74s"
72
+ format :info, " %-69s [%7s]"
73
+ end
74
+
75
+ puts Prigner::Version
76
+
77
+ status.start "Creating project path" do
78
+ builder.make_project_path
79
+ end
80
+
81
+ status.start "Creating directories" do
82
+ builder.make_project_directories
83
+ end
84
+
85
+ status.start "Writing files" do
86
+ builder.make_project_files
87
+ end
88
+
89
+ end
90
+
91
+ rescue => error
92
+ puts "#{program}: #{command}: #{error.message} (#{error.class})"
93
+ puts "Try '#{program} #{command} -h' or '#{program} #{command} --help' for more information."
94
+ exit 1
95
+ end
96
+
@@ -0,0 +1,102 @@
1
+ # == Command-Line Interface
2
+ #
3
+ # This module look a command placed in CLI directory and run it.
4
+ module Prigner::CLI
5
+
6
+ require "rbconfig"
7
+
8
+ # Ruby VM installed in OS.
9
+ def self.ruby
10
+ File.join(*::RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) +
11
+ ::RbConfig::CONFIG["EXEEXT"]
12
+ end
13
+
14
+ # The CLI path.
15
+ def self.path
16
+ "#{Prigner::ROOT}/lib/prigner/cli"
17
+ end
18
+
19
+ # List all templates as commands.
20
+ def self.templates
21
+ Prigner::Template.all.map do |namespace, templates|
22
+ templates.map{ |template| template.mask }
23
+ end.flatten
24
+ end
25
+
26
+ # List of commands placed in <tt>lib/prigner/cli/</tt>.
27
+ def self.commands
28
+ Dir["#{path}/*.rb"].map do |source|
29
+ File.basename(source, ".rb")
30
+ end.sort
31
+ end
32
+
33
+ # Source command placed in CLI directory.
34
+ def self.source(command)
35
+ "#{path}/#{command}.rb"
36
+ end
37
+
38
+ # Look command in *CLI* directory and execute (by exec).
39
+ def self.run(*args)
40
+ command = args.shift if commands.include? args.first
41
+ raise RuntimeError, "unknown command '#{args.first}'" unless command
42
+ rubyopt = "-I#{Prigner::ROOT}/lib"
43
+ exec ruby, rubyopt, source(command), *args
44
+ end
45
+
46
+ class Status
47
+
48
+ attr_reader :formats
49
+
50
+ attr_reader :count
51
+
52
+ attr_reader :states
53
+
54
+ attr_reader :current
55
+
56
+ def initialize
57
+ @states = {
58
+ :success => :done,
59
+ :failure => :fail,
60
+ :error => :err
61
+ }
62
+ @formats = {
63
+ :title => "\n* %-74s",
64
+ :info => "\n %-70s [%4s]"
65
+ }
66
+ @count = {}
67
+ @states.keys.each{|k| @count[k] = 0 }
68
+ end
69
+
70
+ def setup(&block)
71
+ instance_eval(&block)
72
+ end
73
+
74
+ def state(key, value)
75
+ return unless @states.has_key?key
76
+ @states[key] = value
77
+ @count[key] = 0
78
+ end
79
+
80
+ def format(key, format)
81
+ @formats[key] = "#{format}\n" if @formats.has_key?key
82
+ end
83
+
84
+ def increment!
85
+ @count[@current] += 1
86
+ end
87
+
88
+ def start(title, &block)
89
+ printf @formats[:title], title
90
+ hash = yield block
91
+ hash.each do |message, status|
92
+ @current = status ? :success : :failure
93
+ printf @formats[:info], message, @states[@current]
94
+ increment!
95
+ end
96
+ end
97
+
98
+ end
99
+
100
+ end
101
+
102
+
@@ -0,0 +1,67 @@
1
+ class Symbol
2
+
3
+ # Method for comparison between symbols.
4
+ def <=>(other)
5
+ self.to_s <=> other.to_s
6
+ end
7
+
8
+ end
9
+
10
+ class Hash
11
+
12
+ alias has? has_key?
13
+
14
+ # Only symbolize all keys, including all key in sub-hashes.
15
+ def symbolize_keys
16
+ return self.clone if self.empty?
17
+ self.inject({}) do |hash, (key, value)|
18
+ hash[key.to_sym] = if value.kind_of? Hash
19
+ value.symbolize_keys
20
+ else
21
+ value
22
+ end
23
+ hash
24
+ end
25
+ end
26
+
27
+ # Set instance variables by key and value only if object respond
28
+ # to access method for variable.
29
+ def instance_variables_set_to(object)
30
+ collect do |variable, value|
31
+ object.instance_variable_set("@#{variable}", value) if object.respond_to? variable
32
+ end
33
+ object
34
+ end
35
+
36
+ # Convert to Struct including all values that are Hash class.
37
+ def to_struct
38
+ keys = self.keys.sort
39
+ members = keys.map{ |key| key.to_sym }
40
+ Struct.new(*members).new(*keys.map do |key|
41
+ if self[key].kind_of? Hash
42
+ self[key].to_struct
43
+ else
44
+ self[key]
45
+ end
46
+ end) unless self.empty?
47
+ end
48
+
49
+ end
50
+
51
+ class Struct
52
+
53
+ # Public bind.
54
+ def binding
55
+ super
56
+ end
57
+
58
+ end
59
+
60
+ class Pathname
61
+
62
+ def chpath(source, path)
63
+ self.path.gsub(%r{#{source}}, path).to_path
64
+ end
65
+
66
+ end
67
+
@@ -0,0 +1,97 @@
1
+ # == Model
2
+ #
3
+ # This class implements several methods for load a model file of template.
4
+ #
5
+ # Basically behaviour as parser based in a ERB file placed in +models+
6
+ # directory of a Template.
7
+ #
8
+ # Example:
9
+ #
10
+ # # model: jedi/knight
11
+ # class <%=name.capitalize%>
12
+ # <%for attribute in attributes%>
13
+ # attr_accessor :<%=attribute%>
14
+ # <%end%>
15
+ # def initialize(<%=attributes.join(",")%>)
16
+ # <%for attribute in attributes%>
17
+ # @<%=attribute%> = <%attribute%>
18
+ # <%end%>
19
+ # end
20
+ #
21
+ # def to_s
22
+ # <%for attribute in attributes%>
23
+ # "\n<%=attribute.capitalize%>: #{<%=attribute%>}"
24
+ # <%end%>
25
+ # end
26
+ # end
27
+ #
28
+ # <%=name%> = <%=name.capitalize%>.new("Yoda", "Jedi Grand Master")
29
+ # # end model
30
+ #
31
+ # $> model = Pringer::Model.new "jedi/knight", {
32
+ # :name => "knight",
33
+ # :attributes => [ :name, :position ]
34
+ # }
35
+ # $> model.build!
36
+ # $> model.contents
37
+ # => class Knight
38
+ # =>
39
+ # => attr_accessor :name
40
+ # => attr_accessor :position
41
+ # =>
42
+ # => def initialize(name,position)
43
+ # =>
44
+ # => @name = name
45
+ # => @position = position
46
+ # =>
47
+ # => end
48
+ # =>
49
+ # => def to_s
50
+ # =>
51
+ # => "\nName: #{name}"
52
+ # => "\nPosition: #{position}"
53
+ # =>
54
+ # => end
55
+ # => end
56
+ # =>
57
+ # => knight = Knight.new("Yoda", "Jedi Grand Master")
58
+ #
59
+ class Prigner::Model
60
+
61
+ # Model path file.
62
+ attr_reader :path
63
+
64
+ # Parsed contents.
65
+ attr_reader :contents
66
+
67
+ # Value bindings.
68
+ attr_accessor :binder
69
+
70
+ # Result file written
71
+ attr_reader :file_written
72
+
73
+ # Initializes a model passing all attributes by Hash.
74
+ def initialize(path, binder = {})
75
+ @path = Pathname.new(path)
76
+ @binder = (binder.kind_of? Hash) ? binder.to_struct : binder
77
+ end
78
+
79
+ # Build model contents.
80
+ def build!
81
+ require "erb"
82
+ @contents = ::ERB.new(@path.read).result(binder.binding)
83
+ end
84
+
85
+ # Write contents into file.
86
+ def write(file)
87
+ @file_written = file
88
+ file = Pathname.new(file)
89
+ file.dirname.mkpath
90
+ file.open "w+" do |output|
91
+ output << self.build!
92
+ end
93
+ self
94
+ end
95
+
96
+ end
97
+
@@ -0,0 +1,71 @@
1
+ # == Project
2
+ #
3
+ # This class is a simple implementation of a tree of directories. It is used
4
+ # in model files.
5
+ class Prigner::Project
6
+
7
+ # Project name.
8
+ attr_reader :name
9
+
10
+ alias project name
11
+
12
+ # Project files.
13
+ attr_reader :path
14
+
15
+ # Project timestamp
16
+ attr_reader :timestamp
17
+
18
+ # Initialize a new project directory path.
19
+ def initialize(path)
20
+ @path = File.expand_path(path =~ /^\/.*/ ? path : "#{Dir.pwd}/#{path}")
21
+ @name = File.basename(@path)
22
+ @timestamp = DateTime.now
23
+ end
24
+
25
+ def name_splited
26
+ @name.split(/[-_]/)
27
+ end
28
+
29
+ def namespace(joiner = "::")
30
+ name_splited.join(joiner)
31
+ end
32
+
33
+ def upper_camel_case_namespace(joiner = "::")
34
+ name_splited.map{ |str| str.capitalize }.join(joiner)
35
+ end
36
+
37
+ def lower_camel_case_namespace(joiner = "::")
38
+ return name if name_splited.size == 0
39
+ name_splited[1..-1].map do |str|
40
+ str.capitalize
41
+ end.unshift(name_splited.first).join(joiner)
42
+ end
43
+
44
+ def upper_case_namespace(joiner = "::")
45
+ upper_camel_case_namespace(joiner).upcase
46
+ end
47
+
48
+ def lower_case_namespace(joiner = "::")
49
+ upper_camel_case_namespace(joiner).downcase
50
+ end
51
+
52
+ def upper_camel_case_name
53
+ upper_camel_case_namespace(nil)
54
+ end
55
+
56
+ alias class_name upper_camel_case_name
57
+
58
+ def lower_camel_case_name
59
+ lower_camel_case_namespace(nil)
60
+ end
61
+
62
+ def user
63
+ Etc.getpwnam(Etc.getlogin)
64
+ end
65
+
66
+ def author
67
+ self.user.gecos.split(",").first
68
+ end
69
+
70
+ end # Project
71
+
@@ -0,0 +1,148 @@
1
+ # == Project Template
2
+ #
3
+ # This class implements several methods for load a template for a new project.
4
+ # Basically, a template is based in an YAML file for +specfile+ and a directory
5
+ # containing all files to be parsed.
6
+ #
7
+ # The template tree is based in following structure:
8
+ #
9
+ # namespace
10
+ # `-- template
11
+ # |-- models
12
+ # `-- specfile
13
+ #
14
+ # See Spec class for more information about mandatory attributes for draw your
15
+ # project using +specfile+.
16
+ class Prigner::Template
17
+
18
+ # Namespace of template.
19
+ attr_reader :namespace
20
+
21
+ # Name of template.
22
+ attr_reader :name
23
+
24
+ # List of models.
25
+ attr_reader :models
26
+
27
+ # List of directories that will created in project tree.
28
+ attr_reader :directories
29
+
30
+ # List of options (see Spec#options).
31
+ attr_reader :options
32
+
33
+ # Path to template.
34
+ attr_reader :path
35
+
36
+ # Specifications
37
+ attr_reader :spec
38
+
39
+ # Initialize a template using a path.
40
+ #
41
+ # Example:
42
+ #
43
+ # template = Prigner::Template.new "path/to/template"
44
+ #
45
+ # The template initialization will search the +specfile+ in path passed as
46
+ # argument (<tt>path/to/template/specfile</tt>) for Spec attributes.
47
+ def initialize(path)
48
+ @path = Pathname.new(path).tap{ |check| check.stat }
49
+ @namespace = @path.parent.basename.to_s
50
+ @name = @path.basename.to_s
51
+ initialize_specfile
52
+ initialize_options
53
+ initialize_directories
54
+ initialize_models
55
+ rescue Exception => error
56
+ raise RuntimeError, error.message
57
+ end
58
+
59
+ # Mask for presentation of template.
60
+ def mask
61
+ @name == "default" ? @namespace : "#{@namespace}:#{@name}"
62
+ end
63
+
64
+ # Load template from shared directories. The shared path set the home user
65
+ # directory and Prigner::Template shared files.
66
+ def self.load(namespace, template = :default)
67
+ shared_path.map do |source|
68
+ path = "#{source}/#{namespace}/#{template}"
69
+ return new(path) if File.exist? path
70
+ end
71
+ nil
72
+ end
73
+
74
+ # Look at user home and template shared path.
75
+ def self.shared_path
76
+ user_home_templates = File.join(user_home_basedir, "templates")
77
+ [ user_home_templates, "#{Prigner::ROOT}/share/templates" ]
78
+ end
79
+
80
+ # User home.
81
+ def self.user_home
82
+ File.expand_path "~"
83
+ rescue
84
+ if File::ALT_SEPARATOR then
85
+ "C:/"
86
+ else
87
+ "/"
88
+ end
89
+ end
90
+
91
+ # User home base directory for Prigner files.
92
+ def self.user_home_basedir
93
+ File.join(user_home, ".prigner")
94
+ end
95
+
96
+ # Return all template paths placed in shared user or in project base
97
+ # directory.
98
+ def self.all_template_paths
99
+ shared_path.map do |source|
100
+ Dir.glob("#{source}/*/*")
101
+ end.flatten.compact
102
+ end
103
+
104
+ # All templates grouped by namespace.
105
+ def self.all
106
+ all_template_paths.map do |path|
107
+ new(path)
108
+ end.inject({}) do |group, template|
109
+ group[template.namespace] ||= []
110
+ group[template.namespace] << template
111
+ group
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ # Return the specfile placed in template path.
118
+ def specfile
119
+ Dir["#{@path}/[Ss]pecfile"].first
120
+ end
121
+
122
+ # Load +specfile+ placed in template path.
123
+ def initialize_specfile
124
+ @spec = Prigner::Spec.load(specfile)
125
+ end
126
+
127
+ # Initialize options.
128
+ def initialize_options
129
+ @options = @spec.options.inject({}) do |options, (name, desc)|
130
+ options[name] = { :enabled => nil, :description => desc }
131
+ options
132
+ end.to_struct if @spec.options
133
+ end
134
+
135
+ def initialize_directories
136
+ @directories = @spec.directories
137
+ end
138
+
139
+ def initialize_models
140
+ @models = @spec.files.inject({}) do |models, (source, file)|
141
+ model = Prigner::Model.new(@path.join("models", source))
142
+ models[model] = file ? file : source
143
+ models
144
+ end if @spec.files
145
+ end
146
+
147
+ end
148
+