specify_cli 0.0.5

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 (106) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +17 -0
  5. data/Gemfile.lock +117 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.rdoc +43 -0
  8. data/Rakefile +15 -0
  9. data/bin/specify_cli +248 -0
  10. data/lib/specify.rb +45 -0
  11. data/lib/specify/branch_parser.rb +85 -0
  12. data/lib/specify/cli.rb +11 -0
  13. data/lib/specify/cli/database_setup.rb +46 -0
  14. data/lib/specify/cli/stubs.rb +63 -0
  15. data/lib/specify/cli/viewset.rb +21 -0
  16. data/lib/specify/configuration.rb +12 -0
  17. data/lib/specify/configuration/config.rb +120 -0
  18. data/lib/specify/configuration/db_config.rb +162 -0
  19. data/lib/specify/configuration/host_config.rb +37 -0
  20. data/lib/specify/database.rb +140 -0
  21. data/lib/specify/models.rb +43 -0
  22. data/lib/specify/models/accession.rb +33 -0
  23. data/lib/specify/models/agent.rb +138 -0
  24. data/lib/specify/models/app_resource_data.rb +32 -0
  25. data/lib/specify/models/app_resource_dir.rb +43 -0
  26. data/lib/specify/models/auto_numbering_scheme.rb +94 -0
  27. data/lib/specify/models/collecting_event.rb +38 -0
  28. data/lib/specify/models/collection.rb +67 -0
  29. data/lib/specify/models/collection_object.rb +127 -0
  30. data/lib/specify/models/createable.rb +21 -0
  31. data/lib/specify/models/determination.rb +63 -0
  32. data/lib/specify/models/discipline.rb +61 -0
  33. data/lib/specify/models/division.rb +26 -0
  34. data/lib/specify/models/geography.rb +5 -0
  35. data/lib/specify/models/geography/administrative_division.rb +32 -0
  36. data/lib/specify/models/geography/geographic_name.rb +66 -0
  37. data/lib/specify/models/geography/geography.rb +23 -0
  38. data/lib/specify/models/institution.rb +13 -0
  39. data/lib/specify/models/locality.rb +50 -0
  40. data/lib/specify/models/preparation.rb +53 -0
  41. data/lib/specify/models/preparation_type.rb +30 -0
  42. data/lib/specify/models/record_set.rb +55 -0
  43. data/lib/specify/models/record_set_item.rb +29 -0
  44. data/lib/specify/models/taxonomy.rb +6 -0
  45. data/lib/specify/models/taxonomy/common_name.rb +14 -0
  46. data/lib/specify/models/taxonomy/rank.rb +31 -0
  47. data/lib/specify/models/taxonomy/taxon.rb +54 -0
  48. data/lib/specify/models/taxonomy/taxonomy.rb +21 -0
  49. data/lib/specify/models/tree_queryable.rb +55 -0
  50. data/lib/specify/models/updateable.rb +20 -0
  51. data/lib/specify/models/user.rb +104 -0
  52. data/lib/specify/models/view_set_object.rb +32 -0
  53. data/lib/specify/number_format.rb +60 -0
  54. data/lib/specify/services.rb +18 -0
  55. data/lib/specify/services/service.rb +51 -0
  56. data/lib/specify/services/stub_generator.rb +291 -0
  57. data/lib/specify/services/view_loader.rb +177 -0
  58. data/lib/specify/session.rb +77 -0
  59. data/lib/specify/user_type.rb +61 -0
  60. data/lib/specify/version.rb +19 -0
  61. data/man/specify_cli-database.1 +60 -0
  62. data/man/specify_cli-database.1.html +137 -0
  63. data/man/specify_cli-database.1.ronn +53 -0
  64. data/man/specify_cli-repository.1 +55 -0
  65. data/man/specify_cli-repository.1.html +128 -0
  66. data/man/specify_cli-repository.1.ronn +42 -0
  67. data/man/specify_cli-stubs.1 +177 -0
  68. data/man/specify_cli-stubs.1.html +239 -0
  69. data/man/specify_cli-stubs.1.ronn +147 -0
  70. data/man/specify_cli-viewset.1 +92 -0
  71. data/man/specify_cli-viewset.1.html +154 -0
  72. data/man/specify_cli-viewset.1.ronn +72 -0
  73. data/man/specify_cli.1 +213 -0
  74. data/man/specify_cli.1.html +252 -0
  75. data/man/specify_cli.1.ronn +157 -0
  76. data/spec/branch_parser_spec.rb +94 -0
  77. data/spec/cli/stubs_spec.rb +44 -0
  78. data/spec/configuration/config_spec.rb +269 -0
  79. data/spec/configuration/db_config_spec.rb +299 -0
  80. data/spec/configuration/host_config_spec.rb +64 -0
  81. data/spec/database_spec.rb +83 -0
  82. data/spec/examples.txt +217 -0
  83. data/spec/helpers.rb +15 -0
  84. data/spec/models/app_resource_data_spec.rb +38 -0
  85. data/spec/models/app_resource_dir_spec.rb +8 -0
  86. data/spec/models/auto_numbering_scheme_spec.rb +78 -0
  87. data/spec/models/collection_object_spec.rb +92 -0
  88. data/spec/models/collection_spec.rb +32 -0
  89. data/spec/models/discipline_spec.rb +31 -0
  90. data/spec/models/record_set_spec.rb +18 -0
  91. data/spec/models/user_spec.rb +182 -0
  92. data/spec/models/view_set_object_spec.rb +70 -0
  93. data/spec/number_format_spec.rb +43 -0
  94. data/spec/services/stub_generator_spec.rb +635 -0
  95. data/spec/services/view_loader_spec.rb +436 -0
  96. data/spec/session_spec.rb +105 -0
  97. data/spec/spec_helper.rb +116 -0
  98. data/spec/support/db.yml +12 -0
  99. data/spec/support/stub.yaml +17 -0
  100. data/spec/support/stub_locality.yaml +19 -0
  101. data/spec/support/viewsets/paleo.views.xml +30 -0
  102. data/spec/support/viewsets/paleo.xml +30 -0
  103. data/spec/user_type_spec.rb +79 -0
  104. data/specify_cli.gemspec +27 -0
  105. data/specify_cli.rdoc +1 -0
  106. metadata +246 -0
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Specify
4
+ module Configuration
5
+ # HostConfigs are configurations that map file directories to host names.
6
+ class HostConfig < Config
7
+ # Retutrns a new HostConfig for +file+ (a YAML file).
8
+ def initialize(file = nil)
9
+ super(file)
10
+ @saved = true
11
+ end
12
+
13
+ # Returns +true+ if +directory+ is mapped to a host name.
14
+ def directory?(directory)
15
+ params.key? directory
16
+ end
17
+
18
+ # Maps +directory+ to +host+.
19
+ def map_directory(directory, host)
20
+ raise "Directory '#{directory}' already mapped" if params[directory]
21
+ params[directory] = host
22
+ touch
23
+ end
24
+
25
+ # Returns a Hash with the parameters for the current directory mappings
26
+ # from the configuration YAML file.
27
+ def params
28
+ super.fetch :dir_names
29
+ end
30
+
31
+ # Returns the host name that is mapped to +directory+
32
+ def resolve_host(directory)
33
+ params.fetch directory
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Specify
4
+ # Databases represent _Specify_ database.
5
+ class Database
6
+ # The Sequel::Database instance for the current connection.
7
+ attr_accessor :connection
8
+
9
+ # The name of the _Specify_ database.
10
+ attr_reader :database
11
+
12
+ # The name of the MySQL/MariaDB host for the _Specify_ database.
13
+ attr_reader :host
14
+
15
+ # The port for the MySQL/MariaDB server for the _Specify_ database.
16
+ attr_reader :port
17
+
18
+ # An Array of Specify::Session instances currently registered (and open).
19
+ attr_reader :sessions
20
+
21
+ # The MySQL/MariaDB user for the database. This is typically the _Specify_
22
+ # <em>master user</em>.
23
+ attr_reader :user
24
+
25
+ # Creates a new Database from +config_file+ file (a _YAML_ file).
26
+ #
27
+ # +config_file+ should have the structure:
28
+ # ---
29
+ # :hosts:
30
+ # <hostname>:
31
+ # :port: <port_number>
32
+ # :databases:
33
+ # <database_name>:
34
+ # :db_user:
35
+ # :name: <mysql_username>
36
+ # :password: <password>
37
+ # :sp_user: <specify_username>
38
+ #
39
+ # Items prefices with +:+ will be deserialized as symbols. Leave
40
+ # +:password:+ blank to be prompted.
41
+ #
42
+ # +host+: the name of the MySQL/MariaDB host for the database.
43
+ # +database+: the name of the MySQL/MariaDB database.
44
+ def self.load_config(host, database, config_file = nil)
45
+ config = Configuration::DBConfig.new(host, database, config_file)
46
+ new(database, config.connection)
47
+ end
48
+
49
+ # Returns a new Database for the +database+ (the name of the database) on
50
+ # +host+.
51
+ #
52
+ # +port+: the port for the MySQL/MariaDB server for the database.
53
+ #
54
+ # +user+: the MySQL/MariaDB user (typically the _Specify_
55
+ # <em>master user</em>).
56
+ #
57
+ # +password+: the password for the MySQL/MariaDB user.
58
+ def initialize(database,
59
+ host: 'localhost',
60
+ port: 3306,
61
+ user: 'root',
62
+ password: nil)
63
+ @database = database
64
+ @host = host
65
+ @port = port
66
+ @user = user
67
+ @password = password || prompt
68
+ @connection = nil
69
+ @sessions = []
70
+ ObjectSpace.define_finalizer(self, method(:close))
71
+ yield(self) if block_given?
72
+ end
73
+
74
+ # Adds a new Session to the sessions pool.
75
+ def <<(session)
76
+ session.open
77
+ session.add_observer self
78
+ sessions << session
79
+ end
80
+
81
+ # Closes all sessions.
82
+ def close
83
+ return if sessions.empty?
84
+ sessions.each do |session|
85
+ session.close
86
+ session.delete_observer self
87
+ end
88
+ # TODO: should close database connection
89
+ end
90
+
91
+ # Establishes a connection and creates the object if it does not already
92
+ # exist. Loads all Specify::Model classes.
93
+ #
94
+ # Returns the Sequel::Database object for the database.
95
+ def connect
96
+ return connection if connection
97
+ @connection = Sequel.connect adapter: :mysql2,
98
+ user: @user,
99
+ password: @password,
100
+ host: @host,
101
+ port: @port,
102
+ database: @database
103
+ require_relative 'models'
104
+ connection
105
+ end
106
+
107
+ # Creates a string representation of +self+.
108
+ def inspect
109
+ "#{self} database: #{@database}, host: #{@host}, port: #{@port}"\
110
+ ", user: #{@user}, connected: #{connection ? true : false}"
111
+ end
112
+
113
+ # Createas a new Session for +user+ (String, an existing
114
+ # Specify::Model::User#name) in +collection+ (String, an existing
115
+ # Specify::Model::Collection#name) and adds it to the #sessions pool.
116
+ #
117
+ # Returns the new Session.
118
+ def start_session(user, collection)
119
+ connect
120
+ session = Session.new user, collection
121
+ self << session
122
+ session
123
+ end
124
+
125
+ # Deletes a +session+ (a Session) from the sessions pool when +session+
126
+ # has been closed.
127
+ def update(session)
128
+ sessions.delete session
129
+ end
130
+
131
+ private
132
+
133
+ def prompt
134
+ print 'password: '
135
+ password = STDIN.noecho(&:gets).chomp
136
+ puts
137
+ password
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.extension :inflector
4
+
5
+ Sequel.inflections do |inflect|
6
+ inflect.irregular 'taxon', 'taxa'
7
+ end
8
+
9
+ String.inflections do |inflect|
10
+ inflect.irregular 'taxon', 'taxa'
11
+ end
12
+
13
+ require_relative 'models/tree_queryable'
14
+ require_relative 'models/createable'
15
+ require_relative 'models/updateable'
16
+ require_relative 'models/accession'
17
+ require_relative 'models/agent'
18
+ require_relative 'models/app_resource_data'
19
+ require_relative 'models/app_resource_dir'
20
+ require_relative 'models/auto_numbering_scheme'
21
+ require_relative 'models/collecting_event'
22
+ require_relative 'models/collection'
23
+ require_relative 'models/collection_object'
24
+ require_relative 'models/determination'
25
+ require_relative 'models/discipline'
26
+ require_relative 'models/division'
27
+ require_relative 'models/geography'
28
+ require_relative 'models/institution'
29
+ require_relative 'models/locality'
30
+ require_relative 'models/preparation'
31
+ require_relative 'models/preparation_type'
32
+ require_relative 'models/record_set'
33
+ require_relative 'models/record_set_item'
34
+ require_relative 'models/taxonomy'
35
+ require_relative 'models/user'
36
+ require_relative 'models/view_set_object'
37
+
38
+ module Specify
39
+ # Model contains Sequel::Model classes for the _Specify_ schema.
40
+ module Model
41
+ AMBIGUOUS_MATCH_ERROR = 'Ambiguous results during search'
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Specify
4
+ module Model
5
+ # Accessions are _interactions_ that represent the formal transferral of
6
+ # ownership of one or more #collection_objects (instances of
7
+ # Specify::Model::CollectionObjects).
8
+ #
9
+ # An Accession belongs to a #division (an instance of
10
+ # Specify::Model::Division).
11
+ class Accession < Sequel::Model(:accession)
12
+ include Createable
13
+ include Updateable
14
+
15
+ many_to_one :division,
16
+ key: :DivisionID
17
+ many_to_one :created_by,
18
+ class: 'Specify::Model::Agent',
19
+ key: :CreatedByAgentID
20
+ many_to_one :modified_by,
21
+ class: 'Specify::Model::Agent',
22
+ key: :ModifiedByAgentID
23
+ one_to_many :collection_objects,
24
+ key: :AccessionID
25
+
26
+ # Returns a String with the accession number (a number under which all
27
+ # information relating to an accession is filed).
28
+ def number
29
+ self[:AccessionNumber]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Specify
4
+ module Model
5
+ # Agents represent entities (people or organizations).
6
+ #
7
+ # An Agent represents a Specify::Model::User in a Specify::Model::Division;
8
+ # that agent will be associated with every record creation and modification
9
+ # carried out by the user in the database.
10
+ class Agent < Sequel::Model(:agent)
11
+ include Updateable
12
+
13
+ many_to_one :user,
14
+ key: :SpecifyUserID
15
+ many_to_one :division,
16
+ key: :DivisionID
17
+
18
+ one_to_many :created_accessions,
19
+ class: 'Specify::Model::Accession',
20
+ key: :CreatedByAgentID
21
+ one_to_many :modified_accessions,
22
+ class: 'Specify::Model::Accession',
23
+ key: :ModifiedByAgentID
24
+
25
+ one_to_many :created_app_resource_data,
26
+ class: 'Specify::Model::AppResourceData',
27
+ key: :CreatedByAgentID
28
+ one_to_many :modified_app_resource_data,
29
+ class: 'Specify::Model::AppResourceData',
30
+ key: :ModifiedByAgentID
31
+
32
+ one_to_many :created_app_resource_dirs,
33
+ class: 'Specify::Model::AppResourceDir',
34
+ key: :CreatedByAgentID
35
+ one_to_many :modified_app_resource_dirs,
36
+ class: 'Specify::Model::AppResourceDir',
37
+ key: :ModifiedByAgentID
38
+
39
+ one_to_many :created_autonumbering_schemes,
40
+ class: 'Specify::Model::AutonumberinScheme',
41
+ key: :CreatedByAgentID
42
+ one_to_many :modified_autonumbering_schemes,
43
+ class: 'Specify::Model::AutonumberinScheme',
44
+ key: :ModifiedByAgentID
45
+
46
+ one_to_many :created_collecting_events,
47
+ class: 'Specify::Model::CollectingEvent',
48
+ key: :CreatedByAgentID
49
+ one_to_many :modified_collecting_events,
50
+ class: 'Specify::Model::CollectingEvent',
51
+ key: :ModifiedByAgentID
52
+
53
+ one_to_many :created_colection_objects,
54
+ class: 'Specify::Model::CollectionObject',
55
+ key: :CreatedByAgentID
56
+ one_to_many :modified_colection_objects,
57
+ class: 'Specify::Model::CollectionObject',
58
+ key: :ModifiedByAgentID
59
+
60
+ one_to_many :created_determinations,
61
+ class: 'Specify::Model::Determination',
62
+ key: :CreatedByAgentID
63
+ one_to_many :modified_determinations,
64
+ class: 'Specify::Model::Determination',
65
+ key: :ModifiedByAgentID
66
+
67
+ one_to_many :created_localities,
68
+ class: 'Specify::Model::Locality',
69
+ key: :CreatedByAgentID
70
+ one_to_many :modified_localities,
71
+ class: 'Specify::Model::Locality',
72
+ key: :ModifiedByAgentID
73
+
74
+ one_to_many :created_preparations,
75
+ class: 'Specify::Model::Preparation',
76
+ key: :CreatedByAgentID
77
+ one_to_many :modified_preparations,
78
+ class: 'Specify::Model::Preparation',
79
+ key: :ModifiedByAgentID
80
+
81
+ one_to_many :created_preparation_types,
82
+ class: 'Specify::Model::PreparationType',
83
+ key: :CreatedByAgentID
84
+ one_to_many :modified_preparation_types,
85
+ class: 'Specify::Model::PreparationType',
86
+ key: :ModifiedByAgentID
87
+
88
+ one_to_many :created_record_sets,
89
+ class: 'Specify::Model::RecordSet',
90
+ key: :CreatedByAgentID
91
+ one_to_many :modified_record_sets,
92
+ class: 'Specify::Model::RecordSet',
93
+ key: :ModifiedByAgentID
94
+
95
+ one_to_many :created_record_set_items,
96
+ class: 'Specify::Model::RecordSetItem',
97
+ key: :CreatedByAgentID
98
+ one_to_many :modified_record_set_items,
99
+ class: 'Specify::Model::RecordSetItem',
100
+ key: :ModifiedByAgentID
101
+
102
+ one_to_many :created_view_set_objects,
103
+ class: 'Specify::Model::ViewSetObject',
104
+ key: :CreatedByAgentID
105
+ one_to_many :modified_view_set_objects,
106
+ class: 'Specify::Model::ViewSetObject',
107
+ key: :ModifiedByAgentID
108
+
109
+ # Returns a String that is the first name of a person.
110
+ def first_name
111
+ self[:FirstName]
112
+ end
113
+
114
+ # Returns a String of attributes concatanated according to the
115
+ # +formatter+.
116
+ def full_name(formatter = nil)
117
+ formatter ||= "#{last_name}, #{first_name} #{middle_name}"
118
+ formatter.strip
119
+ end
120
+
121
+ # Returns a String that is the last name of a person or the name of an
122
+ # organization.
123
+ def last_name
124
+ self[:LastName]
125
+ end
126
+
127
+ # Returns a String that is the middle name of a person.
128
+ def middle_name
129
+ self[:MiddleInitial]
130
+ end
131
+
132
+ # Creates a string representation of +self+.
133
+ def to_s
134
+ full_name
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Specify
4
+ module Model
5
+ # AppResourceData hold data the _Specify_ application uses (usually in the
6
+ # form of _XML_ files that are stored as blobs).
7
+ #
8
+ # An AppResourceData belongs to a #viewsetobj (an instance of
9
+ # Specify::Model::ViewSetObject) or AppResource (not implemented).
10
+ class AppResourceData < Sequel::Model(:spappresourcedata)
11
+ include Createable
12
+ include Updateable
13
+
14
+ many_to_one :viewsetobj,
15
+ class: 'Specify::Model::ViewSetObject',
16
+ key: :SpViewSetObjID
17
+ many_to_one :created_by,
18
+ class: 'Specify::Model::Agent',
19
+ key: :CreatedByAgentID
20
+ many_to_one :modified_by,
21
+ class: 'Specify::Model::Agent',
22
+ key: :ModifiedByAgentID
23
+
24
+ # Stores the contents of +data_file+ (typically an _.xml_ file) as a
25
+ # blob in the database.
26
+ def import(data_file)
27
+ self.data = Sequel.blob(File.read(data_file))
28
+ save
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Specify
4
+ module Model
5
+ # AppResourceDirs associate instances of Specify::Model::ViewSetObject
6
+ # or AppResource (not implemented) with other Specify::Model classes,
7
+ # such as Specify::Model::Collection, Specify::Model::Discipline, or
8
+ # Specify::Model::User, as well as the abstract/custom Specify::UserType,
9
+ # and are used to locate a relevant ViewSetObject or AppResource for a
10
+ # given instance of one of these classes.
11
+ #
12
+ # Resources (ViewSetObjects or AppResurces) are usually _XML_ files used
13
+ # by the _Specify_ application. The files are stored in the AppResourceDirs
14
+ # associated #view_set_object's or AppResource (not implemented)
15
+ # Specify::Model::AppResourceData.
16
+ class AppResourceDir < Sequel::Model(:spappresourcedir)
17
+ include Createable
18
+ include Updateable
19
+
20
+ many_to_one :discipline,
21
+ key: :DisciplineID
22
+ many_to_one :collection,
23
+ key: :CollectionID
24
+ many_to_one :user,
25
+ key: :SpecifyUserID
26
+ one_to_one :view_set_object,
27
+ class: 'Specify::Model::ViewSetObject',
28
+ key: :SpAppResourceDirID
29
+ many_to_one :created_by,
30
+ class: 'Specify::Model::Agent',
31
+ key: :CreatedByAgentID
32
+ many_to_one :modified_by,
33
+ class: 'Specify::Model::Agent',
34
+ key: :ModifiedByAgentID
35
+
36
+ # Returns a String that is the discipline type (same as
37
+ # Specify::Model::Discipline#name) +self+ belongs to.
38
+ def discipline_type
39
+ self[:DisciplineType]
40
+ end
41
+ end
42
+ end
43
+ end