social_snippet 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +2 -0
  3. data/.travis.yml +22 -3
  4. data/Gemfile +25 -2
  5. data/Guardfile +9 -0
  6. data/README.md +6 -1
  7. data/Rakefile +51 -1
  8. data/appveyor.yml +36 -0
  9. data/bin/ssnip +10 -0
  10. data/bin/sspm +10 -0
  11. data/lib/social_snippet.rb +15 -4
  12. data/lib/social_snippet/api.rb +238 -0
  13. data/lib/social_snippet/command_line.rb +4 -0
  14. data/lib/social_snippet/command_line/command.rb +158 -0
  15. data/lib/social_snippet/command_line/ssnip.rb +2 -0
  16. data/lib/social_snippet/command_line/ssnip/main_command.rb +26 -0
  17. data/lib/social_snippet/command_line/sspm.rb +3 -0
  18. data/lib/social_snippet/command_line/sspm/main_command.rb +63 -0
  19. data/lib/social_snippet/command_line/sspm/sub_commands.rb +16 -0
  20. data/lib/social_snippet/command_line/sspm/sub_commands/complete_command.rb +28 -0
  21. data/lib/social_snippet/command_line/sspm/sub_commands/info_command.rb +28 -0
  22. data/lib/social_snippet/command_line/sspm/sub_commands/install_command.rb +103 -0
  23. data/lib/social_snippet/command_line/sspm/sub_commands/publish_command.rb +51 -0
  24. data/lib/social_snippet/command_line/sspm/sub_commands/search_command.rb +32 -0
  25. data/lib/social_snippet/command_line/sspm/sub_commands/update_command.rb +37 -0
  26. data/lib/social_snippet/config.rb +92 -0
  27. data/lib/social_snippet/context.rb +89 -0
  28. data/lib/social_snippet/core.rb +40 -0
  29. data/lib/social_snippet/inserter.rb +64 -0
  30. data/lib/social_snippet/logger.rb +9 -0
  31. data/lib/social_snippet/registry.rb +3 -0
  32. data/lib/social_snippet/registry/registry_client.rb +15 -0
  33. data/lib/social_snippet/registry/registry_resources.rb +3 -0
  34. data/lib/social_snippet/registry/registry_resources/base.rb +80 -0
  35. data/lib/social_snippet/registry/registry_resources/repositories.rb +23 -0
  36. data/lib/social_snippet/repository.rb +6 -0
  37. data/lib/social_snippet/repository/drivers.rb +3 -0
  38. data/lib/social_snippet/repository/drivers/base_repository.rb +192 -0
  39. data/lib/social_snippet/repository/drivers/git_repository.rb +76 -0
  40. data/lib/social_snippet/repository/repository_errors.rb +5 -0
  41. data/lib/social_snippet/repository/repository_factory.rb +59 -0
  42. data/lib/social_snippet/repository/repository_installer.rb +86 -0
  43. data/lib/social_snippet/repository/repository_manager.rb +177 -0
  44. data/lib/social_snippet/resolvers.rb +4 -0
  45. data/lib/social_snippet/resolvers/base_resolver.rb +103 -0
  46. data/lib/social_snippet/resolvers/dep_resolver.rb +61 -0
  47. data/lib/social_snippet/resolvers/insert_resolver.rb +100 -0
  48. data/lib/social_snippet/snippet.rb +14 -0
  49. data/lib/social_snippet/tag.rb +198 -0
  50. data/lib/social_snippet/tag_parser.rb +61 -0
  51. data/lib/social_snippet/version.rb +26 -1
  52. data/social_snippet.gemspec +18 -3
  53. data/spec/helpers/codeclimate_helper.rb +4 -0
  54. data/spec/helpers/fakefs_helper.rb +15 -0
  55. data/spec/helpers/webmock_helper.rb +16 -0
  56. data/spec/lib/api_spec.rb +106 -0
  57. data/spec/lib/command_line/sspm_install_spec.rb +224 -0
  58. data/spec/lib/command_line/sspm_search_spec.rb +167 -0
  59. data/spec/lib/command_line/sspm_spec.rb +81 -0
  60. data/spec/lib/config_spec.rb +56 -0
  61. data/spec/lib/context_spec.rb +48 -0
  62. data/spec/lib/core_spec.rb +126 -0
  63. data/spec/lib/inserter_spec.rb +177 -0
  64. data/spec/lib/registry_client_spec.rb +173 -0
  65. data/spec/lib/repository/base_repository_spec.rb +104 -0
  66. data/spec/lib/repository/git_repository_spec.rb +83 -0
  67. data/spec/lib/repository/repository_factory_spec.rb +31 -0
  68. data/spec/lib/repository/repository_installer_spec.rb +63 -0
  69. data/spec/lib/repository/repository_manager_spec.rb +201 -0
  70. data/spec/lib/tag_parser_spec.rb +173 -0
  71. data/spec/lib/tag_spec.rb +93 -0
  72. data/spec/spec_helper.rb +106 -0
  73. data/test/base_repository_test.rb +375 -0
  74. data/test/command_test.rb +39 -0
  75. data/test/context_test.rb +31 -0
  76. data/test/core_test.rb +2091 -0
  77. data/test/git_repository_test.rb +114 -0
  78. data/test/install_command_test.rb +28 -0
  79. data/test/repository_manager_test.rb +109 -0
  80. data/test/tag_parser_test.rb +47 -0
  81. data/test/tag_test.rb +217 -0
  82. data/test/version_test.rb +56 -0
  83. metadata +271 -14
