specify_cli 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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