xnlogic 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +43 -0
- data/bin/xnlogic +13 -0
- data/lib/xnlogic/cli/application.rb +60 -0
- data/lib/xnlogic/cli.rb +73 -0
- data/lib/xnlogic/friendly_errors.rb +50 -0
- data/lib/xnlogic/man/xnlogic +46 -0
- data/lib/xnlogic/man/xnlogic.txt +42 -0
- data/lib/xnlogic/templates/application/.rspec.tt +2 -0
- data/lib/xnlogic/templates/application/Gemfile.tt +6 -0
- data/lib/xnlogic/templates/application/Readme.md.tt +86 -0
- data/lib/xnlogic/templates/application/gitignore.tt +9 -0
- data/lib/xnlogic/templates/application/lib/fixtures/sample_fixtures.rb.tt +53 -0
- data/lib/xnlogic/templates/application/lib/gemname/fixtures.rb.tt +108 -0
- data/lib/xnlogic/templates/application/lib/gemname/initializers/inflections.rb.tt +13 -0
- data/lib/xnlogic/templates/application/lib/gemname/models.rb.tt +37 -0
- data/lib/xnlogic/templates/application/lib/gemname/parts/has_notes.rb.tt +10 -0
- data/lib/xnlogic/templates/application/lib/gemname/parts/note.rb.tt +41 -0
- data/lib/xnlogic/templates/application/lib/gemname/permissions.rb.tt +136 -0
- data/lib/xnlogic/templates/application/lib/gemname/type/url.rb.tt +13 -0
- data/lib/xnlogic/templates/application/lib/gemname/type.rb.tt +10 -0
- data/lib/xnlogic/templates/application/lib/gemname/version.rb.tt +3 -0
- data/lib/xnlogic/templates/application/lib/gemname.rb.tt +116 -0
- data/lib/xnlogic/templates/application/spec/gemname/gemname_spec.rb.tt +7 -0
- data/lib/xnlogic/templates/application/spec/spec_helper.rb.tt +44 -0
- data/lib/xnlogic/ui/shell.rb +96 -0
- data/lib/xnlogic/ui/silent.rb +45 -0
- data/lib/xnlogic/ui.rb +7 -0
- data/lib/xnlogic/version.rb +3 -0
- data/lib/xnlogic.rb +33 -0
- data/man/xnlogic.ronn +38 -0
- data/xnlogic.gemspec +27 -0
- metadata +149 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
# Some of our part/model names aren't inflected correctly by default.
|
2
|
+
# Here we exclude them:
|
3
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
4
|
+
inflect.uncountable "media"
|
5
|
+
inflect.uncountable "software"
|
6
|
+
|
7
|
+
inflect.irregular "vrf", "vrfs"
|
8
|
+
|
9
|
+
inflect.acronym 'DNS'
|
10
|
+
inflect.acronym 'FEX'
|
11
|
+
inflect.acronym 'GW'
|
12
|
+
inflect.acronym 'GWs'
|
13
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module <%= config[:constant_name] %>
|
2
|
+
module M
|
3
|
+
end
|
4
|
+
|
5
|
+
# NOTE: The ORDER that the class parts are specified in within each model
|
6
|
+
# IS IMPORTANT here. If two parts define the same method, the one that is
|
7
|
+
# included later will win. It can also call `super` because a real class
|
8
|
+
# heirarchy is created.
|
9
|
+
#
|
10
|
+
# Tools rely on models' parts being defined in order of significance.
|
11
|
+
#
|
12
|
+
# NOTE NOTE
|
13
|
+
# NOTE The LAST PART in the list SHOULD BEST DESCRIBE THE MODEL NOTE
|
14
|
+
# NOTE (if possible) NOTE
|
15
|
+
#
|
16
|
+
client_models = {
|
17
|
+
user_account: [PacerModel::Extensions::Permission, PacerModel::Extensions::User],
|
18
|
+
group: [PacerModel::Extensions::Permission, PacerModel::Extensions::Group],
|
19
|
+
token: [PacerModel::Extensions::Token],
|
20
|
+
external_record: [PacerModel::Extensions::ExternalRecord],
|
21
|
+
data_source: [PacerModel::Extensions::DataSource],
|
22
|
+
import_record: [PacerModel::Extensions::ImportRecord],
|
23
|
+
import: [PacerModel::Extensions::Import],
|
24
|
+
|
25
|
+
# Metadata models
|
26
|
+
note: [Note],
|
27
|
+
}
|
28
|
+
|
29
|
+
client_models.each do |name, parts|
|
30
|
+
<%= config[:constant_name] %>.models[name.to_s] = PacerModel::Model.new(:gemname,
|
31
|
+
name: name,
|
32
|
+
namespace: <%= config[:constant_name] %>::M,
|
33
|
+
core_parts: [PacerModel::Extensions::Record],
|
34
|
+
parts: ([HasNotes] + parts)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module <%= config[:constant_name] %>
|
2
|
+
module Note
|
3
|
+
xn_part
|
4
|
+
|
5
|
+
not_cloneable!
|
6
|
+
|
7
|
+
from_one PacerModel::Extensions::User, from: :author, to: :authored_notes, default: ->{ Thread.current[:context].current_user }
|
8
|
+
from_one :HasNotes
|
9
|
+
|
10
|
+
property :text, type: :text, filter: true
|
11
|
+
|
12
|
+
display :author_info do
|
13
|
+
if author
|
14
|
+
{
|
15
|
+
id: author.element_id,
|
16
|
+
name: author.name,
|
17
|
+
email: author.email,
|
18
|
+
email_md5: author.email_md5
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
action :delete_note, guard: :guard_delete_note do |ctx|
|
24
|
+
delete!
|
25
|
+
end
|
26
|
+
|
27
|
+
module Vertex
|
28
|
+
def guard_delete_note(errors)
|
29
|
+
if created_at < 15.minutes.ago
|
30
|
+
errors.add(:created_at, "Notes can only be deleted for 15 minutes")
|
31
|
+
end
|
32
|
+
|
33
|
+
if author != Thread.current[:context].current_user
|
34
|
+
errors.add(:author, "You can only delete notes you created")
|
35
|
+
end
|
36
|
+
|
37
|
+
# TODO Discuss the pros cons to having additional rules here, like only deleting the last note
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# This module maps out the groups to Part permissions.
|
2
|
+
#
|
3
|
+
# We define CRUD permissions as well as an 'action_access' permission and the scheme
|
4
|
+
# is DENY all unless explicitly allowed by this matrix.
|
5
|
+
#
|
6
|
+
# NB: 'action_access' refers to both the 'action' and 'job' methods in PacerModel
|
7
|
+
# 'read_access' includes the route_traversal, document and display methds
|
8
|
+
#
|
9
|
+
# Also, these permissions are merely the default. Clients may choose to modify them
|
10
|
+
# in any way they wish.
|
11
|
+
#
|
12
|
+
# Some standard users and groups are also created in PacerModel::Application::Security.
|
13
|
+
module <%= config[:constant_name] %>
|
14
|
+
module CreatePermissions
|
15
|
+
# Implementation details. See module/classes defined below.
|
16
|
+
def initialize(app)
|
17
|
+
@app = app
|
18
|
+
end
|
19
|
+
def load!
|
20
|
+
@app.graph.transaction(nesting: true) do
|
21
|
+
groups.inject({}) do |result, (key, name)|
|
22
|
+
result[key] = group key
|
23
|
+
result
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
# This method completely resets existing groups' permissions.
|
28
|
+
def group(key)
|
29
|
+
name = groups[key]
|
30
|
+
group = @app.find_or_create @app.group_model, name: name, key: key.to_s
|
31
|
+
group.revoke_access!
|
32
|
+
group.inherit_full_access [ @app.core_permissions_group ]
|
33
|
+
access_types.each do |access|
|
34
|
+
metaparts = send(key, access).map do |part|
|
35
|
+
mp = @app.find_metapart part
|
36
|
+
puts "WARNING: No metapart for #{ part.inspect }" unless mp
|
37
|
+
mp
|
38
|
+
end
|
39
|
+
group.send("add_#{ access }", metaparts.compact)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
def access_types
|
43
|
+
[:create_access, :read_access, :update_access, :action_access, :delete_access]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# This module is used to create Groups of parts that can easily be assigned to
|
49
|
+
# user groups in the permission generation strategy classes, below:
|
50
|
+
module PartGroups
|
51
|
+
include <%= config[:constant_name] %>
|
52
|
+
|
53
|
+
# Use this method to determine if any of the app's parts are not part of
|
54
|
+
# any of the group definitions below.
|
55
|
+
def missing
|
56
|
+
@app.all_parts - internal_only - users - external - client_models
|
57
|
+
end
|
58
|
+
|
59
|
+
# Use this technique to group together subgroups of parts, which is very useful in larger, more complex models!
|
60
|
+
def client_models
|
61
|
+
base + note + comments
|
62
|
+
end
|
63
|
+
|
64
|
+
# NOTE: only the xnlogic administrator should have access to these
|
65
|
+
def internal_only
|
66
|
+
Set[ PacerModel::Extensions::Part, PacerModel::Extensions::Token,
|
67
|
+
PacerModel::Extensions::XnInfraAdmin ]
|
68
|
+
end
|
69
|
+
|
70
|
+
# Other parts that are built in to XN
|
71
|
+
|
72
|
+
def users
|
73
|
+
Set[ PacerModel::Extensions::Permission, PacerModel::Extensions::User, PacerModel::Extensions::Group ]
|
74
|
+
end
|
75
|
+
|
76
|
+
def external
|
77
|
+
Set[ PacerModel::Extensions::DataSource, PacerModel::Extensions::ExternalRecord ]
|
78
|
+
end
|
79
|
+
|
80
|
+
def base
|
81
|
+
Set[ PacerModel::Extensions::Record, HasNotes, PacerModel::Extensions::User, ]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Your Application-specific parts should go in a bunch of groups starting here:
|
85
|
+
|
86
|
+
def note
|
87
|
+
Set[ Note, ]
|
88
|
+
end
|
89
|
+
|
90
|
+
def comments
|
91
|
+
# Placeholder for theoretical comments part...
|
92
|
+
Set[ ]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# A sophisticated role based access model
|
98
|
+
class RoleBasedPermissions
|
99
|
+
include CreatePermissions
|
100
|
+
include PartGroups
|
101
|
+
|
102
|
+
def initialize(app)
|
103
|
+
super
|
104
|
+
end
|
105
|
+
|
106
|
+
def groups
|
107
|
+
{
|
108
|
+
support: 'Support',
|
109
|
+
staff: 'Staff',
|
110
|
+
read_only: 'Read Only'
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def support(access)
|
115
|
+
base + users + client_models
|
116
|
+
end
|
117
|
+
|
118
|
+
def read_only(access)
|
119
|
+
case access
|
120
|
+
when :read_access
|
121
|
+
base + client_models
|
122
|
+
else
|
123
|
+
[]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def staff(access)
|
128
|
+
case access
|
129
|
+
when :delete_access
|
130
|
+
base + comments
|
131
|
+
when :read_access, :update_access, :create_access, :action_access
|
132
|
+
base + client_models
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module <%= config[:constant_name] %>
|
2
|
+
module Type
|
3
|
+
|
4
|
+
# URL can be a property's type
|
5
|
+
Property::URL = {
|
6
|
+
type: :text,
|
7
|
+
label: 'URL',
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
# URL can be an argument in an action as well. Definition is identical to Property::URL:
|
11
|
+
Argument::URL = Property::URL
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
if RUBY_VERSION == '1.8.7'
|
2
|
+
STDERR.puts <<WARNING
|
3
|
+
WARNING: XN Logic is developed using JRuby in 1.9 mode. I recommend you
|
4
|
+
restart JRuby in 1.9 mode, either with the --1.9 flag, or by
|
5
|
+
defaulting to 1.9 mode by setting the environment variable
|
6
|
+
JRUBY_OPTS=--1.9
|
7
|
+
WARNING
|
8
|
+
raise "1.8 mode: no go"
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'rubygems'
|
12
|
+
require 'bundler/setup'
|
13
|
+
require 'active_model'
|
14
|
+
require 'active_model/naming'
|
15
|
+
require 'active_support/core_ext'
|
16
|
+
require 'pacer'
|
17
|
+
require 'pacer-model'
|
18
|
+
require 'pathname'
|
19
|
+
|
20
|
+
require '<%= config[:namespaced_path] %>/initializers/inflections'
|
21
|
+
|
22
|
+
module <%= config[:constant_name] %>
|
23
|
+
VertexDsl = PacerModel::VertexDsl
|
24
|
+
START_TIME = Time.now unless const_defined? :START_TIME
|
25
|
+
|
26
|
+
class << self
|
27
|
+
include PacerModel::AppBase
|
28
|
+
|
29
|
+
def lib_dir
|
30
|
+
Pathname.new(File.expand_path(__FILE__)).parent
|
31
|
+
end
|
32
|
+
|
33
|
+
def sample_app(args)
|
34
|
+
unless args.is_a? Hash
|
35
|
+
puts "sample_app takes an args hash with :graph and :client_name required."
|
36
|
+
return
|
37
|
+
end
|
38
|
+
unless args.keys.include? :client_name and args.keys.include? :graph
|
39
|
+
puts ":graph and :client_name are required arguments"
|
40
|
+
return
|
41
|
+
end
|
42
|
+
require '<%= config[:namespaced_path] %>/fixtures'
|
43
|
+
Dir[<%= config[:constant_name] %>.lib_dir + "fixtures/*.{rb,class}"].each do |file|
|
44
|
+
require Pathname.new(file).relative_path_from(<%= config[:constant_name] %>.lib_dir)
|
45
|
+
end
|
46
|
+
a = app(args)
|
47
|
+
Fixtures.load_fixtures(a)
|
48
|
+
a
|
49
|
+
end
|
50
|
+
|
51
|
+
# Usage: permission_models[x].new(app).load!
|
52
|
+
# Note, it's load! method must be idempotent.
|
53
|
+
def permission_models
|
54
|
+
{
|
55
|
+
'role_based' => <%= config[:constant_name] %>::RoleBasedPermissions
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the time pacer was last reloaded (or when it was started).
|
60
|
+
def reload_time
|
61
|
+
if defined? @reload_time
|
62
|
+
@reload_time
|
63
|
+
else
|
64
|
+
START_TIME
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Reload all Ruby modified files in the Pacer library. Useful for debugging
|
69
|
+
# in the console. Does not do any of the fancy stuff that Rails reloading
|
70
|
+
# does. Certain types of changes will still require restarting the
|
71
|
+
# session.
|
72
|
+
def reload!
|
73
|
+
lib_dir.find do |path|
|
74
|
+
if path.extname == '.rb' and path.mtime > reload_time
|
75
|
+
puts path.to_s
|
76
|
+
load path.to_s rescue nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@reload_time = Time.now
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
require '<%= config[:namespaced_path] %>/version'
|
85
|
+
require '<%= config[:namespaced_path] %>/type'
|
86
|
+
|
87
|
+
PacerModel.applications['<%= config[:name] %>'] = <%= config[:constant_name] %>
|
88
|
+
PacerModel.property_namespaces << <%= config[:constant_name] %>::Type::Property
|
89
|
+
PacerModel.argument_namespaces << <%= config[:constant_name] %>::Type::Argument
|
90
|
+
PacerModel.part_namespaces << <%= config[:constant_name] %>
|
91
|
+
PacerModel.on_part_registered << lambda do |part|
|
92
|
+
if part.name =~ /^<%= config[:constant_name] %>::/
|
93
|
+
<%= config[:constant_name] %>.parts[part.key.to_s] = part
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
#NOTE: meta contains some common parts. So, load meta first to ensure
|
98
|
+
# common parts are loaded into other parts on boot
|
99
|
+
Dir[<%= config[:constant_name] %>.lib_dir + '<%= config[:namespaced_path] %>/meta/*.{class,rb}'].each do |file|
|
100
|
+
require Pathname.new(file).relative_path_from(<%= config[:constant_name] %>.lib_dir)
|
101
|
+
end
|
102
|
+
<%= config[:constant_name] %>.common_parts!
|
103
|
+
# dirs under lib/<%= config[:namespaced_path] %> that contain groupings of parts
|
104
|
+
sections = %w[
|
105
|
+
parts
|
106
|
+
]
|
107
|
+
|
108
|
+
# Now that we have DEFAULT_CONSTANTS being referenced, if load order is important...
|
109
|
+
#require <%= config[:constant_name] %>.lib_dir + "<%= config[:namespaced_path] %>/group/part"
|
110
|
+
|
111
|
+
Dir[<%= config[:constant_name] %>.lib_dir + "<%= config[:namespaced_path] %>/{#{sections.join ','}}/*.{rb,class}"].each do |file|
|
112
|
+
require Pathname.new(file).relative_path_from(<%= config[:constant_name] %>.lib_dir)
|
113
|
+
end
|
114
|
+
|
115
|
+
require '<%= config[:namespaced_path] %>/models'
|
116
|
+
require '<%= config[:namespaced_path] %>/permissions'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup :default, :development
|
4
|
+
|
5
|
+
require '<%= config[:namespaced_path] %>'
|
6
|
+
|
7
|
+
Bundler.require :development
|
8
|
+
require 'pp'
|
9
|
+
|
10
|
+
Dir['./spec/support/**/*.rb'].map {|f| require f}
|
11
|
+
|
12
|
+
class RSpec::Core::ExampleGroup
|
13
|
+
def self.run_all(reporter=nil)
|
14
|
+
run(reporter || NullObject.new)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def in_editor?
|
19
|
+
ENV.has_key?('TM_MODE') || ENV.has_key?('EMACS') || ENV.has_key?('VIM')
|
20
|
+
end
|
21
|
+
|
22
|
+
RSpec.configure do |c|
|
23
|
+
c.color_enabled = !in_editor?
|
24
|
+
c.filter_run :focus => true
|
25
|
+
c.run_all_when_everything_filtered = true
|
26
|
+
Pacer.hide_route_elements = true
|
27
|
+
Pacer.verbose = false
|
28
|
+
JRuby.objectspace = true
|
29
|
+
c.mock_with :rr
|
30
|
+
|
31
|
+
SpecApp = <%= config[:constant_name] %>.app graph: Pacer.tg, client_name: 'specs'
|
32
|
+
SpecApp.add_to_models!
|
33
|
+
SpecApp.add_to_parts!
|
34
|
+
|
35
|
+
puts "Using JRuby #{ JRUBY_VERSION } in #{ RUBY_VERSION } mode."
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def use_simple_fixtures
|
40
|
+
before { <%= config[:constant_name] %>.graph = Pacer.tg }
|
41
|
+
let(:graph) { <%= config[:constant_name] %>.graph }
|
42
|
+
let(:manufacturer) { graph.create_vertex <%= config[:constant_name] %>::Manufacturer }
|
43
|
+
let(:device_model){ graph.create_vertex <%= config[:constant_name] %>::DeviceModel }
|
44
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Xnlogic
|
2
|
+
module UI
|
3
|
+
class Shell
|
4
|
+
LEVELS = %w(silent error warn confirm info debug)
|
5
|
+
|
6
|
+
attr_writer :shell
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
if options["no-color"] || !STDOUT.tty?
|
10
|
+
Thor::Base.shell = Thor::Shell::Basic
|
11
|
+
end
|
12
|
+
@shell = Thor::Base.shell.new
|
13
|
+
@level = ENV['DEBUG'] ? "debug" : "info"
|
14
|
+
end
|
15
|
+
|
16
|
+
def info(msg, newline = nil)
|
17
|
+
tell_me(msg, nil, newline) if level("info")
|
18
|
+
end
|
19
|
+
|
20
|
+
def confirm(msg, newline = nil)
|
21
|
+
tell_me(msg, :green, newline) if level("confirm")
|
22
|
+
end
|
23
|
+
|
24
|
+
def warn(msg, newline = nil)
|
25
|
+
tell_me(msg, :yellow, newline) if level("warn")
|
26
|
+
end
|
27
|
+
|
28
|
+
def error(msg, newline = nil)
|
29
|
+
tell_me(msg, :red, newline) if level("error")
|
30
|
+
end
|
31
|
+
|
32
|
+
def debug(msg, newline = nil)
|
33
|
+
tell_me(msg, nil, newline) if level("debug")
|
34
|
+
end
|
35
|
+
|
36
|
+
def debug?
|
37
|
+
# needs to be false instead of nil to be newline param to other methods
|
38
|
+
level("debug")
|
39
|
+
end
|
40
|
+
|
41
|
+
def quiet?
|
42
|
+
LEVELS.index(@level) <= LEVELS.index("warn")
|
43
|
+
end
|
44
|
+
|
45
|
+
def ask(msg)
|
46
|
+
@shell.ask(msg)
|
47
|
+
end
|
48
|
+
|
49
|
+
def level=(level)
|
50
|
+
raise ArgumentError unless LEVELS.include?(level.to_s)
|
51
|
+
@level = level
|
52
|
+
end
|
53
|
+
|
54
|
+
def level(name = nil)
|
55
|
+
name ? LEVELS.index(name) <= LEVELS.index(@level) : @level
|
56
|
+
end
|
57
|
+
|
58
|
+
def trace(e, newline = nil)
|
59
|
+
return unless debug?
|
60
|
+
msg = "#{e.class}: #{e.message}\n#{e.backtrace.join("\n ")}"
|
61
|
+
tell_me(msg, nil, newline)
|
62
|
+
end
|
63
|
+
|
64
|
+
def silence
|
65
|
+
old_level, @level = @level, "silent"
|
66
|
+
yield
|
67
|
+
ensure
|
68
|
+
@level = old_level
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# valimism
|
74
|
+
def tell_me(msg, color = nil, newline = nil)
|
75
|
+
msg = word_wrap(msg) if newline.is_a?(Hash) && newline[:wrap]
|
76
|
+
if newline.nil?
|
77
|
+
@shell.say(msg, color)
|
78
|
+
else
|
79
|
+
@shell.say(msg, color, newline)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def strip_leading_spaces(text)
|
84
|
+
spaces = text[/\A\s+/, 0]
|
85
|
+
spaces ? text.gsub(/#{spaces}/, '') : text
|
86
|
+
end
|
87
|
+
|
88
|
+
def word_wrap(text, line_width = @shell.terminal_width)
|
89
|
+
strip_leading_spaces(text).split("\n").collect do |line|
|
90
|
+
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
|
91
|
+
end * "\n"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Xnlogic
|
2
|
+
module UI
|
3
|
+
class Silent
|
4
|
+
def info(message, newline = nil)
|
5
|
+
end
|
6
|
+
|
7
|
+
def confirm(message, newline = nil)
|
8
|
+
end
|
9
|
+
|
10
|
+
def warn(message, newline = nil)
|
11
|
+
end
|
12
|
+
|
13
|
+
def error(message, newline = nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
def debug(message, newline = nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
def debug?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def quiet?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
27
|
+
def ask(message)
|
28
|
+
end
|
29
|
+
|
30
|
+
def level=(name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def level(name = nil)
|
34
|
+
end
|
35
|
+
|
36
|
+
def trace(message, newline = nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
def silence
|
40
|
+
yield
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
data/lib/xnlogic/ui.rb
ADDED
data/lib/xnlogic.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'xnlogic/version'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'pathname'
|
4
|
+
require 'xnlogic/ui'
|
5
|
+
|
6
|
+
module Xnlogic
|
7
|
+
class XnlogicError < StandardError
|
8
|
+
def self.status_code(code)
|
9
|
+
define_method(:status_code) { code }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class InvalidOption < XnlogicError; status_code(15) ; end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_writer :ui
|
17
|
+
|
18
|
+
def ui
|
19
|
+
@ui ||= UI::Silent.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def which(executable)
|
23
|
+
if File.file?(executable) && File.executable?(executable)
|
24
|
+
executable
|
25
|
+
elsif ENV['PATH']
|
26
|
+
path = ENV['PATH'].split(File::PATH_SEPARATOR).find do |p|
|
27
|
+
File.executable?(File.join(p, executable))
|
28
|
+
end
|
29
|
+
path && File.expand_path(executable, path)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/man/xnlogic.ronn
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
xnlogic(1) -- XN Logic Command-line Tools
|
2
|
+
=========================================
|
3
|
+
|
4
|
+
## SYNOPSIS
|
5
|
+
|
6
|
+
`xnlogic` COMMAND [--no-color] [--verbose] [ARGS]
|
7
|
+
|
8
|
+
## DESCRIPTION
|
9
|
+
|
10
|
+
XN Logic is a graph database-backed application framework.
|
11
|
+
|
12
|
+
See [the XN Logic website](https://xnlogic.com) for information on getting
|
13
|
+
started, or https://xnlogic.zendesk.com/hc/en-us for support.
|
14
|
+
|
15
|
+
## OPTIONS
|
16
|
+
|
17
|
+
* `--no-color`:
|
18
|
+
Prints all output without color
|
19
|
+
|
20
|
+
* `--verbose`:
|
21
|
+
Prints out additional logging information
|
22
|
+
|
23
|
+
## COMMANDS
|
24
|
+
|
25
|
+
We divide `xnlogic` subcommands into primary commands and utilities.
|
26
|
+
|
27
|
+
## PRIMARY COMMANDS
|
28
|
+
|
29
|
+
* ...
|
30
|
+
|
31
|
+
* `xnlogic help(1)`:
|
32
|
+
Displays detailed help for each subcommand
|
33
|
+
|
34
|
+
## UTILITIES
|
35
|
+
|
36
|
+
* `xnlogic application(1)`:
|
37
|
+
Create a simple application, suitable for development with xnlogic
|
38
|
+
|