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
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ describe YARD::Virtus::Declarations::VirtusModel do
4
+ let(:ast) { ruby_ast(declaration) }
5
+ let(:namespace) { YARD::CodeObjects::ClassObject.new(nil, "TemporarySpecClass") }
6
+ let(:subject) { described_class.new(ast) }
7
+
8
+ context "when declaration is 'Virtus.model'" do
9
+ let(:declaration) { "Virtus.model" }
10
+
11
+ it "has one module proxy" do
12
+ expect(subject.module_proxies_in_ns(namespace)).to have(1).item
13
+ end
14
+
15
+ it "has module proxy for Virtus.model" do
16
+ expect(subject.module_proxies_in_ns(namespace)).to include(P("Virtus.model"))
17
+ end
18
+ end
19
+
20
+ context "when declaration is 'Virtus.value_object'" do
21
+ let(:declaration) { "Virtus.value_object" }
22
+
23
+ it "has module proxy for Virtus.value_object" do
24
+ expect(subject.module_proxies_in_ns(namespace)).to include(P("Virtus.value_object"))
25
+ end
26
+ end
27
+
28
+ context "when declaration is 'Virtus.model(mass_assignmet: false)'" do
29
+ let(:declaration) { "Virtus.model(mass_assignment: false)" }
30
+
31
+ it "has module proxy for Virtus.model" do
32
+ expect(subject.module_proxies_in_ns(namespace)).to include(P("Virtus.model"))
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ class A
2
+ include Model
3
+ end
4
+
5
+ class ModelA
6
+ include Virtus.model
7
+ end
8
+
9
+ class ModelB
10
+ include Virtus.value_object
11
+ end
12
+
13
+ class ModelC
14
+ include Virtus.model(mass_assignmet: false)
15
+ end
@@ -0,0 +1,67 @@
1
+ class Address
2
+ attribute :city, String
3
+ end
4
+
5
+ class User
6
+ include Virtus.model
7
+
8
+ attribute :name, String
9
+ attribute :age, Integer
10
+
11
+ attribute :friends, Array[User]
12
+ attribute :addresses, Hash[Symbol => Address]
13
+
14
+ attribute :unique_id, String, :writer => :private
15
+
16
+ # keeps extracted type if comment is present
17
+ attribute :email, Email
18
+
19
+ # preserves private attribute if comment is present
20
+ attribute :unique_uuid, String, :writer => :private
21
+ end
22
+
23
+ class Page
24
+ include Virtus.model
25
+
26
+ attribute :title, String
27
+
28
+ # default from a singleton value (integer in this case)
29
+ attribute :views, Integer, :default => 0
30
+
31
+ # default from a singleton value (boolean in this case)
32
+ attribute :published, Boolean, :default => false
33
+
34
+ # default from a callable object (proc in this case)
35
+ attribute :slug, String, :default => lambda { |page, attribute| page.title.downcase.gsub(' ', '-') }
36
+
37
+ # default from a method name as symbol
38
+ attribute :editor_title, String, :default => :default_editor_title
39
+ end
40
+
41
+ class BaseVirtusModel
42
+ include Virtus.model
43
+ end
44
+
45
+ class City < BaseVirtusModel
46
+ attribute :name, String
47
+ end
48
+
49
+ # Mixin is not defined yet which can
50
+ # happen in real world setup when files are
51
+ # scanned in the order which does not match
52
+ # their dependencies order.
53
+ class District
54
+ include ModelMixin
55
+
56
+ attribute :city, City
57
+ end
58
+
59
+ module ModelMixin
60
+ include Virtus.model
61
+ end
62
+
63
+ class Country
64
+ include ModelMixin
65
+
66
+ attribute :cities, Array[City]
67
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ describe YARD::Virtus::Handlers::IncludeVirtusModel, type: :handler do
4
+ before(:all) { parse_file! :include_virtus_model_001 }
5
+
6
+ it "processes basic model declaration" do
7
+ expect(YARD::Registry.at(:ModelA).instance_mixins).to include(P("Virtus.model"))
8
+ end
9
+
10
+ it "processes value object delcaration" do
11
+ expect(YARD::Registry.at(:ModelB).instance_mixins).to include(P("Virtus.value_object"))
12
+ end
13
+
14
+ it "processes model declaration with parameters" do
15
+ expect(YARD::Registry.at(:ModelC).instance_mixins).to include(P("Virtus.model"))
16
+ end
17
+
18
+ it "marks model namespace as supporting virtus attributes" do
19
+ namespace = YARD::Registry.at(:ModelA)
20
+ expect(namespace[:supports_virtus_attributes]).to be_true
21
+ end
22
+ end
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+
3
+ describe YARD::Virtus::Handlers::VirtusAttribute, type: :handler do
4
+ before(:all) { parse_file! :virtus_attribute_001 }
5
+
6
+ it "does not parse attribute if namespace does not include Virtus declaration" do
7
+ expect(YARD::Registry.at(:Address)).not_to define_readable_attribute(:city)
8
+ expect(YARD::Registry.at(:Address)).not_to define_writable_attribute(:city)
9
+ end
10
+
11
+ it "parses attribute name" do
12
+ expect(YARD::Registry.at(:User)).to define_readable_attribute(:name)
13
+ expect(YARD::Registry.at(:User)).to define_writable_attribute(:name)
14
+ end
15
+
16
+ it "parses attribute with scalar type" do
17
+ expect(YARD::Registry.at("User#name")).to have_return_type("String")
18
+ end
19
+
20
+ it "parses attribute with collection type" do
21
+ expect(YARD::Registry.at("User#friends")).to have_return_type("Array<User>")
22
+ end
23
+
24
+ it "parses attribute with collection map type" do
25
+ expect(YARD::Registry.at("User#addresses")).to have_return_type("Hash{Symbol => Address}")
26
+ end
27
+
28
+ it "parses and marks attributes with private writers" do
29
+ expect(YARD::Registry.at("User#unique_id=")).to have_private_writer_api
30
+ end
31
+
32
+ it "parses type of attribute with attached docstring" do
33
+ expect(YARD::Registry.at("User#email")).to have_return_type("Email")
34
+ end
35
+
36
+ it "parses information about private writers with attached docstring" do
37
+ expect(YARD::Registry.at("User#unique_uuid=")).to have_private_writer_api
38
+ end
39
+
40
+ context "when namespace becomes virtus model via inheritance chain" do
41
+ context "from parent class" do
42
+ it "parses attribute declaration" do
43
+ expect(YARD::Registry.at(:City)).to define_readable_attribute(:name)
44
+ expect(YARD::Registry.at(:City)).to define_writable_attribute(:name)
45
+ end
46
+ end
47
+
48
+ context "from mixin" do
49
+ context "and mixin have already been defined when processing declaration" do
50
+ it "parses attribute declaration" do
51
+ expect(YARD::Registry.at(:Country)).to define_readable_attribute(:cities)
52
+ expect(YARD::Registry.at(:Country)).to define_writable_attribute(:cities)
53
+ end
54
+ end
55
+
56
+ context "and mixin have not been defined when processing delcaration" do
57
+ it "parses attribute declaration" do
58
+ pending
59
+
60
+ expect(YARD::Registry.at(:District)).to define_readable_attribute(:city)
61
+ expect(YARD::Registry.at(:District)).to define_writable_attribute(:city)
62
+ end
63
+ end
64
+ end
65
+ end # context "when namespace becomes virtus model via inheritance chain" do
66
+
67
+ it "parses default value when it is singleton" do
68
+ expect(YARD::Registry.at("Page#views")).to have_default_value(0)
69
+ expect(YARD::Registry.at("Page#published")).to have_default_value(false)
70
+ end
71
+ end
@@ -0,0 +1,65 @@
1
+ require "stringio"
2
+
3
+ _dir = File.dirname(__FILE__)
4
+
5
+ # Requires supporting ruby files with custom matchers and macros, etc,
6
+ # in spec/support/ and its subdirectories.
7
+ Dir[File.join(_dir, "support", "**", "*.rb")].each do |f|
8
+ require f
9
+ end
10
+
11
+ require File.join(_dir, "..", "lib", "yard-virtus")
12
+
13
+ # Shortcut for creating a YARD::CodeObjects::Proxy via a path
14
+ #
15
+ # @see YARD::CodeObjects::Proxy
16
+ # @see YARD::Registry.resolve
17
+ def P(namespace, name = nil, type = nil)
18
+ namespace, name = nil, namespace if name.nil?
19
+ YARD::Registry.resolve(namespace, name, false, true, type)
20
+ end
21
+
22
+ RSpec.configure do |config|
23
+ # Run specs in random order to surface order dependencies. If you find an
24
+ # order dependency and want to debug it, you can fix the order by providing
25
+ # the seed, which is printed after each run.
26
+ # --seed 1234
27
+ config.order = "random"
28
+ config.color = true
29
+
30
+ config.include VirtusYARD::Spec::HandlerHelpers, :type => :handler
31
+ config.include VirtusYARD::Spec::ParsingHelpers
32
+
33
+ config.before(:all) { YARD::Logger.instance(StringIO.new) }
34
+ end
35
+
36
+ def parse_file!(file, thisfile = __FILE__, log_level = log.level, ext = '.rb.txt')
37
+ YARD::Registry.clear
38
+ path = File.join(File.dirname(thisfile), 'examples', file.to_s + ext)
39
+ YARD::Parser::SourceParser.parse(path, [], log_level)
40
+ end
41
+
42
+ class StubbedProcessor < YARD::Handlers::Processor
43
+ def process(statements)
44
+ statements.each_with_index do |stmt, index|
45
+ find_handlers(stmt).each do |handler|
46
+ handler.new(self, stmt).process
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ class StubbedSourceParser < YARD::Parser::SourceParser
53
+ StubbedSourceParser.parser_type = :ruby
54
+ def post_process
55
+ post = StubbedProcessor.new(self)
56
+ post.process(@parser.enumerator)
57
+ end
58
+ end
59
+
60
+ def with_parser(parser_type, &block)
61
+ tmp = StubbedSourceParser.parser_type
62
+ StubbedSourceParser.parser_type = parser_type
63
+ yield
64
+ StubbedSourceParser.parser_type = tmp
65
+ end
@@ -0,0 +1,9 @@
1
+ module VirtusYARD
2
+ module Spec
3
+ module HandlerHelpers
4
+ def r(key)
5
+ YARD::P(key)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module VirtusYARD
2
+ module Spec
3
+ module ParsingHelpers
4
+ def parser_for(code)
5
+ YARD::Parser::Ruby::RubyParser.new(code, "virtus-yard-spec.rb")
6
+ end
7
+
8
+ def ruby_ast(statement)
9
+ parser_for(statement).parse.ast[0]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,45 @@
1
+ RSpec::Matchers.define :define_readable_attribute do |attr_name|
2
+ match do |namespace|
3
+ expect(namespace).to be_kind_of(YARD::CodeObjects::NamespaceObject)
4
+
5
+ rname = namespace.to_s + "#" + attr_name.to_s
6
+ reader_object = YARD::Registry.at(rname)
7
+ attributes_map = namespace.attributes[:instance][attr_name]
8
+
9
+ expect(reader_object).to be_instance_of(YARD::CodeObjects::MethodObject)
10
+ expect(attributes_map[:read]).to eq(reader_object)
11
+ end
12
+
13
+ description do |attr_name|
14
+ "define readable attribute #{attr_name} in #{namespace.source}"
15
+ end
16
+ end
17
+
18
+ RSpec::Matchers.define :define_writable_attribute do |attr_name|
19
+ match do |namespace|
20
+ expect(namespace).to be_kind_of(YARD::CodeObjects::NamespaceObject)
21
+
22
+ wname = namespace.to_s + "#" + attr_name.to_s + "="
23
+ writer_object = YARD::Registry.at(wname)
24
+ attributes_map = namespace.attributes[:instance][attr_name]
25
+
26
+ expect(writer_object).to be_instance_of(YARD::CodeObjects::MethodObject)
27
+ expect(attributes_map[:write]).to eq(writer_object)
28
+ end
29
+
30
+ description do
31
+ "define writable attribute #{attr_name}"
32
+ end
33
+ end
34
+
35
+ RSpec::Matchers.define :have_private_writer_api do
36
+ match do |method_object|
37
+ expect(method_object.tags(:private).size).to be > 0
38
+ end
39
+ end
40
+
41
+ RSpec::Matchers.define :have_default_value do |default_value|
42
+ match do |method_object|
43
+ expect(true).to be_true
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ # @example
2
+ # expect(YARD::Registry.at("User#friends")).to have_return_type("Array<User>")
3
+ RSpec::Matchers.define :have_return_type do |yard_type_string|
4
+ match do |method_object|
5
+ return_tags = method_object.tags(:return)
6
+
7
+ expect(return_tags.size).to be > 0
8
+
9
+ expect(return_tags.any? { |tag| tag.types.include?(yard_type_string) }).to be_true
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path("../lib/yard/virtus/version", __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "yard-virtus"
7
+ gem.version = YARD::Virtus::VERSION
8
+ gem.authors = ["Dmitry Dzema"]
9
+ gem.email = ["dimad.ag@gmail.com"]
10
+ gem.homepage = "https://github.com/DimaD/yard-virtus"
11
+ gem.summary = "This library provides handlers to YARD so it can extract information about Virtus attributes like types and visibility"
12
+ gem.description = gem.summary
13
+ gem.license = "MIT"
14
+
15
+ gem.require_paths = ["lib"]
16
+ gem.files = `git ls-files`.split("\n")
17
+ gem.test_files = `git ls-files -- {spec}/*`.split("\n")
18
+ gem.extra_rdoc_files = %w[LICENSE.txt README.md]
19
+
20
+ gem.add_dependency("virtus", "= 1.0.2")
21
+ gem.add_dependency("yard", "~> 0.8.7.4")
22
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yard-virtus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Dmitry Dzema
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: virtus
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.8.7.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.8.7.4
41
+ description: This library provides handlers to YARD so it can extract information
42
+ about Virtus attributes like types and visibility
43
+ email:
44
+ - dimad.ag@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files:
48
+ - LICENSE.txt
49
+ - README.md
50
+ files:
51
+ - .gitignore
52
+ - Gemfile
53
+ - Gemfile.lock
54
+ - Guardfile
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - example/README.md
59
+ - example/address.rb
60
+ - example/city.rb
61
+ - example/user.rb
62
+ - lib/yard-virtus.rb
63
+ - lib/yard/virtus/code_objects.rb
64
+ - lib/yard/virtus/code_objects/attribute_reader.rb
65
+ - lib/yard/virtus/code_objects/attribute_writer.rb
66
+ - lib/yard/virtus/declarations.rb
67
+ - lib/yard/virtus/declarations/options.rb
68
+ - lib/yard/virtus/declarations/type.rb
69
+ - lib/yard/virtus/declarations/virtus_attribute.rb
70
+ - lib/yard/virtus/declarations/virtus_model.rb
71
+ - lib/yard/virtus/handlers.rb
72
+ - lib/yard/virtus/handlers/include_virtus_model.rb
73
+ - lib/yard/virtus/handlers/virtus_attribute.rb
74
+ - lib/yard/virtus/version.rb
75
+ - spec/code_objects/attribute_reader_spec.rb
76
+ - spec/code_objects/attribute_writer_spec.rb
77
+ - spec/declarations/options_spec.rb
78
+ - spec/declarations/type_spec.rb
79
+ - spec/declarations/virtus_attribute_spec.rb
80
+ - spec/declarations/virtus_model_spec.rb
81
+ - spec/examples/include_virtus_model_001.rb.txt
82
+ - spec/examples/virtus_attribute_001.rb.txt
83
+ - spec/handlers/include_virtus_model_spec.rb
84
+ - spec/handlers/virtus_attribute_spec.rb
85
+ - spec/spec_helper.rb
86
+ - spec/support/helpers/handler_helpers.rb
87
+ - spec/support/helpers/parsing_helpers.rb
88
+ - spec/support/matchers/attribute_matchers.rb
89
+ - spec/support/matchers/method_type_matchers.rb
90
+ - yard-virtus.gemspec
91
+ homepage: https://github.com/DimaD/yard-virtus
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.2.2
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: This library provides handlers to YARD so it can extract information about
115
+ Virtus attributes like types and visibility
116
+ test_files: []
117
+ has_rdoc: