me 0.0.1 → 1.0.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +2 -0
  5. data/README.md +4 -2
  6. data/bin/me +6 -0
  7. data/lib/me/activation.rb +39 -0
  8. data/lib/me/activation_view_model.rb +5 -0
  9. data/lib/me/cli/activate_command.rb +30 -0
  10. data/lib/me/cli/activation_view.rb +23 -0
  11. data/lib/me/cli/active_identity_view.rb +11 -0
  12. data/lib/me/cli/git_config_command.rb +23 -0
  13. data/lib/me/cli/git_config_view.rb +11 -0
  14. data/lib/me/cli/git_not_configured_view.rb +9 -0
  15. data/lib/me/cli/new_active_identity_view.rb +11 -0
  16. data/lib/me/cli/ssh_config_command.rb +23 -0
  17. data/lib/me/cli/ssh_config_view.rb +17 -0
  18. data/lib/me/cli/ssh_not_configured_view.rb +9 -0
  19. data/lib/me/cli/switch_command.rb +45 -0
  20. data/lib/me/cli/whoami_command.rb +18 -0
  21. data/lib/me/cli.rb +86 -0
  22. data/lib/me/error_presenter.rb +36 -0
  23. data/lib/me/errors.rb +32 -0
  24. data/lib/me/executor.rb +15 -0
  25. data/lib/me/git_activation.rb +30 -0
  26. data/lib/me/git_config.rb +71 -0
  27. data/lib/me/git_config_view_model.rb +5 -0
  28. data/lib/me/identity.rb +64 -0
  29. data/lib/me/identity_view_model.rb +5 -0
  30. data/lib/me/mappers/git_config_store2.rb +65 -0
  31. data/lib/me/mappers/identity_store2.rb +40 -0
  32. data/lib/me/mappers/ssh_config_store2.rb +68 -0
  33. data/lib/me/registry.rb +54 -0
  34. data/lib/me/ssh_activation.rb +28 -0
  35. data/lib/me/ssh_config.rb +65 -0
  36. data/lib/me/ssh_config_view_model.rb +5 -0
  37. data/lib/me/store2.rb +147 -0
  38. data/lib/me/thread_scope.rb +29 -0
  39. data/lib/me/version.rb +1 -1
  40. data/lib/me/view.rb +15 -0
  41. data/me.gemspec +2 -0
  42. data/spec/me/activation_spec.rb +22 -0
  43. data/spec/me/cli/activate_command_spec.rb +56 -0
  44. data/spec/me/cli/git_config_command_spec.rb +53 -0
  45. data/spec/me/cli/ssh_config_command_spec.rb +54 -0
  46. data/spec/me/cli/switch_command_spec.rb +64 -0
  47. data/spec/me/cli/whoami_command_spec.rb +26 -0
  48. data/spec/me/executor_spec.rb +23 -0
  49. data/spec/me/git_config_spec.rb +150 -0
  50. data/spec/me/identity_spec.rb +101 -0
  51. data/spec/me/mappers/git_config_store2_spec.rb +71 -0
  52. data/spec/me/mappers/identity_store2_spec.rb +37 -0
  53. data/spec/me/mappers/ssh_config_store2_spec.rb +65 -0
  54. data/spec/me/ssh_config_spec.rb +125 -0
  55. data/spec/me/store2_spec.rb +151 -0
  56. data/spec/spec_helper.rb +38 -0
  57. data/test.sh +38 -0
  58. metadata +85 -4
