lti2 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +184 -0
- data/Rakefile +23 -0
- data/app/assets/javascripts/lti2/application.js +13 -0
- data/app/assets/stylesheets/lti2/application.css +15 -0
- data/app/controllers/lti2/application_controller.rb +4 -0
- data/app/helpers/lti2/application_helper.rb +4 -0
- data/app/views/layouts/lti2/application.html.erb +14 -0
- data/config/routes.rb +4 -0
- data/lib/lti2.rb +7 -0
- data/lib/lti2/engine.rb +9 -0
- data/lib/lti2/version.rb +3 -0
- data/lib/lti2_commons/lib/lti2_commons.rb +9 -0
- data/lib/lti2_commons/lib/lti2_commons/cache.rb +29 -0
- data/lib/lti2_commons/lib/lti2_commons/json_wrapper.rb +118 -0
- data/lib/lti2_commons/lib/lti2_commons/lti2_launch.rb +158 -0
- data/lib/lti2_commons/lib/lti2_commons/message_support.rb +218 -0
- data/lib/lti2_commons/lib/lti2_commons/oauth_request.rb +179 -0
- data/lib/lti2_commons/lib/lti2_commons/signer.rb +75 -0
- data/lib/lti2_commons/lib/lti2_commons/substitution_support.rb +87 -0
- data/lib/lti2_commons/lib/lti2_commons/utils.rb +28 -0
- data/lib/lti2_commons/lib/lti2_commons/version.rb +3 -0
- data/lib/lti2_commons/lib/lti2_commons/wire_log.rb +163 -0
- data/lib/lti2_commons/test/test_jsonpath.rb +255 -0
- data/lib/lti2_commons/test/test_jsonwrapper.rb +230 -0
- data/lib/lti2_commons/test/test_oauth_request.rb +143 -0
- data/lib/lti2_commons/test/test_substitution_support.rb +71 -0
- data/lib/lti2_commons/test/test_wire_log.rb +15 -0
- data/lib/tasks/lti2_tasks.rake +4 -0
- metadata +199 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c7fb808b35c9fa1aa859277f7fbf6e7789a126cc
|
4
|
+
data.tar.gz: 58137b32270008ffd738b49b609b27997afd5c5b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c8ebbfa0b82662ebd64d16228ffcc11b68d697f595f4758cbf7841073df1971046b9696761c442300da34192420fbf95bd975fdff857d050fa55f1c654783d3c
|
7
|
+
data.tar.gz: d78b7a644c7b29e1a4298e6199b3a8edda475cf5d263e40b4483ab592384056c6acdc97cc90e96561d89b1893e1dce4c42af891ce41a92dbaabb2fda0a1a7fa6
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
|
2
|
+
LTI2 -- Ruby/Rails reference implementation
|
3
|
+
==============
|
4
|
+
__John Tibbetts, Integration Architect, Vital Source Technologies__
|
5
|
+
|
6
|
+
This is an LTI2 reference implementation. A reference implementation is one that is intended to demonstrate working code based on an emerging standard. However implementation provides than just exemplary code. In
|
7
|
+
addition, it fulfills the following responsibilities:
|
8
|
+
|
9
|
+
* It implements both sides of the LTI convesation; that is, Tool Consumer (often an LMS) and Tool Provider. These two components will work (out-of-the-box) with one another. Or the Tool Consumer can be aimed at an external Tool Provider (possibly in development) to test it. Or the Tool Provider can be addressed by an external Tool Consumer (possibly in development) to test it.
|
10
|
+
|
11
|
+
* As new facilities, messages, or services are added to LTI2 they will be added to this reference model. The intent is to add them in while they are still in development so that the LTI Services Task Force can see them in early operation.
|
12
|
+
|
13
|
+
* The actual LTI-specific TC and TP functionality are implemented as Rails moountable engines. A mountable engine is a type of gem that is used for creating a Rails 'sub-application'; that is, an application within an application. Each mountable engine has its own models, controller, etc. Common behavior of both engines is abstracted into a third gem: lti2-commons. A consequence of using this design modularity is that either one or both engines can also be mounted in real working production code. In particular, the Vital Source BusinessCenter incorporates the Tool Consumer engine to provide launches appropriate for viewing e-textbooks. It also incorporates the Tool Provider to allow new interactive e-textbooks to launch out of the book into other LTI tools, either provided by Vital Source, the book publisher, or anyone else.
|
14
|
+
|
15
|
+
* The reference implementation contains sample applications that can either be run under sqlite3 or MySQL. Sample data is provided for each type of database.
|
16
|
+
|
17
|
+
For all the details of LTI, see the full [LTI2 online docs](http://www.imsglobal.org/lti/index.html).
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
Prerequisites
|
22
|
+
-------------
|
23
|
+
|
24
|
+
* Ruby/Rails. Follow online docs to install Ruby/Rails for your development platform. This code is currently built on Ruby 1.9.3.
|
25
|
+
|
26
|
+
* Github identity. During the prototype period (pre-conformance test) this must be sent to Lisa Mattson )Lisa Mattson (lisa@imsglobal.org) all access to the repos.
|
27
|
+
|
28
|
+
* With a valid Github identity, clone this repo.
|
29
|
+
|
30
|
+
* Run bundler in subsdirectories tc_sample_app (the TC directory) and tp_sample_app (the TP directory).
|
31
|
+
|
32
|
+
* This code has been tested with either MySQL or sqlite. The default database load instructions will work for either of these databases. They would likely work with virtually any other Rails-compatible database.
|
33
|
+
|
34
|
+
|
35
|
+
Structure of this repository
|
36
|
+
-----------------------------
|
37
|
+
The repository structure is as follows:
|
38
|
+
|
39
|
+
LTI2--
|
40
|
+
|
|
41
|
+
|
|
42
|
+
--closet (database backups, common scripts, metadata artifacts)
|
43
|
+
|
|
44
|
+
|
|
45
|
+
--lti2_tc (tool consumer engine)
|
46
|
+
|
|
47
|
+
|
|
48
|
+
--lti2_tp (tool provider engine)
|
49
|
+
|
|
50
|
+
|
|
51
|
+
--lti2_commons (library used by both TC and TP)
|
52
|
+
|
|
53
|
+
|
|
54
|
+
--tc_sample_app (lightweight TC host based on active_admin gem)
|
55
|
+
|
|
56
|
+
|˙
|
57
|
+
--tp_sample_app (lightweight TP and tool)
|
58
|
+
|
59
|
+
|
60
|
+
Getting it running
|
61
|
+
==================
|
62
|
+
|
63
|
+
1. Clone the repo onto your machine.
|
64
|
+
|
65
|
+
2. Create a command prompt for the tool consumer. chdir into <lti_repo>/tc_sample_app.
|
66
|
+
|
67
|
+
3. [FIRST-TIME ONLY after clone of TC] 'rake init_task:backup'. This will reset data to base state and ensure that sqlite3 is the current database. (Instructions below for changing to MySQL. Recommend running it first as sqlite3).
|
68
|
+
|
69
|
+
4. Start a rails server for the Tool Consumer on port 4000: 'rails s -p 4000'.
|
70
|
+
|
71
|
+
5. Create a command prompt for the tool provider. chdir into <lti_repo>/tp_sample_app.
|
72
|
+
|
73
|
+
6. [FIRST-TIME ONLY after clone of TP] 'rake init_task:backup'. This will reset data to base state and ensure that sqlite3 is the current database. (Instructions below for changing to MySQL. Recommend running it first as sqlite3).
|
74
|
+
|
75
|
+
7. Start a rails server for the Tool Provider on port 5000: 'rails s -p 5000'.
|
76
|
+
|
77
|
+
8. Open a browser and go to: 'http://localhost:4000/admin'. If you're prompted for a login, username is 'admin@lumos.org', password is 'password'.
|
78
|
+
|
79
|
+
9. Note: from here on you can see this tool used at: [LTI2 webcast](https://www.youtube.com/watch?v=3zTbtTldeiA "LTI2 webcast").
|
80
|
+
|
81
|
+
10. Dashboard: 'Admin Functions' -> 'Register New Tool'. Enter: 'http://localhost:5000/lti2_tp/registrations'.
|
82
|
+
|
83
|
+
11. This will invoke an LTI2 Registration request to the Tool Provider. The screen with title Fabericious is the Tool Provider's dialog to gather information. The only action is to add some unique string to the end of the 'Institution name'. For example, put in the time 1113. Doing this ensures it can be run repeatedly, since duplicate subsequent entries would be rejected if the value didn't change. Click the "Update" button.
|
84
|
+
|
85
|
+
12. This should return you to the Tool Actions page of the Tool Consumer. Click on the "[enable now]" link in the column 'Enabled?'. This will activate the newly registered tool.
|
86
|
+
|
87
|
+
13. Return to admin menu. Click 'My Courses'. Pick SMPL101A. Links to a number of resources (within the single tool) are available. Try 'Echo' to see the Tool Provider's log of the LTI parameters. Try other resources as you like.
|
88
|
+
|
89
|
+
14. Return to Admin. 'Admin Functions' --> 'Show Wirelog'. This will display a structured log with messages emanating from the Tool Consumer left-adjusted and messages emanating from the Tool Provider center-adjusted.
|
90
|
+
|
91
|
+
Resetting the data
|
92
|
+
------------------
|
93
|
+
|
94
|
+
After running the demo, the data can be reset to its base state (eliminating any current registrations, links, etc.). To reset the data for the Tool Consumer:
|
95
|
+
|
96
|
+
1. Stop the server in the TC directory.
|
97
|
+
2. rake db:seed
|
98
|
+
3. Restart the server
|
99
|
+
|
100
|
+
The procedure is identical for the Tool Provider, except perform the operations in the TP directory.
|
101
|
+
|
102
|
+
Changing your database configuration
|
103
|
+
=============================
|
104
|
+
|
105
|
+
__NOTE WELL: Default database choice has changed! It is now sqlite3, not MySQL__
|
106
|
+
|
107
|
+
sqlite3 is built in and the data is prebuilt in github. This means that the TC and TP can be checked out and immediately run with no database prep.
|
108
|
+
|
109
|
+
If you'd prefer to use MySQL it's easy to switch.
|
110
|
+
|
111
|
+
* To change the TC to MySQL, edit lti_tc/config/database.yml. To change the TP to MySQL, edit lti_tp/config/database.yml. These are entirely independent. There is no requirement that they use the same database configuration.
|
112
|
+
|
113
|
+
* Follow the TC- or TP-appropriate instructions in the sections below. For a MySQL database you'll need to load the database from the /backups directory.
|
114
|
+
|
115
|
+
* There is no requirement that the TC and the TP need to use the same database configuration. e.g. The TC might be using sqlite3 and the TP mysql.
|
116
|
+
|
117
|
+
It's also possible to switch database configurations with a command line option rather than hard-wiring the choice using database.yml. To accomplish this use the -e option on the relevant rails command.
|
118
|
+
|
119
|
+
For example:
|
120
|
+
|
121
|
+
* Start the TC server with mysql: rails s -p 4000 -e mysql
|
122
|
+
|
123
|
+
* Start the TP server with sqlite: rails s -p 5000 -e sqlite3
|
124
|
+
|
125
|
+
If using this method remember to apply these same environments to rake tasks:
|
126
|
+
|
127
|
+
For example:
|
128
|
+
|
129
|
+
* Reseed the mysql database: RAILS_ENV=mysql rake db:seed
|
130
|
+
|
131
|
+
* Reseed the sqlite3 database: RAILS_ENV=sqlite3 rake db:seed
|
132
|
+
|
133
|
+
|
134
|
+
Setting up MySQL databases
|
135
|
+
-----------------------
|
136
|
+
|
137
|
+
_If you choose a MySQL Database for TC_
|
138
|
+
|
139
|
+
1. Create the 'Lumos' database.
|
140
|
+
2. Allow Lumos access to user: 'ltiuser' with password 'ltipswd'.
|
141
|
+
3. Initialize the database: mysql Lumos -u ltiuser -p < backup/lti2_tc.sql
|
142
|
+
|
143
|
+
_If you choose a MySQL Database for TP__
|
144
|
+
|
145
|
+
1. Create the 'fabericious' database.
|
146
|
+
2. Allow fabericious access to user: 'ltiuser' with password 'ltipswd'.
|
147
|
+
3. mysql fabericious -u ltiuser -p < backup/lti2_tp.sql
|
148
|
+
|
149
|
+
Using the Tool Consumer Engine with another host application
|
150
|
+
=========================================
|
151
|
+
As described above, this distribution uses tc_sample_app as a pseudo LMS. All of the LTI-specific behavior is mounted into this app using the Rails mountable engine capability. To use this same engine in another host application the following steps need to be followed.
|
152
|
+
|
153
|
+
1. The Gemfile of the host application should access the gem from VST github by including the line:
|
154
|
+
* gem 'lti2_tc', :github => 'vitalsource/lti2_tc'
|
155
|
+
|
156
|
+
2. At a rails command-line in the host, import the TC engine migrations into the db migrations of the host:
|
157
|
+
* rake lti2_tc:install:migrations
|
158
|
+
|
159
|
+
3. In the host application's routes.rb, specify the mount point of the TC engine:
|
160
|
+
* mount Lti2Tc::Engine, :at => '/lti2_tc'
|
161
|
+
|
162
|
+
4. Implement the host responsibilities of the engine. The can be seen in exemplary code in the tc_sample_app.
|
163
|
+
* In the database table 'lti2_tc_registries': modify the tc_deployment_url to the base URL of your server.
|
164
|
+
* In the database table 'lti2_tc_registries': look for the field tool_consumer_profile_template. This is
|
165
|
+
the tool_consumer_profile that your TC will serve. Modify it to suit.
|
166
|
+
* Implement a user experience to gather a Tool Provider ToolRegistration. cf. tc_sample_app/app/admin/deployment_requests.rb in member_action :request_product.
|
167
|
+
* Once the tool is registered it needs to be enabled. Set up a UX to display tools and tool status. Cf. tc_sample_app/app/admin/tool_actions.rb. Enabling (or disabling) the tool is performed by changing 'is_enabled'. cf. 'toggle' behavior in 'tool_actions'.
|
168
|
+
* Set up 'resolver' methods (Visitor pattern) in course, user, grade_result. These are invoked by the callbacks in app/services/LtiLaunch.
|
169
|
+
|
170
|
+
Using the Tool Provider Engine with another host application
|
171
|
+
=========================================
|
172
|
+
As described above, this distribution uses tp_sample_app as a pseudo tool provider. All of the LTI-specific behavior is mounted into this app using the Rails mountable engine capability. To use this same engine in another host application the following steps need to be followed.
|
173
|
+
|
174
|
+
1. The Gemfile of the host application should access the gem from VST github by including the line:
|
175
|
+
* gem 'lti2_tp', :github => 'vitalsource/lti2_tp'
|
176
|
+
|
177
|
+
2. At a rails command-line in the host, import the TP engine migrations into the db migrations of the host:
|
178
|
+
* rake lti2_tp:install:migrations
|
179
|
+
|
180
|
+
3. In the host application's routes.rb, specify the mount point of the TP engine:
|
181
|
+
* mount Lti2Tp::Engine, :at => '/lti2_tp'
|
182
|
+
|
183
|
+
4. Implement the host responsibilities of the engine:
|
184
|
+
* [to be added]
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Lti2'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Lti2</title>
|
5
|
+
<%= stylesheet_link_tag "lti2/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "lti2/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
data/config/routes.rb
ADDED
data/lib/lti2.rb
ADDED
data/lib/lti2/engine.rb
ADDED
data/lib/lti2/version.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require_relative 'lti2_commons/cache'
|
2
|
+
require_relative 'lti2_commons/json_wrapper'
|
3
|
+
require_relative 'lti2_commons/message_support'
|
4
|
+
require_relative 'lti2_commons/oauth_request'
|
5
|
+
require_relative 'lti2_commons/signer'
|
6
|
+
require_relative 'lti2_commons/substitution_support'
|
7
|
+
require_relative 'lti2_commons/utils'
|
8
|
+
require_relative 'lti2_commons/version'
|
9
|
+
require_relative 'lti2_commons/wire_log'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'lrucache'
|
2
|
+
|
3
|
+
module Lti2Commons
|
4
|
+
# Cache adapter. This adapter wraps a simple LRUCache gem.
|
5
|
+
# A more scalable and cluster-friendly cache solution such as Redis
|
6
|
+
# or Memcache # would probably be suitable in a production environment.
|
7
|
+
# This class is used to document the interface. In this particular case
|
8
|
+
# the interface exactly matches the protocol of the supplied implementation.
|
9
|
+
# Consequently, this adapter is not really required.
|
10
|
+
class Cache
|
11
|
+
# create cache.
|
12
|
+
# @params options [Hash] Should include ttl: <expiry_time>
|
13
|
+
def initialize(options)
|
14
|
+
@cache = LRUCache.new options
|
15
|
+
end
|
16
|
+
|
17
|
+
def clear
|
18
|
+
@cache.clear
|
19
|
+
end
|
20
|
+
|
21
|
+
def fetch(name)
|
22
|
+
@cache.fetch(name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def store(name, value)
|
26
|
+
@cache.store(name, value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
require 'jsonpath'
|
4
|
+
|
5
|
+
module Lti2Commons
|
6
|
+
class JsonWrapper
|
7
|
+
attr_accessor :root
|
8
|
+
|
9
|
+
def initialize(json_str_or_obj)
|
10
|
+
@root = JsonPath.new('$').on(json_str_or_obj).first
|
11
|
+
end
|
12
|
+
|
13
|
+
def at(path)
|
14
|
+
JsonPath.new(path).on(@root)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Deep copy through reserialization. Only used to preserve immutability of source.
|
18
|
+
#
|
19
|
+
# @return [JsonObject] A full new version of self
|
20
|
+
def deep_copy
|
21
|
+
JsonWrapper.new(@root.to_json)
|
22
|
+
end
|
23
|
+
|
24
|
+
def each_leaf
|
25
|
+
JsonPath.new('$..*').on(@root).each do |node|
|
26
|
+
yield node if node.is_a? String
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def first_at(path)
|
31
|
+
at(path).first
|
32
|
+
end
|
33
|
+
|
34
|
+
# Does this node, possibly repeating, match all elements of the constraint_hash
|
35
|
+
#
|
36
|
+
# @params candidate [Hash/Array] node or Array of nodes to match
|
37
|
+
# @params constraint_hash [Hash] Hash of value to match
|
38
|
+
# @return [TreeNode] Either self or immediate child that matches constraint_hash
|
39
|
+
def get_matching_node(candidate, constraint_hash)
|
40
|
+
if candidate.is_a? Hash
|
41
|
+
return candidate if is_hash_intersect(candidate, constraint_hash)
|
42
|
+
elsif candidate.is_a? Array
|
43
|
+
candidate.each do |child|
|
44
|
+
# are there extraneous keys in constraint_hash
|
45
|
+
return child if is_hash_intersect(child, constraint_hash)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
# Convenience method to find a particular node with matching attributes to constraint_hash
|
52
|
+
# and then return specific element of that node.
|
53
|
+
# e.g., search for 'path' within the 'resource_handler' with constraint_hash attributes.
|
54
|
+
#
|
55
|
+
# @params path [JsonPath] path to parent of candidate array of nodes
|
56
|
+
# @params constraint_hash [Hash] Hash of values which must match target
|
57
|
+
# @params return_path [JsonPath] Path of element within matching target
|
58
|
+
# @return [Object] result_path within matching node or nil
|
59
|
+
def search(path, constraint_hash, return_path)
|
60
|
+
candidate = JsonPath.new(path).on(@root)
|
61
|
+
candidate = get_matching_node(candidate.first, constraint_hash)
|
62
|
+
return nil unless candidate
|
63
|
+
JsonPath.new(return_path).on(candidate).first
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convenience method to find a particular node with matching attributes to constraint_hash
|
67
|
+
# and then return specific element of that node.
|
68
|
+
# e.g., search for 'path' within the 'resource_handler' with constraint_hash attributes.
|
69
|
+
#
|
70
|
+
# @params path [JsonPath] path to parent of candidate array of nodes
|
71
|
+
# @params constraint_hash [Hash] Hash of values which must match target
|
72
|
+
# @params return_path [JsonPath] Path of element within matching target
|
73
|
+
# @return [Object] result_path within matching node or nil
|
74
|
+
def select(path, selector, value, return_path)
|
75
|
+
candidates = JsonPath.new(path).on(@root)
|
76
|
+
now_candidate = nil
|
77
|
+
candidates.each do |candidate|
|
78
|
+
now_candidate = candidate
|
79
|
+
selector_node = JsonPath.new(selector).on(candidate.first)
|
80
|
+
break if selector_node.include? value
|
81
|
+
end
|
82
|
+
if now_candidate
|
83
|
+
JsonPath.new(return_path).on(now_candidate.first).first
|
84
|
+
else
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def substitute_text_in_all_nodes(token_prefix, token_suffix, hash)
|
90
|
+
self.each_leaf do |v|
|
91
|
+
substitute_template_values_from_hash(v, token_prefix, token_suffix, hash)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_pretty_json
|
96
|
+
JSON.pretty_generate(root)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def is_hash_intersect(target_hash, constraint_hash)
|
102
|
+
# failed search if constraint_hash as invalid keys
|
103
|
+
return nil if (constraint_hash.keys - target_hash.keys).length > 0
|
104
|
+
target_hash.each_pair do |k, v|
|
105
|
+
if constraint_hash.key? k
|
106
|
+
return false unless v == constraint_hash[k]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
true
|
110
|
+
end
|
111
|
+
|
112
|
+
def substitute_template_values_from_hash(source_string, prefix, suffix, hash)
|
113
|
+
hash.each do |k, v|
|
114
|
+
source_string.sub!(prefix + k + suffix, v)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|