technologist 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2378b10dae9e8be8a3a908670493e515b460d703
4
- data.tar.gz: 97fa566a3d4d81b5b19a6dbac8b22520cf066299
3
+ metadata.gz: 28a3182ef7ef764fb494a4531ce1060151420944
4
+ data.tar.gz: c895b0a90fb072bc2b35545e335d88ed2549b422
5
5
  SHA512:
6
- metadata.gz: c9fe126a1345093b72fb02131f99b09d533acd2fe34db60102062cf66b77f5f931497bd5d2506b0726a7e756188a002b27916d502c087f1034ba13298661c185
7
- data.tar.gz: 1f447477bbba87d7d84caa8d9ec7dfafbc7cc283772451cae00912ea17969155779eeb2d40021e67adec77598f782c06fb96223cef7a22abd6daf533fc8a867c
6
+ metadata.gz: 39af217cc9fd4cd46e5d4681550603f14a7499b846df3d2f9174970b7835df4205f22da59502059a15d6f9149e4c6481ede1196cab595df979b1645bd16e273d
7
+ data.tar.gz: 641c27183e4f540c84ee6bc1c28ecad335bbe8d5817693542180ba865096692933cc9129ed9c6aea64ce280332382ea0fed71448649494908f4a68cda9a43d28
@@ -1,42 +1,39 @@
1
- require 'yaml'
1
+ require 'technologist/yaml_parser'
2
2
 
3
3
  module Technologist
4
4
  class FrameworkDetector
5
5
  FRAMEWORK_RULES = File.expand_path('../frameworks.yml', __FILE__)
6
6
 
7
+ attr_reader :repository, :yaml_parser
8
+
7
9
  def initialize(repository)
8
10
  @repository = repository
11
+ @yaml_parser = YamlParser.new(FRAMEWORK_RULES)
9
12
  end
10
13
 
11
14
  def primary_frameworks
12
15
  matched_frameworks.map do |technology|
13
16
  # it's either the primary value defined in the yaml
14
17
  # or the technology itself
15
- rules[technology]['primary'] || technology
18
+ yaml_parser.rules[technology]['primary'] || technology
16
19
  end.uniq
17
20
  end
18
21
 
19
22
  def secondary_frameworks
20
23
  matched_frameworks.map do |technology|
21
24
  # it's a secondary if a primary is defined in the yaml
22
- rules[technology]['primary'] && technology
25
+ yaml_parser.rules[technology]['primary'] && technology
23
26
  end.compact
24
27
  end
25
28
 
26
29
  private
27
30
 
28
- attr_reader :repository
29
-
30
- def rules
31
- @rules ||= YAML.load_file(FRAMEWORK_RULES)
32
- end
33
-
34
31
  def matched_frameworks
35
32
  @frameworks ||=
36
33
  begin
37
- rules.map do |technology, definition|
34
+ yaml_parser.rules.map do |technology, definition|
38
35
  definition['rules'].map do |rule|
39
- if rule.matches?(technology, repository)
36
+ if rule.matches?(repository)
40
37
  technology
41
38
  end
42
39
  end
@@ -1,91 +1,142 @@
1
1
  Rails:
2
2
  rules:
3
- - !ruby/object:GemRule {}
4
- - !ruby/object:GemRule { gem_name: 'jrails' }
5
- - !ruby/object:FileContentRule { file_name: 'boot.rb', file_content_pattern: 'Rails.boot!' }
3
+ - Gem
4
+ - Gem:
5
+ gem_name: 'jrails'
6
+ - FileContent:
7
+ file_name: 'boot.rb'
8
+ file_content_pattern: 'Rails.boot!'
6
9
 
7
10
  Locomotive:
8
11
  rules:
9
- - !ruby/object:GemRule { gem_name: 'locomotivecms_wagon' }
12
+ - Gem:
13
+ gem_name: 'locomotivecms_wagon'
10
14
  primary: Rails
11
15
 
12
16
  Magnolia:
13
17
  rules:
14
- - !ruby/object:FileContentRule { file_name: 'pom.xml', file_content_pattern: '<magnoliaVersion>' }
18
+ - FileContent:
19
+ file_name: 'pom.xml'
20
+ file_content_pattern: '<magnoliaVersion>'
15
21
 
16
22
  Sinatra:
17
23
  rules:
18
- - !ruby/object:FileContentRule { file_name: 'config.ru', file_content_pattern: 'run Sinatra::Application' }
19
- - !ruby/object:GemRule {}
24
+ - FileContent:
25
+ file_name: 'config.ru'
26
+ file_content_pattern: 'run Sinatra::Application'
27
+ - Gem
20
28
 
21
29
  Dashing:
22
30
  rules:
23
- - !ruby/object:GemRule {}
31
+ - Gem
24
32
  primary: Sinatra
25
33
 
26
34
  Middleman:
27
35
  rules:
28
- - !ruby/object:GemRule {}
36
+ - Gem
29
37
 
30
38
  Meteor:
31
39
  rules:
32
- - !ruby/object:DirectoryPresenceRule { directory_name: '.meteor' }
40
+ - DirectoryPresence:
41
+ directory_name: '.meteor'
33
42
 
34
43
  Spree:
35
44
  rules:
36
- - !ruby/object:GemRule {}
37
- - !ruby/object:FileContentRule { file_name: 'boot.rb', file_content_pattern: 'Spree.boot!' }
45
+ - Gem
46
+ - FileContent:
47
+ file_name: 'boot.rb'
48
+ file_content_pattern: 'Spree.boot!'
38
49
  primary: Rails
39
50
 
40
51
  Wordpress:
41
52
  rules:
42
- - !ruby/object:FilePresenceRule { file_name: 'wp-settings.php' }
53
+ - FilePresence:
54
+ file_name: 'wp-settings.php'
43
55
 
44
56
  Volt:
45
57
  rules:
46
- - !ruby/object:GemRule {}
58
+ - Gem
47
59
 
48
60
  Ionic:
49
61
  rules:
50
- - !ruby/object:FilePresenceRule { file_name: 'ionic.project' }
62
+ - FilePresence:
63
+ file_name: 'ionic.project'
51
64
 
52
65
  Node:
53
66
  rules:
54
- - !ruby/object:FileContentRule
67
+ - FileContent:
55
68
  file_name: 'package.json'
56
- file_content_pattern: '"engines":\s*{\s*"node":'
69
+ file_content_pattern: '^\s*"node":'
57
70
 
58
71
  Hoodie:
59
72
  rules:
60
- - !ruby/object:FileContentRule
73
+ - FileContent:
61
74
  file_name: 'package.json'
62
75
  file_content_pattern: '"hoodie":\s*{'
63
76
  primary: Node
64
77
 
65
78
  PrestaShop:
66
79
  rules:
67
- - !ruby/object:FileContentRule { file_name: 'init.php', file_content_pattern: 'PrestaShop' }
80
+ - FileContent:
81
+ file_name: 'init.php'
82
+ file_content_pattern: 'PrestaShop'
68
83
 
69
84
  Cordova:
70
85
  rules:
71
- - !ruby/object:FileContentRule
86
+ - FileContent:
72
87
  file_name: 'config.xml'
73
88
  file_content_pattern: '\bxmlns:cdv="http://cordova.apache.org/ns/.+"'
74
89
 
75
90
  iOS:
76
91
  rules:
77
- - !ruby/object:FileContentRule
92
+ - FileContent:
78
93
  file_name: 'project.pbxproj'
79
94
  file_content_pattern: '\n\s*SDKROOT = iphoneos;\n'
80
95
 
81
96
  Refinery CMS:
82
97
  rules:
83
- - !ruby/object:GemRule { gem_name: 'refinerycms' }
98
+ - Gem:
99
+ gem_name: 'refinerycms'
84
100
  primary: Rails
85
101
 
86
102
  Rack:
87
103
  rules:
88
- - !ruby/object:GemRule { gem_name: 'rack' }
89
- - !ruby/object:FileContentRule
90
- file_name: config.ru
104
+ - Gem:
105
+ gem_name: 'rack'
106
+ - FileContent:
107
+ file_name: 'config.ru'
91
108
  file_content_pattern: 'run Proc\.new { \|env\|'
109
+
110
+ Magento:
111
+ rules:
112
+ - FileContent:
113
+ file_name: 'composer.json'
114
+ file_content_pattern: '^\s*"magento":\s*{\s*$'
115
+
116
+ Vaadin:
117
+ rules:
118
+ - FileContent:
119
+ file_name: 'pom.xml'
120
+ file_content_pattern: '<version>\${vaadin.base.version}</version>'
121
+
122
+ Spring:
123
+ rules:
124
+ - MavenPlugin:
125
+ plugin_name: 'org.springframework'
126
+ - MavenPlugin:
127
+ plugin_name: '${spring.groupId}'
128
+
129
+ Felix:
130
+ rules:
131
+ - MavenPlugin:
132
+ plugin_name: 'org.apache.felix'
133
+
134
+ GWT:
135
+ rules:
136
+ - MavenPlugin:
137
+ plugin_name: 'com.google.gwt'
138
+
139
+ Jersey:
140
+ rules:
141
+ - MavenPlugin:
142
+ plugin_name: 'com.sun.jersey'
@@ -12,17 +12,6 @@ module Technologist
12
12
  repository.head.target.tree
