haveapi-fs 0.1.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 (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