@@ -0,0 +1,64 @@
1
+ require "me/thread_scope"
2
+ require "me/git_config"
3
+ require "me/store2"
4
+
5
+ module Me
6
+ # Represents certain person, or person in certain role or situation
7
+ class Identity
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+
12
+ def ==(other)
13
+ return false unless other.is_a?(Identity)
14
+ self.name == other.name
15
+ end
16
+
17
+ def build_view(view_factory)
18
+ view_factory.new(name: name)
19
+ end
20
+
21
+ def activate
22
+ mapper.update(active_identity: name)
23
+ end
24
+
25
+ def git_config
26
+ GitConfig.for_identity(name)
27
+ end
28
+
29
+ def ssh_config
30
+ SshConfig.for_identity(name)
31
+ end
32
+
33
+ def with_mapper(mapper)
34
+ @mapper = mapper
35
+ self
36
+ end
37
+
38
+ protected
39
+
40
+ attr_reader :name, :mapper
41
+ end
42
+
43
+ class << Identity
44
+ def active
45
+ Registry.identity_mapper_factory.new.find
46
+ end
47
+
48
+ def build(mapper:, name:, active_identity:)
49
+ Identity.new(name).with_mapper(mapper)
50
+ end
51
+ end
52
+
53
+ # @abstract
54
+ class Identity::Mapper
55
+ def initialize(name = nil)
56
+ end
57
+
58
+ def find
59
+ end
60
+
61
+ def update(name: nil, active_identity: nil)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,5 @@
1
+ require "me/view"
2
+
3
+ module Me
4
+ IdentityViewModel = View.new(:name)
5
+ end
@@ -0,0 +1,65 @@
1
+ require "me/store2"
2
+ require "me/git_config"
3
+ require "me/errors"
4
+
5
+ module Me
6
+ module Mappers
7
+ class GitConfigStore2 < GitConfig::Mapper
8
+ def self.find_by_identity(identity_name)
9
+ new(nil, nil, identity_name).find
10
+ end
11
+
12
+ def initialize(name, email, identity_name)
13
+ @identity_name = identity_name
14
+ @name = name || fetch_name
15
+ @email = email || fetch_email
16
+ end
17
+
18
+ def find
19
+ ensure_present
20
+ GitConfig
21
+ .new(name, email, identity_name)
22
+ .with_mapper(self)
23
+ end
24
+
25
+ def update(name: nil, email: nil)
26
+ return unless name || email
27
+ scoped.set("name", name) if name
28
+ scoped.set("email", email) if email
29
+ scoped.save
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :name, :email, :identity_name, :fetched
35
+
36
+ def ensure_present
37
+ return if name && email
38
+ fail Errors::GitNotConfigured, identity_name
39
+ end
40
+
41
+ def fetch_name
42
+ scoped.get("name")
43
+ end
44
+
45
+ def fetch_email
46
+ scoped.get("email")
47
+ end
48
+
49
+ def store
50
+ @_store ||= Store2.build
51
+ end
52
+
53
+ def scoped
54
+ @_scoped ||= _scoped
55
+ end
56
+
57
+ def _scoped
58
+ store.get_or_set("identities", {})
59
+ store.get_or_set("identities", identity_name, {})
60
+ store.get_or_set("identities", identity_name, "git", {})
61
+ store.scoped("identities", identity_name, "git")
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,40 @@
1
+ require "me/identity"
2
+ require "me/store2"
3
+
4
+ module Me
5
+ module Mappers
6
+ class IdentityStore2 < Identity::Mapper
7
+ def initialize(name = nil)
8
+ @name = name || active_identity
9
+ end
10
+
11
+ def find
12
+ Identity.build(
13
+ mapper: self,
14
+ name: name,
15
+ active_identity: active_identity,
16
+ )
17
+ end
18
+
19
+ def update(name: nil, active_identity: nil)
20
+ return unless active_identity
21
+ store.set("active_identity", active_identity)
22
+ store.save
23
+ end
24
+
25
+ protected
26
+
27
+ attr_reader :name
28
+
29
+ private
30
+
31
+ def active_identity
32
+ store.get_or_set("active_identity", "<none>")
33
+ end
34
+
35
+ def store
36
+ @_store ||= Store2.build
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,68 @@
1
+ require "me/store2"
2
+ require "me/ssh_config"
3
+ require "me/errors"
4
+
5
+ module Me
6
+ module Mappers
7
+ class SshConfigStore2 < SshConfig::Mapper
8
+ def self.find_by_identity(identity_name)
9
+ new(nil, identity_name).find
10
+ end
11
+
12
+ def initialize(keys, identity_name)
13
+ @identity_name = identity_name
14
+ @keys = fetch_keys(keys)
15
+ end
16
+
17
+ def find
18
+ ensure_present
19
+ SshConfig
20
+ .new(keys, identity_name)
21
+ .with_mapper(self)
22
+ end
23
+
24
+ def update(keys: nil, identity_name: nil)
25
+ return unless keys
26
+ scoped.set("keys", keys)
27
+ scoped.save
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :identity_name, :keys
33
+
34
+ def ensure_present
35
+ return if keys && !keys.empty?
36
+ fail Errors::SshNotConfigured, identity_name
37
+ end
38
+
39
+ def fetch_keys(keys)
40
+ return keys if keys && !keys.empty?
41
+ scoped.get("keys")
42
+ end
43
+
44
+ def store
45
+ @_store ||= Store2.build
46
+ end
47
+
48
+ def identity
49
+ @_identity ||= _identity
50
+ end
51
+
52
+ def _identity
53
+ store.get_or_set("identities", {})
54
+ store.get_or_set("identities", identity_name, {})
55
+ store.scoped("identities", identity_name)
56
+ end
57
+
58
+ def scoped
59
+ @_scoped ||= _scoped
60
+ end
61
+
62
+ def _scoped
63
+ identity.get_or_set("ssh", {})
64
+ identity.scoped("ssh")
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,54 @@
1
+ require "forwardable"
2
+ require "me/thread_scope"
3
+
4
+ module Me
5
+ module Registry
6
+ extend self
7
+ extend Forwardable
8
+
9
+ delegate [
10
+ :identity_mapper_factory, :register_identity_mapper_factory,
11
+ :git_config_mapper_factory, :register_git_config_mapper_factory,
12
+ :ssh_config_mapper_factory, :register_ssh_config_mapper_factory,
13
+ :executor_factory, :register_executor_factory,
14
+ ] => :thread_scoped
15
+
16
+ delegate [
17
+ :error_view_factories, :register_error_view_factories,
18
+ :kernel, :register_kernel,
19
+ ] => :process_scoped
20
+
21
+ private
22
+
23
+ def process_scoped
24
+ @_process_scoped ||= ProcessScoped.new
25
+ end
26
+
27
+ def thread_scoped
28
+ ThreadScope[:thread_scoped_registry] ||= ThreadScoped.new
29
+ end
30
+
31
+ class Base
32
+ def self.def_registry_readers(*names)
33
+ names.each do |name|
34
+ attr_reader(name)
35
+ define_method(:"register_#{name}") do |value|
36
+ instance_variable_set(:"@#{name}", value)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ class ProcessScoped < Base
43
+ def_registry_readers :error_view_factories,
44
+ :kernel
45
+ end
46
+
47
+ class ThreadScoped < Base
48
+ def_registry_readers :identity_mapper_factory,
49
+ :git_config_mapper_factory,
50
+ :ssh_config_mapper_factory,
51
+ :executor_factory
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,28 @@
1
+ require "me/activation"
2
+ require "me/registry"
3
+
4
+ module Me
5
+ class SshActivation < Activation
6
+ def initialize(keys)
7
+ @keys = keys
8
+ end
9
+
10
+ def call
11
+ clear_ssh_keys
12
+ keys.each(&method(:add_ssh_key))
13
+ execute
14
+ end
15
+
16
+ protected
17
+
18
+ attr_reader :keys
19
+
20
+ def clear_ssh_keys
21
+ commands << ["ssh-add", "-D"]
22
+ end
23
+
24
+ def add_ssh_key(key)
25
+ commands << ["ssh-add", key]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,65 @@
1
+ require "me/registry"
2
+ require "me/ssh_activation"
3
+
4
+ module Me
5
+ class SshConfig
6
+ def initialize(keys, identity_name)
7
+ @keys = keys
8
+ @identity_name = identity_name
9
+ end
10
+
11
+ def ==(other)
12
+ return false unless other.is_a?(SshConfig)
13
+ self.keys == other.keys
14
+ end
15
+
16
+ def with_mapper(mapper)
17
+ @mapper = mapper
18
+ self
19
+ end
20
+
21
+ def configure
22
+ return if keys.empty?
23
+ mapper.update(keys: keys)
24
+ end
25
+
26
+ def activate
27
+ activation.call
28
+ activation
29
+ end
30
+
31
+ def build_view(view_factory)
32
+ view_factory.new(keys: keys)
33
+ end
34
+
35
+ protected
36
+
37
+ attr_reader :keys, :identity_name, :mapper
38
+
39
+ private
40
+
41
+ def activation
42
+ @_activation ||= SshActivation.new(keys)
43
+ end
44
+ end
45
+
46
+ class << SshConfig
47
+ def for_identity(identity_name)
48
+ Registry.ssh_config_mapper_factory.find_by_identity(identity_name)
49
+ end
50
+ end
51
+
52
+ class SshConfig::Mapper
53
+ def self.find_by_identity(identity_name)
54
+ end
55
+
56
+ def initialize(keys, identity_name)
57
+ end
58
+
59
+ def find
60
+ end
61
+
62
+ def update(keys: nil, identity_name: nil)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ require "me/view"
2
+
3
+ module Me
4
+ SshConfigViewModel = View.new(:keys)
5
+ end
data/lib/me/store2.rb ADDED
@@ -0,0 +1,147 @@
1
+ require "yaml"
2
+
3
+ module Me
4
+ class Store2
5
+ class Scoped
6
+ def initialize(store, scope)
7
+ @store, @scope = store, scope
8
+ end
9
+
10
+ def ==(other)
11
+ return false unless other.is_a?(Scoped)
12
+ [self.store, self.scope] ==
13
+ [other.store, other.scope]
14
+ end
15
+
16
+ def scoped(*add_scope)
17
+ Scoped.new(store, scope + add_scope)
18
+ end
19
+
20
+ def get(*keys)
21
+ store.get(scope + keys)
22
+ end
23
+
24
+ def set(*keys, value)
25
+ store.set(scope + keys, value)
26
+ end
27
+
28
+ def has?(*keys)
29
+ store.has?(scope + keys)
30
+ end
31
+
32
+ def get_or_set(*keys, value)
33
+ store.get_or_set(scope + keys, value)
34
+ end
35
+
36
+ def fetch(*keys, &block)
37
+ store.fetch(scope + keys, &block)
38
+ end
39
+
40
+ def save
41
+ store.save
42
+ end
43
+
44
+ def _reset
45
+ store._reset
46
+ end
47
+
48
+ protected
49
+
50
+ attr_reader :store, :scope
51
+ end
52
+
53
+ def initialize
54
+ @data = load
55
+ end
56
+
57
+ def self.build
58
+ Store2.new.scoped
59
+ end
60
+
61
+ def ==(other)
62
+ other.is_a?(Store2)
63
+ end
64
+
65
+ def scoped(*scope)
66
+ Scoped.new(self, scope)
67
+ end
68
+
69
+ def get(keys)
70
+ keys.inject(data) { |data, key| data[key] }
71
+ end
72
+
73
+ def set(keys, value)
74
+ get(keys[0...-1])[keys[-1]] = value
75
+ end
76
+
77
+ def has?(keys)
78
+ get(keys[0...-1]).has_key?(keys[-1])
79
+ end
80
+
81
+ def get_or_set(keys, value)
82
+ set(keys, value) unless has?(keys)
83
+ get(keys)
84
+ end
85
+
86
+ def fetch(keys, &block)
87
+ return block.call unless has?(keys)
88
+ get(keys)
89
+ end
90
+
91
+ def save
92
+ File.write(filename, to_yaml)
93
+ end
94
+
95
+ def to_yaml
96
+ data.to_yaml
97
+ end
98
+
99
+ def _reset
100
+ create
101
+ end
102
+
103
+ protected
104
+
105
+ attr_reader :data
106
+
107
+ private
108
+
109
+ def load
110
+ return create unless File.exist?(filename)
111
+ YAML.load_file(filename)
112
+ end
113
+
114
+ def create
115
+ (@data = {}).tap { save }
116
+ end
117
+
118
+ def filename
119
+ "#{user_home}/.me#{filename_suffix}.yml"
120
+ end
121
+
122
+ def user_home
123
+ ENV.fetch("HOME")
124
+ end
125
+
126
+ def filename_suffix
127
+ Environment.wrap(ENV.fetch("ME_ENV", NullEnvironment.new))
128
+ end
129
+
130
+ class Environment < Struct.new(:value)
131
+ def self.wrap(env)
132
+ return env if env.is_a?(self)
133
+ new(env)
134
+ end
135
+
136
+ def to_s
137
+ "_#{value}"
138
+ end
139
+ end
140
+
141
+ class NullEnvironment < Environment
142
+ def to_s
143
+ ""
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,29 @@
1
+ module Me
2
+ module ThreadScope
3
+ extend self
4
+
5
+ NAMESPACE = :__me
6
+
7
+ def [](name)
8
+ scope[name]
9
+ end
10
+
11
+ def []=(name, value)
12
+ scope[name] = value
13
+ end
14
+
15
+ def _reset
16
+ current[NAMESPACE] = {}
17
+ end
18
+
19
+ private
20
+
21
+ def scope
22
+ current[NAMESPACE] ||= {}
23
+ end
24
+
25
+ def current
26
+ Thread.current
27
+ end
28
+ end
29
+ end
data/lib/me/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Me
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/me/view.rb ADDED
@@ -0,0 +1,15 @@
1
+ module Me
2
+ module View
3
+ def self.new(*args)
4
+ return super if args[0].is_a?(Hash)
5
+ Class.new(Struct.new(*args)) do
6
+ include View
7
+ end
8
+ end
9
+
10
+ def initialize(hash)
11
+ super()
12
+ hash.each { |k, v| self[k] = v }
13
+ end
14
+ end
15
+ end
data/me.gemspec CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "thor"
22
+
21
23
  spec.add_development_dependency "bundler", "~> 1.7"