13
13
  end
14
14
 
15
- # Returns the file content.
16
- #
17
- # @param file_name [String] the file name
18
- #
19
- # @return [String] The content of the file or nil if the file cannot be found.
20
- def file_content(file_name)
21
- file = find_blob(file_name)
22
-
23
- file.content if file
24
- end
25
-
26
15
  # Recursively searches for the blob identified by `blob_name`
27
16
  # in all subdirectories in the repository. A blob is usually either
28
17
  # a file or a directory.
@@ -31,18 +20,26 @@ module Technologist
31
20
  # @param current_tree [Rugged::Tree] the git directory tree in which to look for the blob.
32
21
  # Defaults to the root tree (see `#root_tree`).
33
22
  #
23
+ # @yield [Rugged::Blob] Yields the found blob to an optional block.
24
+ # If the block returns `true` it means that the file is found and
25
+ # recursion is stopped. If the return value is `false`, the resursion continues.
26
+ #
34
27
  # @return [Rugged::Blob] The blob blob or nil if it cannot be found.
35
- def find_blob(blob_name, current_tree = root_tree)
28
+ def find_blob(blob_name, current_tree = root_tree, &block)
36
29
  blob = current_tree[blob_name]
37
30
 
38
31
  if blob
39
- repository.lookup(blob[:oid])
40
- else
41
- current_tree.each_tree do |sub_tree|
42
- blob = find_blob(blob_name, repository.lookup(sub_tree[:oid]))
43
- break blob if blob
32
+ blob = repository.lookup(blob[:oid])
33
+ if !block_given? || yield(blob)
34
+ return blob
44
35
  end
45
36
  end
37
+
38
+ # recurse
39
+ current_tree.each_tree do |sub_tree|
40
+ blob = find_blob(blob_name, repository.lookup(sub_tree[:oid]), &block)
41
+ break blob if blob
42
+ end
46
43
  end
47
44
 
48
45
  # Looks for a directory and returns true when the directory
@@ -64,5 +61,20 @@ module Technologist
64
61
  def file_exists?(file_name)
65
62
  !!find_blob(file_name)
66
63
  end
64
+
65
+ # Looks for a file and yields the file content to
66
+ # the block (if the file can be found). The block's
67
+ # return value is used to determine if we're done
68
+ # searching. If the return value is `false`, we
69
+ # keep searching for the file.
70
+ #
71
+ # @param file_name [String] the file name
72
+ #
73
+ # @return [Boolean] true if the file can be found.
74
+ def file_with_content_exists?(file_name)
75
+ !!find_blob(file_name) do |file|
76
+ yield file.content
77
+ end
78
+ end
67
79
  end
68
80
  end
@@ -3,7 +3,7 @@ require 'technologist/rules/rule'
3
3
  class DirectoryPresenceRule < Rule
4
4
  attr_accessor :directory_name
5
5
 
6
- def matches?(framework_name, repository)
6
+ def matches?(repository)
7
7
  repository.directory_exists?(directory_name)
8
8
  end
9
9
  end
@@ -3,7 +3,9 @@ require 'technologist/rules/rule'
3
3
  class FileContentRule < Rule
4
4
  attr_accessor :file_name, :file_content_pattern
5
5
 
