social_snippet 0.0.1 → 0.0.2

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 (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