haveapi-fs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/CHANGELOG +2 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +338 -0
  7. data/Rakefile +1 -0
  8. data/assets/css/bootstrap.min.css +6 -0
  9. data/assets/css/local.css +11 -0
  10. data/bin/haveapi-fs +5 -0
  11. data/haveapi-fs.gemspec +30 -0
  12. data/lib/core_ext/string.rb +9 -0
  13. data/lib/haveapi/fs/auth/base.rb +56 -0
  14. data/lib/haveapi/fs/auth/basic.rb +29 -0
  15. data/lib/haveapi/fs/auth/noauth.rb +9 -0
  16. data/lib/haveapi/fs/auth/token.rb +39 -0
  17. data/lib/haveapi/fs/cache.rb +71 -0
  18. data/lib/haveapi/fs/cleaner.rb +56 -0
  19. data/lib/haveapi/fs/component.rb +237 -0
  20. data/lib/haveapi/fs/components/action_dir.rb +106 -0
  21. data/lib/haveapi/fs/components/action_errors.rb +49 -0
  22. data/lib/haveapi/fs/components/action_exec.rb +12 -0
  23. data/lib/haveapi/fs/components/action_exec_edit.rb +90 -0
  24. data/lib/haveapi/fs/components/action_input.rb +40 -0
  25. data/lib/haveapi/fs/components/action_message.rb +19 -0
  26. data/lib/haveapi/fs/components/action_output.rb +79 -0
  27. data/lib/haveapi/fs/components/action_status.rb +32 -0
  28. data/lib/haveapi/fs/components/cache_stats.rb +19 -0
  29. data/lib/haveapi/fs/components/component_list.rb +24 -0
  30. data/lib/haveapi/fs/components/create_action_dir.rb +15 -0
  31. data/lib/haveapi/fs/components/delete_action_dir.rb +22 -0
  32. data/lib/haveapi/fs/components/directory.rb +45 -0
  33. data/lib/haveapi/fs/components/directory_reset.rb +8 -0
  34. data/lib/haveapi/fs/components/executable.rb +75 -0
  35. data/lib/haveapi/fs/components/file.rb +43 -0
  36. data/lib/haveapi/fs/components/groff_help_file.rb +9 -0
  37. data/lib/haveapi/fs/components/help_file.rb +28 -0
  38. data/lib/haveapi/fs/components/html_help_file.rb +24 -0
  39. data/lib/haveapi/fs/components/index_filter.rb +63 -0
  40. data/lib/haveapi/fs/components/info_files.rb +19 -0
  41. data/lib/haveapi/fs/components/instance_create.rb +20 -0
  42. data/lib/haveapi/fs/components/instance_edit.rb +49 -0
  43. data/lib/haveapi/fs/components/list_item.rb +28 -0
  44. data/lib/haveapi/fs/components/md_help_file.rb +24 -0
  45. data/lib/haveapi/fs/components/meta_dir.rb +42 -0
  46. data/lib/haveapi/fs/components/meta_file.rb +21 -0
  47. data/lib/haveapi/fs/components/parameter.rb +132 -0
  48. data/lib/haveapi/fs/components/pry.rb +9 -0
  49. data/lib/haveapi/fs/components/remote_control_file.rb +92 -0
  50. data/lib/haveapi/fs/components/resource_action_dir.rb +72 -0
  51. data/lib/haveapi/fs/components/resource_dir.rb +161 -0
  52. data/lib/haveapi/fs/components/resource_id.rb +15 -0
  53. data/lib/haveapi/fs/components/resource_instance_dir.rb +146 -0
  54. data/lib/haveapi/fs/components/rfuse_check.rb +3 -0
  55. data/lib/haveapi/fs/components/root.rb +75 -0
  56. data/lib/haveapi/fs/components/save_instance.rb +11 -0
  57. data/lib/haveapi/fs/components/unsaved_list.rb +24 -0
  58. data/lib/haveapi/fs/components/update_action_dir.rb +31 -0
  59. data/lib/haveapi/fs/context.rb +43 -0
  60. data/lib/haveapi/fs/exceptions.rb +0 -0
  61. data/lib/haveapi/fs/factory.rb +59 -0
  62. data/lib/haveapi/fs/fs.rb +198 -0
  63. data/lib/haveapi/fs/help.rb +91 -0
  64. data/lib/haveapi/fs/main.rb +134 -0
  65. data/lib/haveapi/fs/remote_control.rb +29 -0
  66. data/lib/haveapi/fs/version.rb +5 -0
  67. data/lib/haveapi/fs/worker.rb +77 -0
  68. data/lib/haveapi/fs.rb +65 -0
  69. data/templates/help/html/action_dir.erb +33 -0
  70. data/templates/help/html/action_errors.erb +4 -0
  71. data/templates/help/html/action_input.erb +40 -0
  72. data/templates/help/html/action_output.erb +21 -0
  73. data/templates/help/html/index_filter.erb +16 -0
  74. data/templates/help/html/layout.erb +45 -0
  75. data/templates/help/html/resource_action_dir.erb +18 -0
  76. data/templates/help/html/resource_dir.erb +18 -0
  77. data/templates/help/html/resource_instance_dir.erb +64 -0
  78. data/templates/help/html/root.erb +42 -0
  79. data/templates/help/md/action_dir.erb +29 -0
  80. data/templates/help/md/action_errors.erb +2 -0
  81. data/templates/help/md/action_input.erb +23 -0
  82. data/templates/help/md/action_output.erb +11 -0
  83. data/templates/help/md/index_filter.erb +11 -0
  84. data/templates/help/md/layout.erb +14 -0
  85. data/templates/help/md/resource_action_dir.erb +10 -0
  86. data/templates/help/md/resource_dir.erb +15 -0
  87. data/templates/help/md/resource_instance_dir.erb +42 -0
  88. data/templates/help/md/root.erb +34 -0
  89. metadata +231 -0
@@ -0,0 +1,15 @@
1
+ module HaveAPI::Fs::Components
2
+ class CreateActionDir < ActionDir
3
+ help_file :action_dir
4
+
5
+ def exec
6
+ ret = super
7
+
8
+ if ret.is_a?(HaveAPI::Client::Response) && ret.ok?
9
+ context[:resource_dir].refresh
10
+ end
11
+
12
+ ret
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ module HaveAPI::Fs::Components
2
+ class DeleteActionDir < ActionDir
3
+ help_file :action_dir
4
+
5
+ def exec
6
+ ret = super
7
+
8
+ if ret.is_a?(HaveAPI::Client::Response) && ret.ok?
9
+ if @resource.is_a?(HaveAPI::Client::ResourceInstance)
10
+ id = @resource.id
11
+
12
+ else
13
+ id = @resource.prepared_args.last
14
+ end
15
+
16
+ context[:resource_dir].delete(id)
17
+ end
18
+
19
+ ret
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ module HaveAPI::Fs::Components
2
+ # Base class for all components that act as directories.
3
+ #
4
+ # Every directory contains some special hidden files:
5
+ #
6
+ # - `.components` contains a list of all descendant component objects that are
7
+ # created in memory
8
+ # - `.pry` is an executable that opens a developer console
9
+ class Directory < HaveAPI::Fs::Component
10
+ include HaveAPI::Fs::Help
11
+
12
+ def directory?
13
+ true
14
+ end
15
+
16
+ def contents
17
+ help_contents + %w(.reset .unsaved)
18
+ end
19
+
20
+ protected
21
+ def new_child(name)
22
+ return help_file(name) if help_file?(name)
23
+
24
+ case name
25
+ when HaveAPI::Fs::Fs::CHECK_FILE
26
+ RFuseCheck
27
+
28
+ when :'.reset'
29
+ DirectoryReset
30
+
31
+ when :'.unsaved'
32
+ UnsavedList
33
+
34
+ when :'.components'
35
+ ComponentList
36
+
37
+ when :'.pry'
38
+ HaveAPI::Fs::Components::Pry
39
+
40
+ else
41
+ nil
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,8 @@
1
+ module HaveAPI::Fs::Components
2
+ class DirectoryReset < Executable
3
+ def exec
4
+ parent.reset
5
+ true
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,75 @@
1
+ module HaveAPI::Fs::Components
2
+ # Base class for all executables. Executables can be executed either by
3
+ # writing `1` into them or by running them, as they contain a Ruby script.
4
+ #
5
+ # The Ruby script communicates with the file system using {RemoteControlFile}.
6
+ # In short, it tells the file system to execute a component at a certain path,
7
+ # which results in the same action as when `1` is written to the file.
8
+ #
9
+ # In both cases, the file system itself does the operation, the outer process
10
+ # only signals it to do so and then waits for a reply.
11
+ #
12
+ # When the component is to be executed, method {#exec} is called.
13
+ class Executable < File
14
+ def executable?
15
+ true
16
+ end
17
+
18
+ def writable?
19
+ true
20
+ end
21
+
22
+ def read
23
+ abs_path = ::File.join(context.mountpoint, path)
24
+
25
+ <<END
26
+ #!#{RbConfig.ruby}
27
+ #
28
+ # This action can be executed either by running this file or by writing "1" to
29
+ # it, e.g.:
30
+ #
31
+ # .#{abs_path}
32
+ #
33
+ # or
34
+ #
35
+ # echo 1 > #{abs_path}
36
+ #
37
+ require 'yaml'
38
+
39
+ f = ::File.open(
40
+ '#{::File.join(context.mountpoint, '.remote_control')}',
41
+ 'w+'
42
+ )
43
+ f.write(YAML.dump({
44
+ action: :execute,
45
+ path: '/#{path}',
46
+ }))
47
+ f.write(#{RemoteControlFile::MSG_DELIMITER.dump})
48
+ f.flush
49
+ f.seek(0)
50
+
51
+ ret = YAML.load(f.read)
52
+ f.close
53
+
54
+ unless ret[:status]
55
+ warn "Action failed: \#{ret[:message]}"
56
+ if ret[:errors]
57
+ ret[:errors].each do |k, v|
58
+ warn " \#{k}: \#{v.join('; ')}"
59
+ end
60
+ end
61
+ end
62
+
63
+ exit(ret[:status])
64
+ END
65
+ end
66
+
67
+ def write(str)
68
+ exec if str.strip == '1'
69
+ end
70
+
71
+ def exec
72
+ raise NotImplementedError
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,43 @@
1
+ module HaveAPI::Fs::Components
2
+ class File < HaveAPI::Fs::Component
3
+ def file?
4
+ true
5
+ end
6
+
7
+ def size
8
+ read.length
9
+ end
10
+
11
+ def read
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def write(str)
16
+ raise NotImplementedError
17
+ end
18
+
19
+ def raw_open(path, mode, rfusefs = nil)
20
+ nil
21
+ end
22
+
23
+ def raw_read(path, offset, size, raw = nil)
24
+
25
+ end
26
+
27
+ def raw_write(path, offset, size, buf, raw = nil)
28
+
29
+ end
30
+
31
+ def raw_sync(path, datasync, raw = nil)
32
+
33
+ end
34
+
35
+ def raw_truncate(path, offset, raw= nil)
36
+
37
+ end
38
+
39
+ def raw_close(path, raw = nil)
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ require 'md2man/roff/engine'
2
+
3
+ module HaveAPI::Fs::Components
4
+ class GroffHelpFile < MdHelpFile
5
+ def read
6
+ Md2Man::Roff::ENGINE.render(super)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ require 'erb'
2
+
3
+ module HaveAPI::Fs::Components
4
+ class HelpFile < File
5
+ def initialize(component, format)
6
+ super()
7
+ @component = @c = component
8
+ @context = component.context
9
+ @format = format
10
+ end
11
+
12
+ protected
13
+ def template_path(klass)
14
+ if klass.is_a?(::String)
15
+ name = klass
16
+
17
+ else
18
+ name = klass.help_file ? klass.help_file.to_s : klass.name.split('::').last.underscore
19
+ end
20
+
21
+ HaveAPI::Fs::Help.find!(::File.join(@format.to_s, name + ".erb"))
22
+ end
23
+
24
+ def layout(layout_erb)
25
+ layout_erb.result(binding { yield })
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ module HaveAPI::Fs::Components
2
+ class HtmlHelpFile < HelpFile
3
+ def initialize(*args)
4
+ super
5
+
6
+ @layout = ERB.new(::File.open(template_path('layout')).read, 0, '-')
7
+ @template = ERB.new(
8
+ ::File.open(template_path(@c.class)).read,
9
+ 0, '-'
10
+ )
11
+ end
12
+
13
+ def read
14
+ layout(@layout) do
15
+ @template.result(binding)
16
+ end
17
+ end
18
+
19
+ protected
20
+ def asset(path)
21
+ ::File.join(@context.mountpoint, '.assets', path)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,63 @@
1
+ module HaveAPI::Fs::Components
2
+ class IndexFilter < Directory
3
+ component :index_filter
4
+ attr_reader :resource_dir, :param, :filters
5
+
6
+ def initialize(resource_dir, param, filters = {})
7
+ super()
8
+
9
+ @resource_dir = resource_dir
10
+ @param = param
11
+ @filters = filters
12
+ end
13
+
14
+ def title
15
+ "Filter by #{@param}"
16
+ end
17
+
18
+ protected
19
+ def new_child(value)
20
+ if child = super
21
+ child
22
+
23
+ else
24
+ f = @filters.clone
25
+ f[ @param ] = value.to_s
26
+ [IndexFilterValue, @resource_dir.resource, f]
27
+ end
28
+ end
29
+ end
30
+
31
+ class IndexFilterValue < ResourceDir
32
+ component :resource_dir
33
+ help_file :resource_dir
34
+
35
+ def initialize(resource, filters)
36
+ super(resource)
37
+
38
+ @filters = filters
39
+ end
40
+
41
+ def setup
42
+ super
43
+
44
+ @filters.each do |k, v|
45
+ @index.find(:input).find(k).write(v)
46
+ @last = v
47
+ end
48
+ end
49
+
50
+ def title
51
+ @last.to_s
52
+ end
53
+
54
+ protected
55
+ def new_child(name)
56
+ child = super(name)
57
+ return child unless child
58
+
59
+ child << @filters.clone if [child].flatten.first == IndexFilter
60
+ child
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ module HaveAPI::Fs::Components
2
+ class ClientVersion < File
3
+ def read
4
+ HaveAPI::Client::VERSION + "\n"
5
+ end
6
+ end
7
+
8
+ class FsVersion < File
9
+ def read
10
+ HaveAPI::Fs::VERSION + "\n"
11
+ end
12
+ end
13
+
14
+ class ProtocolVersion < File
15
+ def read
16
+ HaveAPI::Client::PROTOCOL_VERSION + "\n"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ require 'yaml'
2
+
3
+ module HaveAPI::Fs::Components
4
+ class InstanceCreate < ActionExecEdit
5
+ def header
6
+ <<END
7
+ # This file is in YAML format. Lines beginning with a hash (#) are comments and
8
+ # are ignored. The new resource instance will be created once this file is saved
9
+ # and closed. The success of this operation can be later checked in
10
+ # actions/create/status.
11
+ #
12
+ # Only required parameters that need to be set are uncommented by default.
13
+ # Parameters that are not specified when this file is closed will not be sent
14
+ # to the API.
15
+ #
16
+ # To cancel the operation, either do not save the file or save it empty.
17
+ END
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,49 @@
1
+ require 'yaml'
2
+
3
+ module HaveAPI::Fs::Components
4
+ class InstanceEdit < ActionExecEdit
5
+ def header
6
+ <<END
7
+ # This file is in YAML format. Lines beginning with a hash (#) are comments and
8
+ # are ignored. The resource instance will be updated once this file is saved
9
+ # and closed. The success of this operation can be later checked in
10
+ # actions/update/status.
11
+ #
12
+ # To avoid updating a parameter, simply comment or delete it from this file.
13
+ # Values of parameters that are not present when the file is closed are not
14
+ # changed.
15
+ #
16
+ # To cancel the update, either do not save the file or save it empty.
17
+ END
18
+ end
19
+
20
+ def read
21
+ ret = header + "\n"
22
+ instance_dir = context[:resource_instance_dir]
23
+
24
+ @action_dir.action.input_params.each do |name, p|
25
+ if p[:type] == 'Resource'
26
+ v = instance_dir.resource.attributes[name][ p[:value_id].to_sym ]
27
+
28
+ else
29
+ v = instance_dir.resource.attributes[name]
30
+ end
31
+
32
+ ret += "# #{p[:label]}; #{p[:type]}\n"
33
+ ret += "# #{p[:description]}\n"
34
+ ret += "# Defaults to '#{p[:default]}'\n" unless p[:default].nil?
35
+ ret += "##{name}: #{v}\n\n"
36
+ end
37
+
38
+ ret
39
+ end
40
+
41
+ def save?(data)
42
+ data.any?
43
+ end
44
+
45
+ def save
46
+ context[:resource_instance_dir].save
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,28 @@
1
+ module HaveAPI::Fs::Components
2
+ class ListItem < Directory
3
+ component :list_item
4
+
5
+ def initialize(action, dir, data)
6
+ super()
7
+
8
+ @action = action
9
+ @dir = dir
10
+ @data = data
11
+ end
12
+
13
+ def contents
14
+ @action.params.keys.map(&:to_s)
15
+ end
16
+
17
+ protected
18
+ def new_child(name)
19
+ [
20
+ Parameter,
21
+ @action,
22
+ name,
23
+ @dir,
24
+ @data,
25
+ ]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ module HaveAPI::Fs::Components
2
+ class MdHelpFile < HelpFile
3
+ def initialize(*args)
4
+ super
5
+
6
+ @layout = ERB.new(::File.open(template_path('layout')).read, 0, '-')
7
+ @template = ERB.new(
8
+ ::File.open(template_path(@c.class)).read,
9
+ 0, '-'
10
+ )
11
+ end
12
+
13
+ def read
14
+ layout(@layout) do
15
+ @template.result(binding)
16
+ end
17
+ end
18
+
19
+ protected
20
+ def safe_print(str)
21
+ str.to_s.gsub('_', '\_')
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ module HaveAPI::Fs::Components
2
+ class MetaDir < Directory
3
+ def initialize(path)
4
+ super()
5
+ @path = path
6
+ end
7
+
8
+ def setup
9
+ super
10
+
11
+ @dir = ::Dir.new(@path)
12
+ end
13
+
14
+ def contents
15
+ @dir.entries[2..-1]
16
+ end
17
+
18
+ def times
19
+ st = ::File.stat(@path)
20
+ [st.atime, st.mtime, st.ctime]
21
+ end
22
+
23
+ protected
24
+ def new_child(name)
25
+ if child = super
26
+ return child
27
+ end
28
+
29
+ real_name = name.to_s
30
+ return unless contents.include?(real_name)
31
+
32
+ path = ::File.join(@dir.path, real_name)
33
+
34
+ if ::File.directory?(path)
35
+ [MetaDir, path]
36
+
37
+ else
38
+ [MetaFile, path]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ module HaveAPI::Fs::Components
2
+ class MetaFile < File
3
+ def initialize(path)
4
+ super()
5
+ @path = path
6
+ end
7
+
8
+ def times
9
+ st = ::File.stat(@path)
10
+ [st.atime, st.mtime, st.ctime]
11
+ end
12
+
13
+ def size
14
+ ::File.size(@path)
15
+ end
16
+
17
+ def read
18
+ ::File.read(@path)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,132 @@
1
+ require 'time'
2
+
3
+ module HaveAPI::Fs::Components
4
+ class Parameter < File
5
+ attr_reader :new_value
6
+
7
+ def initialize(action, name, dir, value = nil, opts = {})
8
+ super()
9
+
10
+ @action = action
11
+ @name = name
12
+ @dir = dir
13
+ @value = value
14
+ @set = false
15
+ @mirror = opts[:mirror]
16
+
17
+ if dir == :input
18
+ @params = @action.input_params
19
+
20
+ else
21
+ @params = @action.params
22
+ end
23
+
24
+ @desc = @params[@name]
25
+
26
+ if opts[:editable].nil?
27
+ @writable = dir == :input
28
+
29
+ else
30
+ @writable = opts[:editable]
31
+ end
32
+ end
33
+
34
+ def writable?
35
+ @writable
36
+ end
37
+
38
+ def read
39
+ str = value.to_s
40
+ str.empty? ? str : str + "\n"
41
+ end
42
+
43
+ def write(raw)
44
+ @set = true
45
+ str = raw.strip
46
+
47
+ if str.empty?
48
+ @new_value = nil
49
+ @mirror.write_safe(@new_value) if @mirror
50
+ return
51
+ end
52
+
53
+ @new_value = case @desc[:type]
54
+ when 'Resource'
55
+ str.to_i
56
+
57
+ when 'Boolean'
58
+ HaveAPI::Client::Parameters::Typed.Boolean.to_b(str)
59
+
60
+ when 'Integer'
61
+ str.to_i
62
+
63
+ when 'Float'
64
+ str.to_f
65
+
66
+ when 'Datetime'
67
+ Time.iso8601(str)
68
+
69
+ else
70
+ str.strip
71
+ end
72
+
73
+ changed
74
+ @mirror.write_safe(@new_value) if @mirror
75
+
76
+ rescue => e
77
+ @set = false
78
+ raise e
79
+ end
80
+
81
+ def write_safe(v)
82
+ @new_value = v
83
+ @set = true
84
+ changed
85
+ end
86
+
87
+ def value
88
+ # Value in ResourceInstance
89
+ # Value in hash
90
+ # Value changed, saved in instance variable
91
+
92
+ hash = @value.is_a?(::Hash)
93
+
94
+ if @desc[:type] == 'Resource'
95
+ return @new_value if @new_value
96
+ return nil if @value.nil?
97
+ return nil if @value.attributes[@name].nil?
98
+ @value.attributes[@name][@desc[:value_id].to_sym]
99
+
100
+ else
101
+ if @new_value
102
+ v = @new_value
103
+
104
+ else
105
+ return nil if @value.nil?
106
+
107
+ v = hash ? @value[@name] : @value.send(@name)
108
+ end
109
+
110
+ case @desc[:type]
111
+ when 'Boolean'
112
+ v ? 1 : 0
113
+
114
+ when 'Datetime'
115
+ v.is_a?(::Time) ? v.iso8601 : v
116
+
117
+ else
118
+ v
119
+ end
120
+
121
+ end
122
+ end
123
+
124
+ def set?
125
+ @set
126
+ end
127
+
128
+ def unsaved?(n = nil)
129
+ set?
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,9 @@
1
+ module HaveAPI::Fs::Components
2
+ # Used to open developer console.
3
+ class Pry < Executable
4
+ def exec
5
+ require 'pry'
6
+ binding.pry
7
+ end
8
+ end
9
+ end