6
- def matches?(framework_name, repository)
7
- !!(repository.file_content(file_name) =~ /#{file_content_pattern}/)
6
+ def matches?(repository)
7
+ !!repository.file_with_content_exists?(file_name) do |content|
8
+ content =~ /#{file_content_pattern}/
9
+ end
8
10
  end
9
11
  end
@@ -3,7 +3,7 @@ require 'technologist/rules/rule'
3
3
  class FilePresenceRule < Rule
4
4
  attr_accessor :file_name
5
5
 
6
- def matches?(framework_name, repository)
6
+ def matches?(repository)
7
7
  repository.file_exists?(file_name)
8
8
  end
9
9
  end
@@ -0,0 +1,13 @@
1
+ require 'technologist/rules/rule'
2
+ require 'nokogiri'
3
+
4
+ class FileXmlContentRule < Rule
5
+ attr_accessor :file_name, :css_selector
6
+
7
+ def matches?(repository)
8
+ !!repository.file_with_content_exists?(file_name) do |content|
9
+ xml = Nokogiri::XML(content)
10
+ xml.css(css_selector).any?
11
+ end
12
+ end
13
+ end
@@ -3,11 +3,11 @@ require 'technologist/rules/file_content_rule'
3
3
  class GemRule < FileContentRule
4
4
  attr_accessor :gem_name
5
5
 
6
- def matches?(framework_name, repository)
6
+ def initialize(framework, attributes = {})
7
+ super
8
+
7
9
  self.file_name = 'Gemfile'
8
- self.gem_name ||= framework_name.downcase
10
+ self.gem_name ||= framework.downcase
9
11
  self.file_content_pattern = /^\s*gem ["']#{gem_name}["']/
10
-
11
- super
12
12
  end
13
13
  end
@@ -0,0 +1,12 @@
1
+ require 'technologist/rules/file_xml_content_rule'
2
+
3
+ class MavenPluginRule < FileXmlContentRule
4
+ attr_accessor :plugin_name
5
+
6
+ def initialize(framework, attributes = {})
7
+ super
8
+
9
+ self.file_name = 'pom.xml'
10
+ self.css_selector = "dependencies > dependency > groupId[text() = '#{plugin_name}']"
11
+ end
12
+ end
@@ -1,9 +1,13 @@
1
1
  class Rule
2
- def initialize(attributes = {})
2
+ attr_accessor :framework
3
+
4
+ def initialize(framework, attributes = {})
5
+ self.framework = framework
6
+
3
7
  attributes.each { |name, value| self.send(:"#{name}=", value) }
4
8
  end
5
9
 
6
- def matches?(framework_name, repository)
10
+ def matches?(repository)
7
11
  false
8
12
  end
9
13
  end
@@ -1,3 +1,3 @@
1
1
  module Technologist
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,71 @@
1
+ require 'yaml'
2
+
3
+ module Technologist
4
+ class YamlParser
5
+ attr_reader :file_name
6
+
7
+ def initialize(file_name)
8
+ @file_name = file_name
9
+ end
10
+
11
+ def rules
12
+ @rules ||=
13
+ begin
14
+ parsed_rules = from_file.map do |technology, definition|
15
+ definition['rules'].map! do |rule|
16
+ instancify(technology, rule)
17
+ end
18
+
19
+ [technology, definition]
20
+ end
21
+
22
+ Hash[parsed_rules]
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def from_file
29
+ YAML.load_file(file_name)
30
+ end
31
+
32
+ # Create a class instance for a rule entry
33
+ def instancify(technology, rule)
34
+ class_name, attributes = send("parse_rule_of_type_#{rule.class.name.downcase}", rule)
35
+
36
+ Rule.const_get("#{class_name}Rule").new(technology, attributes)
37
+ end
38
+
39
+ # Parses a yaml rule where the rule entry is a string,
40
+ # meaning that only the rule class name is given.
41
+ # Sample yaml structure:
42
+ # ```
43
+ # Rails:
44
+ # rules:
45
+ # - Gem
46
+ # ```
47
+ def parse_rule_of_type_hash(rule)
48
+ class_name = rule.keys.first
49
+ rule.delete(class_name)
50
+ attributes = rule
51
+
52
+ [class_name, attributes]
53
+ end
54
+
55
+ # Parses a yaml rule where the rule entry is a hash,
56
+ # meaning that the rule class also has attributes to be assigned.
57
+ # Sample yaml structure:
58
+ # ```
59
+ # Rails:
60
+ # rules:
61
+ # - Gem:
62
+ # gem_name: 'jrails'
63
+ # ```
64
+ def parse_rule_of_type_string(rule)
65
+ class_name = rule
66
+ attributes = {}
67
+
68
+ [class_name, attributes]
69
+ end
70
+ end
71
+ end
data/technologist.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.add_runtime_dependency "rugged", "~> 0.23.3"
23
+ spec.add_runtime_dependency "nokogiri", "~> 1.6"
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1.7"
25
26
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: technologist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Reigel
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-10-29 00:00:00.000000000 Z
11
+ date: 2015-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rugged
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.23.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -108,9 +122,12 @@ files:
108
122
  - lib/technologist/rules/directory_presence_rule.rb
109
123
  - lib/technologist/rules/file_content_rule.rb
110
124
  - lib/technologist/rules/file_presence_rule.rb
125
+ - lib/technologist/rules/file_xml_content_rule.rb
111
126
  - lib/technologist/rules/gem_rule.rb
127
+ - lib/technologist/rules/maven_plugin_rule.rb
112
128
  - lib/technologist/rules/rule.rb
113
129
  - lib/technologist/version.rb
130
+ - lib/technologist/yaml_parser.rb
114
131
  - technologist.gemspec
115
132
  homepage: https://github.com/koffeinfrei/technologist
116
133
  licenses: