technologist 0.3.0 → 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.
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: