yard-virtus 0.0.4

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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +75 -0
  5. data/Guardfile +12 -0
  6. data/LICENSE.txt +19 -0
  7. data/README.md +49 -0
  8. data/Rakefile +7 -0
  9. data/example/README.md +4 -0
  10. data/example/address.rb +7 -0
  11. data/example/city.rb +5 -0
  12. data/example/user.rb +9 -0
  13. data/lib/yard-virtus.rb +28 -0
  14. data/lib/yard/virtus/code_objects.rb +2 -0
  15. data/lib/yard/virtus/code_objects/attribute_reader.rb +26 -0
  16. data/lib/yard/virtus/code_objects/attribute_writer.rb +48 -0
  17. data/lib/yard/virtus/declarations.rb +4 -0
  18. data/lib/yard/virtus/declarations/options.rb +43 -0
  19. data/lib/yard/virtus/declarations/type.rb +65 -0
  20. data/lib/yard/virtus/declarations/virtus_attribute.rb +67 -0
  21. data/lib/yard/virtus/declarations/virtus_model.rb +33 -0
  22. data/lib/yard/virtus/handlers.rb +2 -0
  23. data/lib/yard/virtus/handlers/include_virtus_model.rb +32 -0
  24. data/lib/yard/virtus/handlers/virtus_attribute.rb +58 -0
  25. data/lib/yard/virtus/version.rb +5 -0
  26. data/spec/code_objects/attribute_reader_spec.rb +38 -0
  27. data/spec/code_objects/attribute_writer_spec.rb +75 -0
  28. data/spec/declarations/options_spec.rb +50 -0
  29. data/spec/declarations/type_spec.rb +37 -0
  30. data/spec/declarations/virtus_attribute_spec.rb +73 -0
  31. data/spec/declarations/virtus_model_spec.rb +35 -0
  32. data/spec/examples/include_virtus_model_001.rb.txt +15 -0
  33. data/spec/examples/virtus_attribute_001.rb.txt +67 -0
  34. data/spec/handlers/include_virtus_model_spec.rb +22 -0
  35. data/spec/handlers/virtus_attribute_spec.rb +71 -0
  36. data/spec/spec_helper.rb +65 -0
  37. data/spec/support/helpers/handler_helpers.rb +9 -0
  38. data/spec/support/helpers/parsing_helpers.rb +13 -0
  39. data/spec/support/matchers/attribute_matchers.rb +45 -0
  40. data/spec/support/matchers/method_type_matchers.rb +11 -0
  41. data/yard-virtus.gemspec +22 -0
  42. metadata +117 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: 9ffd190d1362aad92b5aa2fcccf2562f228b3447
4
+ data.tar.gz: 57e661c5aeb9d7701b2c20a4523fb4dfee843f64
5
+ SHA512:
6
+ metadata.gz: 36cf0432fe4431772a065fdbe398247f478b49a8edc546859a154efda5c0b38a3a9a153bf1630aea5bab2e1a2773efb185c01b8e5d84621893e1ad04e2d66fed
7
+ data.tar.gz: dbbc324988267db53eb72dc0b9a8d937c76a0c8ab71b1f80b7673a4e5f5e24646adc90e3f01b380c4296c008f6fae04268880856a3978db8fe8d61032ae8bc44
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ # Ignore bundler config and scripts/binaries
2
+ /.bundle
3
+ /.bin
4
+
5
+ # Ignore YARD run-off
6
+ .yardoc/
7
+
8
+ # Ignore automatically generated documentation
9
+ # for sample project
10
+ example/doc
11
+
12
+ .DS_Store
13
+ *~
14
+ *#
15
+ *.o
16
+ *.dylib
17
+ *.a
18
+ *.so
19
+ .#*
20
+ *.rbc
21
+ .rvmrc
22
+ tmp/
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "virtus", "= 1.0.2"
4
+ gem "yard", "= 0.8.7.4"
5
+
6
+ group :development do
7
+ gem "rspec"
8
+
9
+ gem "guard-rspec", "~> 4.2.9", require: false
10
+ gem "terminal-notifier-guard"
11
+
12
+ gem "rake"
13
+ gem "pry"
14
+
15
+ # Github Flavored Markdown support
16
+ gem "redcarpet"
17
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,75 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ axiom-types (0.1.1)
5
+ descendants_tracker (~> 0.0.4)
6
+ ice_nine (~> 0.11.0)
7
+ thread_safe (~> 0.3, >= 0.3.1)
8
+ celluloid (0.15.2)
9
+ timers (~> 1.1.0)
10
+ coderay (1.1.0)
11
+ coercible (1.0.0)
12
+ descendants_tracker (~> 0.0.1)
13
+ descendants_tracker (0.0.4)
14
+ thread_safe (~> 0.3, >= 0.3.1)
15
+ diff-lcs (1.2.5)
16
+ equalizer (0.0.9)
17
+ ffi (1.9.3)
18
+ formatador (0.2.5)
19
+ guard (2.6.1)
20
+ formatador (>= 0.2.4)
21
+ listen (~> 2.7)
22
+ lumberjack (~> 1.0)
23
+ pry (>= 0.9.12)
24
+ thor (>= 0.18.1)
25
+ guard-rspec (4.2.9)
26
+ guard (~> 2.1)
27
+ rspec (>= 2.14, < 4.0)
28
+ ice_nine (0.11.0)
29
+ listen (2.7.5)
30
+ celluloid (>= 0.15.2)
31
+ rb-fsevent (>= 0.9.3)
32
+ rb-inotify (>= 0.9)
33
+ lumberjack (1.0.6)
34
+ method_source (0.8.2)
35
+ pry (0.9.12.6)
36
+ coderay (~> 1.0)
37
+ method_source (~> 0.8)
38
+ slop (~> 3.4)
39
+ rake (10.3.2)
40
+ rb-fsevent (0.9.4)
41
+ rb-inotify (0.9.4)
42
+ ffi (>= 0.5.0)
43
+ redcarpet (3.1.2)
44
+ rspec (2.14.1)
45
+ rspec-core (~> 2.14.0)
46
+ rspec-expectations (~> 2.14.0)
47
+ rspec-mocks (~> 2.14.0)
48
+ rspec-core (2.14.8)
49
+ rspec-expectations (2.14.5)
50
+ diff-lcs (>= 1.1.3, < 2.0)
51
+ rspec-mocks (2.14.6)
52
+ slop (3.5.0)
53
+ terminal-notifier-guard (1.5.3)
54
+ thor (0.19.1)
55
+ thread_safe (0.3.3)
56
+ timers (1.1.0)
57
+ virtus (1.0.2)
58
+ axiom-types (~> 0.1)
59
+ coercible (~> 1.0)
60
+ descendants_tracker (~> 0.0.3)
61
+ equalizer (~> 0.0.9)
62
+ yard (0.8.7.4)
63
+
64
+ PLATFORMS
65
+ ruby
66
+
67
+ DEPENDENCIES
68
+ guard-rspec (~> 4.2.9)
69
+ pry
70
+ rake
71
+ redcarpet
72
+ rspec
73
+ terminal-notifier-guard
74
+ virtus (= 1.0.2)
75
+ yard (= 0.8.7.4)
data/Guardfile ADDED
@@ -0,0 +1,12 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+ notification :emacs
4
+
5
+ guard :rspec, cmd: "bundle exec rspec" do
6
+ watch(%r{^spec/(.+)/.+_spec\.rb$})
7
+
8
+ watch(%r{^lib/yard/virtus/(.+)/(.+)\.rb$}) { |m| "spec/#{m[1]}/#{m[2]}_spec.rb" }
9
+
10
+ watch('spec/spec_helper.rb') { "spec" }
11
+ watch(%r{spec/support/(.+)/*.rb$}) { "spec" }
12
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Dmitry Dzema
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # yard-virtus
2
+
3
+
4
+ This gem helps to generate YARD documentation for classes built using [Virtus](https://github.com/solnic/virtus). It extracts information about attributes, their types and writers so you don't need to specify it manually.
5
+
6
+ This gem depends on exact details of implementation of Virtus so it's locked to a particular version. In the future I wan to adapt versioning scheme for this gem which will reflect supported Virtus version.
7
+
8
+ ## Install
9
+
10
+ Add the gem to your Gemfile (inside development or documentation group):
11
+
12
+ ``` ruby
13
+ gem "yard-virtus", "= 0.0.4"
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ If you use YARD via Rake and `YARD::Rake::YardocTask` or via custom ruby script add
19
+
20
+ ``` ruby
21
+ require "yard-virtus"
22
+ ```
23
+
24
+ If you use YARD command line tool use `--plugin` switch like this
25
+
26
+ ```shell
27
+ yard --plugin virtus
28
+ ```
29
+
30
+ ## Work in Progress
31
+
32
+ This library is still work in progress and is not recommended for production use.
33
+
34
+ ### TODO
35
+
36
+ * Detect if class gets virtus functionality via inheritance (partially done).
37
+ * Attach documentation about various features inherited from Virtus to namespaces.
38
+ * Extract default values of attributes.
39
+
40
+ ## Notice
41
+
42
+ This library uses eval with $SAFE level 3 to evaluate list of options in
43
+ `attribute` declarations. Setting $SAFE to level 3 means it can not do
44
+ destructive operations on your file system but it can damage objects which
45
+ are already in memory.
46
+
47
+ ### Author
48
+
49
+ [Dmitry Dzema](https://github.com/DimaD) ([@dzema](https://twitter.com/dzema))
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require File.join(File.dirname(__FILE__), "lib", "yard-virtus")
2
+
3
+ desc "Build documentation for sample code set"
4
+ YARD::Rake::YardocTask.new("example") do |t|
5
+ t.files = ["example/**/*.rb"]
6
+ t.options = ["--no-private", "--markup-provider=redcarpet", "--markup=markdown", "--output-dir=./example/doc"]
7
+ end
data/example/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # virtus-yardoc example
2
+
3
+ This is a simple project to demonstrate virtus-yard features.
4
+ Code here is devised from main Virtus reame.
@@ -0,0 +1,7 @@
1
+ class Address
2
+ include Virtus.model
3
+
4
+ attribute :street, String
5
+ attribute :zipcode, String
6
+ attribute :city, City
7
+ end
data/example/city.rb ADDED
@@ -0,0 +1,5 @@
1
+ class City
2
+ include Virtus.model
3
+
4
+ attribute :name, String
5
+ end
data/example/user.rb ADDED
@@ -0,0 +1,9 @@
1
+ class User
2
+ include Virtus.model
3
+
4
+ attribute :name, String
5
+ attribute :age, Integer
6
+
7
+ # User can have work address or home address
8
+ attribute :addresses, Array[Address]
9
+ end
@@ -0,0 +1,28 @@
1
+ require "virtus"
2
+ require "yard"
3
+
4
+ _dir = File.dirname(__FILE__)
5
+ require File.join(_dir, "yard", "virtus", "version")
6
+
7
+ require File.join(_dir, "yard", "virtus", "handlers")
8
+ require File.join(_dir, "yard", "virtus", "declarations")
9
+ require File.join(_dir, "yard", "virtus", "code_objects")
10
+
11
+ # According to ruby conventions if library is called
12
+ # `yard-virtus` it should be included via
13
+ #
14
+ # require "yard/virtus"
15
+ #
16
+ # But YARD plugins do not follow this convention and require
17
+ # gem names like `yard-*` and being required by the same name.
18
+ # Hence this confusing filename.
19
+
20
+ # module YARD
21
+ # module Registry
22
+ # def self.register(object)
23
+
24
+ # return if object.is_a?(YARD::CodeObjects::Proxy)
25
+ # thread_local_store[object.path] = object
26
+ # end
27
+ # end
28
+ # end
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), "code_objects", "attribute_reader")
2
+ require File.join(File.dirname(__FILE__), "code_objects", "attribute_writer")
@@ -0,0 +1,26 @@
1
+ module YARD
2
+ module Virtus
3
+ module CodeObjects
4
+ class AttributeReader
5
+ attr_reader :attr_name, :type
6
+
7
+ # @param [Symbol] attr name of attribute
8
+ # @param [String] type string representation of type in YARD format
9
+ def initialize(attr, type)
10
+ @attr_name = attr
11
+ @type = type
12
+ end
13
+
14
+ def method_name
15
+ attr_name
16
+ end
17
+
18
+ def yard_method_object(namespace)
19
+ YARD::CodeObjects::MethodObject.new(namespace, method_name, :instance).tap do |mo|
20
+ mo.add_tag YARD::Tags::Library.new.return_tag("[#{type}]")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ module YARD
2
+ module Virtus
3
+ module CodeObjects
4
+ class AttributeWriter
5
+ attr_reader :attr_name, :type
6
+
7
+ # @param [Symbol] attr name of attribute
8
+ # @param [String] type string representation of type in YARD format
9
+ # @param [Boolean] is_private indicates if writer is part of private API
10
+ def initialize(attr, type, is_private=false)
11
+ @attr_name = attr
12
+ @type = type
13
+ @is_private = is_private
14
+ end
15
+
16
+ def method_name
17
+ :"#{attr_name}="
18
+ end
19
+
20
+ def yard_method_object(namespace)
21
+ YARD::CodeObjects::MethodObject.new(namespace, method_name, :instance).tap do |mo|
22
+ mo.parameters = [["value", default_value]]
23
+ mo.add_tag param_tag("value", type)
24
+ mo.add_tag private_tag if private?
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def default_value
31
+ nil
32
+ end
33
+
34
+ def private?
35
+ !!@is_private
36
+ end
37
+
38
+ def param_tag(param_name, param_type)
39
+ YARD::Tags::Tag.new("param", nil, param_type, param_name)
40
+ end
41
+
42
+ def private_tag
43
+ YARD::Tags::Tag.new("private", "")
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,4 @@
1
+ require File.join(File.dirname(__FILE__), "declarations", "virtus_model")
2
+ require File.join(File.dirname(__FILE__), "declarations", "virtus_attribute")
3
+ require File.join(File.dirname(__FILE__), "declarations", "type")
4
+ require File.join(File.dirname(__FILE__), "declarations", "options")
@@ -0,0 +1,43 @@
1
+ module YARD
2
+ module Virtus
3
+ module Declarations
4
+ # VirtusModel declaration wraps AST which represents
5
+ # options hash.
6
+ class Options
7
+ attr_reader :ast
8
+
9
+ # @params [YARD::Parser::Ruby::AstNode, nil] ast
10
+ def initialize(ast)
11
+ @ast = ast
12
+ @data = if ast.kind_of?(YARD::Parser::Ruby::AstNode)
13
+ safe_eval("{#{ast.source}}")
14
+ else
15
+ {}
16
+ end
17
+ end
18
+
19
+ def [](key)
20
+ data[key]
21
+ end
22
+
23
+ def empty?
24
+ data.empty?
25
+ end
26
+
27
+ protected
28
+ attr_reader :data
29
+
30
+ # It's not the best idea to use eval but
31
+ # interpreting AST tree to search for hash
32
+ # values is not fun either.
33
+ # @todo replace with AST tree interpreter
34
+ def safe_eval(source)
35
+ proc do |src|
36
+ $SAFE = 3;
37
+ eval(src)
38
+ end.call(source.untaint)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,65 @@
1
+ module YARD
2
+ module Virtus
3
+ module Declarations
4
+ # Type declaration wraps AST which represents
5
+ # type of Virtus attribute
6
+ #
7
+ # @example
8
+ # ast = s(:call, s(:var_ref, s(:const, "Virtus")), :".", s(:ident, "model"))
9
+ # d = VirtusModel.new(ast)
10
+ # d.module_proxies_in_ns(ns) # => P("Virtus.model")
11
+ #
12
+ class Type
13
+ attr_reader :ast
14
+
15
+ # @params [YARD::Parser::Ruby::ReferenceNode, YARD::Parser::Ruby::AstNode] ast
16
+ def initialize(ast)
17
+ @ast = ast
18
+ end
19
+
20
+ def yard_type_string
21
+ if association?(ast)
22
+ yard_type_from_association(ast)
23
+ elsif collection?(ast)
24
+ yard_type_from_collection(ast)
25
+ elsif ast.ref?
26
+ yard_type_from_reference(ast)
27
+ elsif !ast[0].nil?
28
+ Type.new(ast[0]).yard_type_string
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ protected
35
+ def yard_type_from_reference(tree)
36
+ tree.path.join("::")
37
+ end
38
+
39
+ def yard_type_from_association(tree)
40
+ collection = tree[0].jump(:var_ref)
41
+ key, value = extract_kv_from_association(ast[1])
42
+
43
+ "%s{%s => %s}" % [collection, key, value].map { |type| Type.new(type).yard_type_string }
44
+ end
45
+
46
+ def yard_type_from_collection(ast)
47
+ "%s<%s>" % [ast[0], ast[1]].map { |type| Type.new(type).yard_type_string }
48
+ end
49
+
50
+ def association?(tree)
51
+ tree.jump(:assoc) != tree
52
+ end
53
+
54
+ def collection?(tree)
55
+ tree.type == :aref
56
+ end
57
+ def extract_kv_from_association(tree)
58
+ assoc = tree.jump(:assoc)
59
+
60
+ return assoc[0], assoc[1]
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end