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