@@ -0,0 +1,86 @@
1
+ module SocialSnippet::Repository
2
+
3
+ require "yaml"
4
+
5
+ class RepositoryInstaller
6
+
7
+ attr_reader :social_snippet
8
+ attr_reader :data
9
+
10
+ def initialize(new_social_snippet)
11
+ @social_snippet = new_social_snippet
12
+ init_data
13
+ end
14
+
15
+ def path
16
+ social_snippet.config.install_path
17
+ end
18
+
19
+ def data_file
20
+ social_snippet.config.installed_repos_file
21
+ end
22
+
23
+ def init_data
24
+ ::FileUtils.touch data_file
25
+ load
26
+ reset unless data
27
+ end
28
+
29
+ def reset
30
+ @data = {}
31
+ save
32
+ end
33
+
34
+ def load
35
+ @data = ::YAML.load_file(data_file)
36
+ end
37
+
38
+ def save
39
+ ::File.write data_file, data.to_yaml
40
+ end
41
+
42
+ def add(repo_name, repo_ref)
43
+ data[repo_name] ||= Set.new
44
+ data[repo_name].add repo_ref
45
+ save
46
+ end
47
+
48
+ def remove(repo_name, repo_ref)
49
+ data[repo_name] ||= Set.new
50
+ data[repo_name].delete repo_ref
51
+ save
52
+ end
53
+
54
+ def exists?(repo_name, repo_ref = nil)
55
+ data[repo_name] ||= Set.new
56
+ if repo_ref.nil?
57
+ data[repo_name].empty? === false
58
+ else
59
+ data[repo_name].include? repo_ref
60
+ end
61
+ end
62
+
63
+ def copy_repository(repo, options = {})
64
+ social_snippet.logger.debug "repository_installer: repo.path = #{repo.path}"
65
+ ::FileUtils.cp_r repo.path, repo_path(repo.name)
66
+ end
67
+
68
+ def fetch(repo_name, options)
69
+ repo = ::SocialSnippet::Repository::RepositoryFactory.create(repo_path(repo_name), options)
70
+ repo.update
71
+ end
72
+
73
+ def repo_path(name)
74
+ ::File.join(path, name)
75
+ end
76
+
77
+ def each(&block)
78
+ data
79
+ .select {|repo_name, repo_refs| not repo_refs.empty? }
80
+ .keys
81
+ .each {|repo_name| block.call repo_name }
82
+ end
83
+
84
+ end # RepositoryInstaller
85
+
86
+ end # SocialSnippet
@@ -0,0 +1,177 @@
1
+ module SocialSnippet::Repository
2
+
3
+ class RepositoryManager
4
+
5
+ attr_reader :installer
6
+ attr_reader :repo_paths
7
+ attr_reader :repo_cache_path
8
+ attr_reader :social_snippet
9
+
10
+ # Constructor
11
+ #
12
+ # @param new_social_snippet [::SocialSnippet::Core]
13
+ def initialize(new_social_snippet)
14
+ @social_snippet = new_social_snippet
15
+ @installer = ::SocialSnippet::Repository::RepositoryInstaller.new(social_snippet)
16
+ @repo_cache_path = social_snippet.config.repository_cache_path
17
+ @repo_paths = []
18
+
19
+ init_repo_paths
20
+ init_repo_cache_path
21
+ end
22
+
23
+ def init_repo_cache_path
24
+ ::FileUtils.mkdir_p repo_cache_path
25
+ end
26
+
27
+ def init_repo_paths
28
+ repo_paths.push installer.path
29
+ repo_paths.each {|path| ::FileUtils.mkdir_p path }
30
+ end
31
+
32
+ def deps(repo_name, repo_ref = nil)
33
+ repo = find_repository(repo_name)
34
+ repo.checkout(repo_ref) unless repo_ref.nil?
35
+ repo.dependencies
36
+ end
37
+
38
+ def has_deps?(repo_name, repo_ref = nil)
39
+ not deps(repo_name, repo_ref).empty?
40
+ end
41
+
42
+ # Get snippet
43
+ #
44
+ # @param context [::SocialSnippet::Context] The context of snippet
45
+ # @param tag [::SocialSnippet::Tag] The tag of snippet
46
+ def get_snippet(context, tag)
47
+ return ::SocialSnippet::Snippet.new(resolve_snippet_path(context, tag))
48
+ end
49
+
50
+ # Resolve snippet path from tag
51
+ #
52
+ # @param context [::SocialSnippet::Context] The context of snippet
53
+ # @param tag [::SocialSnippet::Tag] The tag of snippet
54
+ def resolve_snippet_path(context, tag)
55
+ if tag.has_repo?
56
+ repo = find_repository_by_tag(tag)
57
+ return repo.real_path tag.path
58
+ end
59
+
60
+ new_context = context.clone
61
+ new_context.move tag.path
62
+ return new_context.path
63
+ end
64
+
65
+ # Find repository by tag
66
+ #
67
+ # @param tag [::SocialSnippet::Tag] The tag of repository
68
+ def find_repository_by_tag(tag)
69
+ if tag.has_ref?
70
+ find_repository(tag.repo, tag.ref)
71
+ else
72
+ find_repository(tag.repo)
73
+ end
74
+ end
75
+
76
+ # Find repository by repo name
77
+ #
78
+ # @param name [String] The name of repository
79
+ def find_repository(name, ref = nil)
80
+ repo_paths.each do |repo_path|
81
+ path = "#{repo_path}/#{name}"
82
+ if ::Dir.exists?(path)
83
+ repo = RepositoryFactory.create(path, ref)
84
+ repo.load_cache repo_cache_path
85
+ return repo
86
+ end
87
+ end
88
+
89
+ return nil
90
+ end
91
+
92
+ def find_repositories_start_with(prefix)
93
+ glob_path = ::File.join(installer.path, "#{prefix}*")
94
+ ::Dir.glob(glob_path).map do |repopath|
95
+ Pathname(repopath).basename.to_s
96
+ end
97
+ end
98
+
99
+ def complete_repo_name(keyword)
100
+ repo_name = get_repo_name_prefix(keyword)
101
+ find_repositories_start_with(repo_name)
102
+ end
103
+
104
+ def complete_file_name(keyword)
105
+ repo_name = get_repo_name(keyword)
106
+ repo = find_repository(repo_name)
107
+ file_path = get_file_path_prefix(keyword)
108
+ glob_path = "#{file_path}*"
109
+
110
+ repo.glob(glob_path).map do |cand_file_path|
111
+ if ::File.directory?(cand_file_path)
112
+ Pathname(cand_file_path).basename.to_s + "/"
113
+ else
114
+ Pathname(cand_file_path).basename.to_s + ">"
115
+ end
116
+ end
117
+ end
118
+
119
+ def complete(keyword)
120
+ if is_completing_repo_name?(keyword)
121
+ complete_repo_name(keyword)
122
+ elsif is_completing_file_path?(keyword)
123
+ complete_file_name(keyword)
124
+ else
125
+ raise "completion error"
126
+ end
127
+ end
128
+
129
+ def get_repo_name(keyword)
130
+ /^[^@]*@[^<]+<([^:#>]*[^:#>])/.match(keyword)[1]
131
+ end
132
+
133
+ def get_repo_name_prefix(keyword)
134
+ /^[^@]*@[^<]+<([^:#>]*[^:#>])$/.match(keyword)[1]
135
+ end
136
+
137
+ def get_file_path_prefix(keyword)
138
+ /^[^@]*@[^<]+<[^#:]+:([^>]*)$/.match(keyword)[1]
139
+ end
140
+
141
+ def is_completing_repo_name?(keyword)
142
+ /^[^@]*@[^<]+<[^:#>]*$/ === keyword
143
+ end
144
+
145
+ def is_completing_file_path?(keyword)
146
+ /^[^@]*@[^<]+<[^#:]+:[^>]*$/ === keyword
147
+ end
148
+
149
+ def cache_installing_repo(repo, options = {})
150
+ repo.create_cache repo_cache_path
151
+ end
152
+
153
+ def fetch(repo_name, options)
154
+ installer.fetch repo_name, options
155
+ end
156
+
157
+ def update(repo_name, repo_ref, repo, options)
158
+ cache_installing_repo repo, options
159
+ installer.add repo_name, repo_ref
160
+ end
161
+
162
+ def install(repo_name, repo_ref, repo, options)
163
+ installer.copy_repository repo, options
164
+ update repo_name, repo_ref, repo, options
165
+ end
166
+
167
+ def exists?(repo_name, repo_ref = nil)
168
+ installer.exists? repo_name, repo_ref
169
+ end
170
+
171
+ def each_installed_repo(&block)
172
+ installer.each &block
173
+ end
174
+
175
+ end # RepositoryManager
176
+
177
+ end # SocialSnippet
@@ -0,0 +1,4 @@
1
+ module SocialSnippet::Resolvers; end
2
+ require_relative "resolvers/base_resolver"
3
+ require_relative "resolvers/insert_resolver"
4
+ require_relative "resolvers/dep_resolver"
@@ -0,0 +1,103 @@
1
+ module SocialSnippet
2
+
3
+ class Resolvers::BaseResolver
4
+
5
+ attr_reader :social_snippet
6
+ attr_reader :visited
7
+
8
+ # Constructor
9
+ #
10
+ # @param new_social_snippet [::SocialSnippet::Core]
11
+ def initialize(new_social_snippet)
12
+ @social_snippet = new_social_snippet
13
+ @visited = Set.new
14
+ end
15
+
16
+ # Call block each snip tags
17
+ #
18
+ # @param src [Array<String>] The text of source code
19
+ # @param context [SocialSnippet::Context] The context of current code
20
+ # @param base_tag [SocialSnippet::Tag]
21
+ def each_snip_tags(src, context, base_tag)
22
+ TagParser.find_snip_tags(src).each do |tag_info|
23
+ t = tag_info[:tag].set_by_tag(base_tag)
24
+ new_context = context.clone
25
+
26
+ move_context_by_tag! new_context, t
27
+ overwrite_tag_in_same_repository! context, t
28
+ update_tag_path_by_context! new_context, t
29
+ resolve_tag_repo_ref! t
30
+
31
+ snippet = social_snippet.repo_manager.get_snippet(context, t)
32
+
33
+ if block_given?
34
+ yield(
35
+ tag_info[:tag],
36
+ tag_info[:line_no],
37
+ snippet,
38
+ new_context
39
+ )
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def move_context_by_tag!(context, tag)
47
+ if tag.has_repo?
48
+ if tag.has_ref?
49
+ context.move tag.path, tag.repo, tag.ref
50
+ else
51
+ context.move tag.path, tag.repo
52
+ end
53
+ else
54
+ context.move tag.path
55
+ end
56
+ end
57
+
58
+ # Overwrite tag
59
+ def overwrite_tag_in_same_repository!(context, tag)
60
+ if context.is_in_repository? && tag.has_repo? === false
61
+ tag.set_repo context.repo
62
+ tag.set_ref context.ref
63
+ end
64
+ end
65
+
66
+ # Update tag path by context
67
+ def update_tag_path_by_context!(context, tag)
68
+ if tag.has_repo?
69
+ tag.set_path context.path
70
+ end
71
+ end
72
+
73
+ # Resolve tag's ref
74
+ def resolve_tag_repo_ref!(tag)
75
+ repo = social_snippet.repo_manager.find_repository_by_tag(tag)
76
+
77
+ # not found
78
+ return if repo.nil?
79
+
80
+ if tag.has_ref? === false || tag.ref != repo.latest_version(tag.ref)
81
+ if repo.has_versions?
82
+ tag.set_ref repo.latest_version(tag.ref)
83
+ else
84
+ tag.set_ref repo.short_commit_id
85
+ end
86
+ end
87
+ end
88
+
89
+ def visit(tag)
90
+ visited.add tag.to_path
91
+ end
92
+
93
+ def is_visited(tag)
94
+ visited.include? tag.to_path
95
+ end
96
+
97
+ def is_self(tag, context)
98
+ tag.repo === context.repo && tag.path === context.path
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,61 @@
1
+ module SocialSnippet
2
+
3
+ class Resolvers::DepResolver < Resolvers::BaseResolver
4
+
5
+ attr_reader :dep_to
6
+
7
+ # Constructor
8
+ #
9
+ # @param social_snippet [::SocialSnippet::Core]
10
+ def initialize(social_snippet)
11
+ @dep_to = {} # dep_to[tag_from] = tag_to
12
+ super(social_snippet)
13
+ end
14
+
15
+ # Find all missing depended snippets
16
+ #
17
+ # @param src [Array<String>] The text of source code
18
+ # @param context_from [SocialSnippet::Context] The context of previous code
19
+ # @param tag_from [SocialSnippet::Tag] The previous tag
20
+ def find(src, context_from, tag_from)
21
+ found_tags = find_func src, context_from, tag_from
22
+ found_tags.each do |tag_info|
23
+ # remove self from deps graph
24
+ tag = tag_info[:tag]
25
+ dep_to[tag.to_path] ||= ::Set.new
26
+ dep_to[tag.to_path].delete tag_from.to_path
27
+ end
28
+ found_tags
29
+ end
30
+
31
+ private
32
+
33
+ def set_snippet_dep(tag_from, tag_to)
34
+ dep_to[tag_from.to_path] ||= ::Set.new
35
+ dep_to[tag_from.to_path].add tag_to.to_path
36
+ end
37
+
38
+ def find_func(src, context_from, tag_from)
39
+ found_tags = []
40
+ context = context_from.clone
41
+
42
+ each_snip_tags(src, context_from, tag_from) do |tag, line_no, snippet, new_context|
43
+ next if is_visited(tag)
44
+ visit tag
45
+
46
+ set_snippet_dep tag_from, tag
47
+ found_tags.push({
48
+ :tag => tag,
49
+ :context => new_context,
50
+ })
51
+ # find more deps
52
+ found_tags.push *find_func(snippet.lines, new_context, tag)
53
+ end
54
+
55
+ return found_tags
56
+ end
57
+
58
+ end # DepResolver
59
+
60
+ end # SocialSnippet
61
+
@@ -0,0 +1,100 @@
1
+ module SocialSnippet
2
+
3
+ class Resolvers::InsertResolver < Resolvers::BaseResolver
4
+
5
+ attr_reader :deps_resolver
6
+
7
+ # Constructor
8
+ #
9
+ # @param social_snippet [::SocialSnippet::Core]
10
+ def initialize(social_snippet)
11
+ @deps_resolver = Resolvers::DepResolver.new(social_snippet)
12
+ super(social_snippet)
13
+ end
14
+
15
+ # Insert snippets to given text
16
+ #
17
+ # @param src [Array<String>] The text of source code
18
+ def insert(src)
19
+ context = Context.new("")
20
+ lines = src.split("\n")
21
+
22
+ TagParser.find_snippet_tags(lines).each do |tag_info|
23
+ visit tag_info[:tag]
24
+ end
25
+
26
+ dest = insert_func(lines, context)
27
+ return dest.join("\n")
28
+ end
29
+
30
+ private
31
+
32
+ def insert_func(code, context_from, base_tag = nil)
33
+ inserter = Inserter.new(code)
34
+ context = context_from.clone
35
+
36
+ # replace each @snip tags
37
+ each_snip_tags(code, context, base_tag) do |tag, line_no, snippet, new_context|
38
+ inserter.set_index line_no
39
+ inserter.ignore
40
+
41
+ visit(tag) if is_self(tag, context)
42
+ next if is_visited(tag)
43
+
44
+ insert_depended_snippets! inserter, snippet, new_context, tag
45
+ insert_by_tag_and_context! inserter, snippet, new_context, tag
46
+ end
47
+
48
+ inserter.set_index_last
49
+ return inserter.dest
50
+ end
51
+
52
+ # Insert snippet by tag and context
53
+ def insert_by_tag_and_context!(inserter, snippet, context, tag)
54
+ src = insert_func(snippet.lines, context, tag)
55
+ inserter.insert tag.to_snippet_tag # @snip -> @snippet
56
+ inserter.insert src
57
+ visit tag
58
+ end
59
+
60
+ # Insert depended snippet
61
+ def insert_depended_snippets!(inserter, snippet, context, tag)
62
+ dep_tags = deps_resolver.find(snippet.lines, context, tag)
63
+ dep_tags = sort_dep_tags_by_dep(dep_tags)
64
+
65
+ dep_tags.each do |tag_info|
66
+ sub_t = tag_info[:tag]
67
+ sub_c = tag_info[:context]
68
+
69
+ visit(tag) if is_self(tag, context)
70
+ next if is_visited(sub_t)
71
+
72
+ sub_snippet = social_snippet.repo_manager.get_snippet(sub_c, sub_t)
73
+ insert_by_tag_and_context! inserter, sub_snippet, sub_c, sub_t
74
+ end
75
+ end
76
+
77
+ # Sort by dependency
78
+ def sort_dep_tags_by_dep(dep_tags)
79
+ dep_tags_index = {}
80
+
81
+ # map tag to index
82
+ dep_tags.each.with_index do |tag_info, k|
83
+ tag = tag_info[:tag]
84
+ dep_tags_index[tag.to_path] = k
85
+ end
86
+
87
+ # generate dependency graph
88
+ dep_tags_hash = {}
89
+ dep_tags.each do |tag_info|
90
+ tag = tag_info[:tag].to_path
91
+ dep_ind = dep_tags_index[tag]
92
+ dep_tags_hash[dep_ind] = deps_resolver.dep_to[tag].to_a.map {|tag| dep_tags_index[tag] }
93
+ end
94
+
95
+ dep_tags_hash.tsort.map {|k| dep_tags[k]}
96
+ end
97
+
98
+ end # BaseResolver
99
+
100
+ end # SocialSnippet