22
24
  spec.add_development_dependency "rake", "~> 10.0"
23
25
  end
@@ -0,0 +1,22 @@
1
+ require "me/activation"
2
+
3
+ module Me
4
+ RSpec.describe Activation do
5
+ subject(:activation) { Activation.new._with_commands(commands) }
6
+
7
+ let(:commands) { double("commands") }
8
+
9
+ let(:view_factory) { double("ViewFactory") }
10
+ let(:view) { double("View") }
11
+
12
+ describe "#build_view" do
13
+ it "gives commands to a view" do
14
+ allow(view_factory)
15
+ .to receive(:new)
16
+ .with(commands: commands)
17
+ .and_return(view)
18
+ expect(activation.build_view(view_factory)).to eq(view)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,56 @@
1
+ require "me/cli/activate_command"
2
+ require "me/registry"
3
+ require "me/git_config"
4
+ require "me/ssh_config"
5
+ require "me/activation"
6
+
7
+ module Me
8
+ module Cli
9
+ RSpec.describe ActivateCommand do
10
+ subject(:command) { described_class.new }
11
+
12
+ let(:identity) { instance_double(Identity) }
13
+ let(:git_config) { instance_double(GitConfig, activate: git_activation) }
14
+ let(:ssh_config) { instance_double(SshConfig, activate: ssh_activation) }
15
+
16
+ let(:git_activation) { instance_double(Activation) }
17
+ let(:ssh_activation) { instance_double(Activation) }
18
+
19
+ let(:git_view) { instance_double(ActivationView) }
20
+ let(:ssh_view) { instance_double(ActivationView) }
21
+
22
+ before do
23
+ allow(Identity).to receive(:active).and_return(identity)
24
+
25
+ allow(identity).to receive(:git_config).and_return(git_config)
26
+ allow(identity).to receive(:ssh_config).and_return(ssh_config)
27
+
28
+ allow(git_activation)
29
+ .to receive(:build_view)
30
+ .with(ActivationView)
31
+ .and_return(git_view)
32
+
33
+ allow(ssh_activation)
34
+ .to receive(:build_view)
35
+ .with(ActivationView)
36
+ .and_return(ssh_view)
37
+ end
38
+
39
+ describe "#call" do
40
+ it "activates active identity's git configuration" do
41
+ expect(git_config).to receive(:activate)
42
+ command.call
43
+ end
44
+
45
+ it "activates active identity's ssh configuration" do
46
+ expect(ssh_config).to receive(:activate)
47
+ command.call
48
+ end
49
+
50
+ it "renders proper response" do
51
+ expect(command.call).to eq([git_view, ssh_view])
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end