├── .gitignore ├── COPYING.GPLv3 ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── header.png │ │ ├── icon-doc-gray.png │ │ ├── icon-doc.png │ │ ├── icon-html-gray.png │ │ ├── icon-html.png │ │ ├── icon-pdf-gray.png │ │ ├── icon-pdf.png │ │ ├── left-arrow.png │ │ ├── logo.png │ │ ├── nav.png │ │ └── sidebar.png │ ├── javascripts │ │ ├── application.js │ │ ├── jquery.qtip.js │ │ └── specs.js │ └── stylesheets │ │ ├── application.css.sass │ │ ├── form.css │ │ ├── reset.css │ │ └── specs.css.sass ├── controllers │ ├── application_controller.rb │ └── specs_controller.rb ├── helpers │ ├── application_helper.rb │ └── specs_helper.rb ├── mailers │ └── .gitkeep ├── models │ ├── .gitkeep │ ├── document.rb │ ├── document_file.rb │ ├── document_pdf_html.rb │ ├── document_toc.rb │ ├── document_version.rb │ ├── release.rb │ ├── spec_scope.rb │ └── spec_serie.rb └── views │ ├── layouts │ ├── _default.html.erb │ └── application.html.erb │ └── specs │ ├── _version_layout.html.erb │ ├── list_series.html.erb │ ├── list_specs.html.erb │ ├── list_versions.html.erb │ ├── specs.html.erb │ └── version.html.erb ├── bin ├── bundle ├── rails ├── rake └── setup ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── credentials.yml.enc ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── secret_token.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── routes.rb ├── storage.yml └── webpacker.yml ├── db ├── migrate │ ├── 20121102122425_create_documents.rb │ ├── 20121102122714_create_releases.rb │ ├── 20121102122758_create_document_versions.rb │ ├── 20121102122817_create_document_files.rb │ ├── 20121102123056_add_relationship.rb │ ├── 20121102124156_create_spec_series.rb │ ├── 20121102124344_create_spec_scopes.rb │ ├── 20121105120333_add_stuff_to_document_files.rb │ ├── 20121106133252_add_stuff_to_document_file.rb │ ├── 20121106133427_create_document_tocs.rb │ └── 20121106135728_add_page_to_document_toc.rb ├── schema.rb └── seeds.rb ├── doc └── README_FOR_APP ├── docker-compose.yml ├── docker ├── build.sh ├── database.yml ├── dev.sh ├── spex.service ├── sync_job.sh ├── webapp.conf └── webapp.sh ├── lib ├── assets │ └── .gitkeep └── tasks │ └── .gitkeep ├── log └── .gitkeep ├── public ├── 404.html ├── 422.html ├── 500.html ├── assets │ ├── application-8ca4e69f02a06c2ac2d7c67f67d7c918.js │ ├── application-8ca4e69f02a06c2ac2d7c67f67d7c918.js.gz │ ├── application-95526ebe1d6994de8fbc114f40ef662f.css │ ├── application-95526ebe1d6994de8fbc114f40ef662f.css.gz │ ├── application.css │ ├── application.css.gz │ ├── application.js │ ├── application.js.gz │ ├── header-bb0c527c9f3db33fa76b20981e7f6b78.png │ ├── header.png │ ├── icon-doc-bf4dd12d8d9b07a16636a030a5932ce9.png │ ├── icon-doc-gray-7651cf7358c8bfe59785db63f0b33aef.png │ ├── icon-doc-gray.png │ ├── icon-doc.png │ ├── icon-html-a5de3a543de6e0115ba65eb80bfb6419.png │ ├── icon-html-gray-875aec4e770e3e0ed99f31a5e40c2eb0.png │ ├── icon-html-gray.png │ ├── icon-html.png │ ├── icon-pdf-b6b758527b1dd18cc42b74fe279d7fdb.png │ ├── icon-pdf-gray-b8060cfae02696ae90a82e5cc16aec86.png │ ├── icon-pdf-gray.png │ ├── icon-pdf.png │ ├── left-arrow-38ca3d012e51a5e30efda45dbe5becbe.png │ ├── left-arrow.png │ ├── logo-3fec1e5b2f64dfffc913e20f2bba3e48.png │ ├── logo.png │ ├── manifest.yml │ ├── nav-9ec7b7a925670805ad24430579fb17cd.png │ ├── nav.png │ ├── sidebar-a103568d82f55430682674b37127782a.png │ └── sidebar.png ├── favicon.ico └── robots.txt ├── script ├── analyze_pdf.rb ├── fix_pdf.rb └── init_3gpp.rb ├── specs └── .gitkeep ├── test ├── fixtures │ ├── .gitkeep │ ├── document_files.yml │ ├── document_series.yml │ ├── document_tocs.yml │ ├── document_versions.yml │ ├── documents.yml │ ├── releases.yml │ └── spec_scopes.yml ├── functional │ ├── .gitkeep │ └── specs_controller_test.rb ├── integration │ └── .gitkeep ├── performance │ └── browsing_test.rb ├── test_helper.rb └── unit │ ├── .gitkeep │ ├── document_file_test.rb │ ├── document_serie_test.rb │ ├── document_test.rb │ ├── document_toc_test.rb │ ├── document_version_test.rb │ ├── helpers │ └── specs_helper_test.rb │ ├── release_test.rb │ └── spec_scope_test.rb └── vendor ├── assets ├── javascripts │ └── .gitkeep └── stylesheets │ └── .gitkeep └── plugins └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | /config/database.yml 8 | 9 | # Ignore bundler config 10 | /.bundle 11 | /vendor/bundle 12 | 13 | # Ignore the default SQLite database. 14 | /db/*.sqlite3 15 | 16 | # Ignore all logfiles and tempfiles. 17 | /log/*.log 18 | /tmp 19 | /specs/* 20 | 21 | /public/assets 22 | 23 | /config/master.key 24 | -------------------------------------------------------------------------------- /COPYING.GPLv3: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM phusion/passenger-ruby26:1.0.11 2 | 3 | # Provide pdf2htmlEX 4 | RUN sed -i 's/# deb-src/deb-src/g' /etc/apt/sources.list && \ 5 | apt-get update && \ 6 | apt-get build-dep -yy libpoppler73 && \ 7 | apt-get install -yy wget git xz-utils libpango1.0-dev m4 libtool libltdl-dev perl \ 8 | libjpeg-dev libtiff5-dev libpng-dev libfreetype6-dev libgif-dev libgtk-3-dev \ 9 | libxml2-dev libpango1.0-dev libcairo2-dev libspiro-dev libuninameslist-dev \ 10 | python3-dev ninja-build cmake build-essential \ 11 | libfontforge-dev libfontconfig-dev && \ 12 | rm -rf /var/lib/apt/lists/* 13 | 14 | RUN cd /tmp && \ 15 | git clone --depth=1 git://git.freedesktop.org/git/poppler/poppler -b poppler-0.81.0 && \ 16 | cd poppler && \ 17 | mkdir build && \ 18 | cd build && \ 19 | cmake -DCMAKE_BUILD_TYPE=Release \ 20 | -DCMAKE_INSTALL_PREFIX=/usr/local \ 21 | -DENABLE_LIBOPENJPEG=none .. && \ 22 | make && \ 23 | make install && \ 24 | cd .. && \ 25 | mkdir -p /usr/local/include/poppler/goo \ 26 | /usr/local/include/poppler/fofi \ 27 | /usr/local/include/poppler/splash && \ 28 | cp build/poppler/poppler-config.h /usr/local/include/poppler && \ 29 | cp poppler/*.h /usr/local/include/poppler && \ 30 | cp goo/*.h /usr/local/include/poppler/goo && \ 31 | cp fofi/*.h /usr/local/include/poppler/fofi && \ 32 | cp splash/*.h /usr/local/include/poppler/splash && \ 33 | cd && rm -rf /tmp/poppler 34 | 35 | RUN cd /tmp && \ 36 | git clone --depth=1 https://github.com/pdf2htmlEX/pdf2htmlEX.git -b v0.18.7-poppler-0.81.0 && \ 37 | cd pdf2htmlEX && \ 38 | export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig && \ 39 | mkdir build && \ 40 | cd build && \ 41 | cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr/local .. && \ 42 | make && \ 43 | make install && \ 44 | cd && rm -rf /tmp/pdf2htmlEX 45 | 46 | # Set correct environment variables. 47 | ENV HOME /root 48 | ENV RAILS_ENV production 49 | 50 | # Use baseimage-docker's init process. 51 | CMD ["/sbin/my_init"] 52 | 53 | # nginx 54 | RUN rm -f /etc/service/nginx/down 55 | 56 | RUN apt-get update && \ 57 | apt-get install -V -yy libpoppler-glib-dev libgirepository1.0-dev && \ 58 | gem install bundler 59 | 60 | # app 61 | ADD . /home/app/webapp 62 | RUN rm -f /etc/nginx/sites-enabled/default 63 | RUN mv /home/app/webapp/docker/webapp.conf /etc/nginx/sites-enabled/webapp.conf 64 | RUN mv /home/app/webapp/docker/database.yml /home/app/webapp/config/database.yml 65 | 66 | # init 67 | RUN mkdir -p /etc/my_init.d 68 | RUN mv /home/app/webapp/docker/webapp.sh /etc/my_init.d/webapp.sh 69 | RUN chown -R app /home/app/webapp 70 | 71 | # cron 72 | RUN cp /home/app/webapp/docker/sync_job.sh /etc/cron.weekly/sync_job && \ 73 | chown root:root /etc/cron.weekly/sync_job && \ 74 | chmod 755 /etc/cron.weekly/sync_job 75 | 76 | ENV SECRET_KEY_BASE "nokey" 77 | 78 | USER app 79 | ENV HOME "/home/app/" 80 | RUN cd /home/app/webapp && \ 81 | bundle config set path 'vendor/bundle' && \ 82 | bundle install --jobs 4 83 | 84 | RUN cd /home/app/webapp && \ 85 | date && \ 86 | bundle exec rake assets:precompile && \ 87 | date 88 | 89 | USER root 90 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '6.0.2.1' 4 | 5 | # Bundle edge Rails instead: 6 | # gem 'rails', :git => 'git://github.com/rails/rails.git' 7 | 8 | gem 'mysql2' 9 | gem 'sqlite3' 10 | 11 | # Gems used only for assets and not required 12 | # in production environments by group 13 | group :assets do 14 | gem 'sass' 15 | gem 'sassc-rails' 16 | gem 'coffee-rails' 17 | 18 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes 19 | gem 'therubyracer', :platforms => :ruby 20 | 21 | gem 'uglifier', '>= 1.0.3' 22 | end 23 | 24 | gem 'jquery-rails' 25 | gem 'jquery-ui-rails' 26 | gem 'awesome_print' 27 | gem 'colored' 28 | gem 'nokogiri' 29 | gem 'rubyzip' 30 | gem "zip-zip" 31 | gem 'poppler' 32 | gem 'tzinfo-data' 33 | 34 | gem 'wirble' 35 | 36 | gem 'responders' 37 | 38 | # To use ActiveModel has_secure_password 39 | # gem 'bcrypt-ruby', '~> 3.0.0' 40 | 41 | # To use Jbuilder templates for JSON 42 | # gem 'jbuilder' 43 | 44 | # Use unicorn as the app server 45 | # gem 'unicorn' 46 | 47 | # Deploy with Capistrano 48 | # gem 'capistrano' 49 | 50 | # To use debugger 51 | #gem 'debugger' 52 | # 53 | group :development do 54 | gem 'listen' 55 | end 56 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (6.0.2.1) 5 | actionpack (= 6.0.2.1) 6 | nio4r (~> 2.0) 7 | websocket-driver (>= 0.6.1) 8 | actionmailbox (6.0.2.1) 9 | actionpack (= 6.0.2.1) 10 | activejob (= 6.0.2.1) 11 | activerecord (= 6.0.2.1) 12 | activestorage (= 6.0.2.1) 13 | activesupport (= 6.0.2.1) 14 | mail (>= 2.7.1) 15 | actionmailer (6.0.2.1) 16 | actionpack (= 6.0.2.1) 17 | actionview (= 6.0.2.1) 18 | activejob (= 6.0.2.1) 19 | mail (~> 2.5, >= 2.5.4) 20 | rails-dom-testing (~> 2.0) 21 | actionpack (6.0.2.1) 22 | actionview (= 6.0.2.1) 23 | activesupport (= 6.0.2.1) 24 | rack (~> 2.0, >= 2.0.8) 25 | rack-test (>= 0.6.3) 26 | rails-dom-testing (~> 2.0) 27 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 28 | actiontext (6.0.2.1) 29 | actionpack (= 6.0.2.1) 30 | activerecord (= 6.0.2.1) 31 | activestorage (= 6.0.2.1) 32 | activesupport (= 6.0.2.1) 33 | nokogiri (>= 1.8.5) 34 | actionview (6.0.2.1) 35 | activesupport (= 6.0.2.1) 36 | builder (~> 3.1) 37 | erubi (~> 1.4) 38 | rails-dom-testing (~> 2.0) 39 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 40 | activejob (6.0.2.1) 41 | activesupport (= 6.0.2.1) 42 | globalid (>= 0.3.6) 43 | activemodel (6.0.2.1) 44 | activesupport (= 6.0.2.1) 45 | activerecord (6.0.2.1) 46 | activemodel (= 6.0.2.1) 47 | activesupport (= 6.0.2.1) 48 | activestorage (6.0.2.1) 49 | actionpack (= 6.0.2.1) 50 | activejob (= 6.0.2.1) 51 | activerecord (= 6.0.2.1) 52 | marcel (~> 0.3.1) 53 | activesupport (6.0.2.1) 54 | concurrent-ruby (~> 1.0, >= 1.0.2) 55 | i18n (>= 0.7, < 2) 56 | minitest (~> 5.1) 57 | tzinfo (~> 1.1) 58 | zeitwerk (~> 2.2) 59 | awesome_print (1.8.0) 60 | builder (3.2.4) 61 | cairo (1.16.4) 62 | native-package-installer (>= 1.0.3) 63 | pkg-config (>= 1.2.2) 64 | cairo-gobject (3.4.1) 65 | cairo (>= 1.16.2) 66 | glib2 (= 3.4.1) 67 | coffee-rails (5.0.0) 68 | coffee-script (>= 2.2.0) 69 | railties (>= 5.2.0) 70 | coffee-script (2.4.1) 71 | coffee-script-source 72 | execjs 73 | coffee-script-source (1.12.2) 74 | colored (1.2) 75 | concurrent-ruby (1.1.5) 76 | crass (1.0.6) 77 | erubi (1.9.0) 78 | execjs (2.7.0) 79 | ffi (1.11.3) 80 | gio2 (3.4.1) 81 | gobject-introspection (= 3.4.1) 82 | glib2 (3.4.1) 83 | native-package-installer (>= 1.0.3) 84 | pkg-config (>= 1.3.5) 85 | globalid (0.4.2) 86 | activesupport (>= 4.2.0) 87 | gobject-introspection (3.4.1) 88 | glib2 (= 3.4.1) 89 | i18n (1.7.0) 90 | concurrent-ruby (~> 1.0) 91 | jquery-rails (4.3.5) 92 | rails-dom-testing (>= 1, < 3) 93 | railties (>= 4.2.0) 94 | thor (>= 0.14, < 2.0) 95 | jquery-ui-rails (6.0.1) 96 | railties (>= 3.2.16) 97 | libv8 (3.16.14.19) 98 | listen (3.7.1) 99 | rb-fsevent (~> 0.10, >= 0.10.3) 100 | rb-inotify (~> 0.9, >= 0.9.10) 101 | loofah (2.19.0) 102 | crass (~> 1.0.2) 103 | nokogiri (>= 1.5.9) 104 | mail (2.7.1) 105 | mini_mime (>= 0.1.1) 106 | marcel (0.3.3) 107 | mimemagic (~> 0.3.2) 108 | method_source (0.9.2) 109 | mimemagic (0.3.10) 110 | nokogiri (~> 1) 111 | rake 112 | mini_mime (1.0.2) 113 | mini_portile2 (2.8.0) 114 | minitest (5.16.3) 115 | mysql2 (0.5.4) 116 | native-package-installer (1.0.9) 117 | nio4r (2.5.8) 118 | nokogiri (1.13.9) 119 | mini_portile2 (~> 2.8.0) 120 | racc (~> 1.4) 121 | pkg-config (1.4.0) 122 | poppler (3.4.1) 123 | cairo-gobject (= 3.4.1) 124 | gio2 (= 3.4.1) 125 | racc (1.6.0) 126 | rack (2.2.4) 127 | rack-test (2.0.2) 128 | rack (>= 1.3) 129 | rails (6.0.2.1) 130 | actioncable (= 6.0.2.1) 131 | actionmailbox (= 6.0.2.1) 132 | actionmailer (= 6.0.2.1) 133 | actionpack (= 6.0.2.1) 134 | actiontext (= 6.0.2.1) 135 | actionview (= 6.0.2.1) 136 | activejob (= 6.0.2.1) 137 | activemodel (= 6.0.2.1) 138 | activerecord (= 6.0.2.1) 139 | activestorage (= 6.0.2.1) 140 | activesupport (= 6.0.2.1) 141 | bundler (>= 1.3.0) 142 | railties (= 6.0.2.1) 143 | sprockets-rails (>= 2.0.0) 144 | rails-dom-testing (2.0.3) 145 | activesupport (>= 4.2.0) 146 | nokogiri (>= 1.6) 147 | rails-html-sanitizer (1.4.3) 148 | loofah (~> 2.3) 149 | railties (6.0.2.1) 150 | actionpack (= 6.0.2.1) 151 | activesupport (= 6.0.2.1) 152 | method_source 153 | rake (>= 0.8.7) 154 | thor (>= 0.20.3, < 2.0) 155 | rake (13.0.1) 156 | rb-fsevent (0.10.3) 157 | rb-inotify (0.10.1) 158 | ffi (~> 1.0) 159 | ref (2.0.0) 160 | responders (3.0.0) 161 | actionpack (>= 5.0) 162 | railties (>= 5.0) 163 | rubyzip (2.3.2) 164 | sass (3.7.4) 165 | sass-listen (~> 4.0.0) 166 | sass-listen (4.0.0) 167 | rb-fsevent (~> 0.9, >= 0.9.4) 168 | rb-inotify (~> 0.9, >= 0.9.7) 169 | sassc (2.2.1) 170 | ffi (~> 1.9) 171 | sassc-rails (2.1.2) 172 | railties (>= 4.0.0) 173 | sassc (>= 2.0) 174 | sprockets (> 3.0) 175 | sprockets-rails 176 | tilt 177 | sprockets (4.0.0) 178 | concurrent-ruby (~> 1.0) 179 | rack (> 1, < 3) 180 | sprockets-rails (3.2.1) 181 | actionpack (>= 4.0) 182 | activesupport (>= 4.0) 183 | sprockets (>= 3.0.0) 184 | sqlite3 (1.5.3) 185 | mini_portile2 (~> 2.8.0) 186 | therubyracer (0.12.3) 187 | libv8 (~> 3.16.14.15) 188 | ref 189 | thor (1.0.1) 190 | thread_safe (0.3.6) 191 | tilt (2.0.10) 192 | tzinfo (1.2.10) 193 | thread_safe (~> 0.1) 194 | tzinfo-data (1.2019.3) 195 | tzinfo (>= 1.0.0) 196 | uglifier (4.2.0) 197 | execjs (>= 0.3.0, < 3) 198 | websocket-driver (0.7.1) 199 | websocket-extensions (>= 0.1.0) 200 | websocket-extensions (0.1.5) 201 | wirble (0.1.3) 202 | zeitwerk (2.6.1) 203 | zip-zip (0.3) 204 | rubyzip (>= 1.0.0) 205 | 206 | PLATFORMS 207 | ruby 208 | 209 | DEPENDENCIES 210 | awesome_print 211 | coffee-rails 212 | colored 213 | jquery-rails 214 | jquery-ui-rails 215 | listen 216 | mysql2 217 | nokogiri 218 | poppler 219 | rails (= 6.0.2.1) 220 | responders 221 | rubyzip 222 | sass 223 | sassc-rails 224 | sqlite3 225 | therubyracer 226 | tzinfo-data 227 | uglifier (>= 1.0.3) 228 | wirble 229 | zip-zip 230 | 231 | BUNDLED WITH 232 | 2.1.2 233 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This is speX 2 | 3 | I found the 3GPP documents hard to find, hard to stay up-to-date, etc ... 4 | So this is an alternative service to expose 3GPP specifications in a different architecture. 5 | 6 | Feel free to fork. 7 | 8 | Service is provided at http://spex.cor-net.org 9 | 10 | ## How to run the service 11 | 12 | ### Using Docker 13 | 14 | The docker image is available as `corfr/spex` or from sources: 15 | ``` 16 | host:spex$ docker build -t spex . 17 | ``` 18 | 19 | In the following we'll run the spex container. Along with it comes a MySQL database and a docker volume for data storage. 20 | 21 | ```console 22 | host:~$ docker volume create spex-storage 23 | host:~$ docker network create spex-net 24 | host:~$ docker run --name spex-mysql --network spex-net -e MYSQL_ROOT_PASSWORD=spex -d mysql:latest 25 | ``` 26 | ``` 27 | # connect to the mysql container 28 | host:~$ docker run -it --network spex-net --rm mysql mysql -h spex-mysql -u root -p 29 | # within mysql, create a new database 30 | spex-mysql:~$ mysql 31 | mysql> CREATE DATABASE spex; 32 | ``` 33 | 34 | Now we can start spex, point it to the MySQL database, and mount the data storage. 35 | ``` 36 | host:~$ docker run --name spex --network spex-net -e MYSQL_ENV_DB_NAME=spex -e MYSQL_ENV_DB_USER=root -e MYSQL_ENV_DB_PASS=spex -e MYSQL_PORT_3306_TCP_ADDR=spex-mysql -v spex-storage:/home/app/webapp/specs -p 3000:80 corfr/spex 37 | ``` 38 | 39 | On first run, you'll need to manually populate the database: 40 | ``` 41 | host:~$ docker exec -ti -u app spex bash 42 | spex:~$ cd /home/app/webapp 43 | # if the database cannot be setup, first disable the security checks 44 | spex:webapp$ export DISABLE_DATABASE_ENVIRONMENT_CHECK=1 45 | spex:webapp$ bundle exec rake db:setup 46 | spex:webapp$ bundle exec /home/app/webapp/script/init_3gpp.rb 47 | ``` 48 | 49 | The spex website is now available on `http://localhost:3000` 50 | 51 | ### Using Docker Compose 52 | 53 | From the top-level directory: 54 | ``` 55 | docker-compose pull 56 | docker-compose build 57 | docker-compose up -d 58 | ``` 59 | 60 | On first run, you'll need to manually populate the database: 61 | ``` 62 | host:~$ docker-compose exec spex bash 63 | spex:~$ cd /home/app/webapp 64 | # if the database cannot be setup, first disable the security checks 65 | spex:webapp$ export DISABLE_DATABASE_ENVIRONMENT_CHECK=1 66 | spex:webapp$ bundle exec rake db:setup 67 | spex:webapp$ bundle exec /home/app/webapp/script/init_3gpp.rb 68 | ``` 69 | 70 | The spex website is now available on `http://localhost:3000` 71 | 72 | ### Using rake (development) 73 | 74 | ``` 75 | bundle install 76 | bundle exec rails server 77 | ``` 78 | 79 | To populate the database, use: 80 | ``` 81 | bundle exec rake db:setup 82 | scripts/init_3gpp.rb 83 | ``` 84 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_directory ../javascripts .js 2 | //= link_directory ../stylesheets .css 3 | //= link_tree ../javascripts .js 4 | //= link_tree ../images 5 | -------------------------------------------------------------------------------- /app/assets/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/header.png -------------------------------------------------------------------------------- /app/assets/images/icon-doc-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/icon-doc-gray.png -------------------------------------------------------------------------------- /app/assets/images/icon-doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/icon-doc.png -------------------------------------------------------------------------------- /app/assets/images/icon-html-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/icon-html-gray.png -------------------------------------------------------------------------------- /app/assets/images/icon-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/icon-html.png -------------------------------------------------------------------------------- /app/assets/images/icon-pdf-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/icon-pdf-gray.png -------------------------------------------------------------------------------- /app/assets/images/icon-pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/icon-pdf.png -------------------------------------------------------------------------------- /app/assets/images/left-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/left-arrow.png -------------------------------------------------------------------------------- /app/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/logo.png -------------------------------------------------------------------------------- /app/assets/images/nav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/nav.png -------------------------------------------------------------------------------- /app/assets/images/sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/assets/images/sidebar.png -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 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 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require jquery.qtip 16 | //= require jquery-ui 17 | //= require_tree . 18 | 19 | $(document).ready(function() { 20 | // $("span.timeago").timeago(); 21 | 22 | $(".with-tooltip").each( 23 | function(index, elmt) 24 | { 25 | pos = { 26 | at: 'bottom right', 27 | my: 'top left' 28 | }; 29 | 30 | if($(this).hasClass('with-tooltip-left')) { 31 | pos = { 32 | at: 'bottom left', 33 | my: 'top right' 34 | }; 35 | } 36 | 37 | $(this).qtip( 38 | { 39 | content: $(this).next(".assos-tooltip").html(), 40 | position: pos, 41 | style: { 42 | classes: 'ui-tooltip-shadow ui-tooltip-light ui-tooltip-rounded' 43 | } 44 | }); 45 | } 46 | ); 47 | 48 | $("#scopes h4.header").each( 49 | function(index, elmt) 50 | { 51 | title = $(elmt).children("a"); 52 | elmt_content = $(elmt).next(); 53 | 54 | $(title).removeAttr("href"); 55 | 56 | if(!$(this).parent().hasClass('expanded')) 57 | { 58 | $(elmt_content).fadeOut('fast'); 59 | $(this).parent().addClass('collapsed') 60 | } 61 | 62 | $(title).data("child", elmt_content); 63 | $(title).click(function() { 64 | if(!$(this).parent().parent().hasClass('expanded')) 65 | { 66 | elmt = $(this).data("child"); 67 | $(elmt).fadeIn('fast'); 68 | $(this).parent().parent().addClass('expanded'); 69 | $(this).parent().parent().removeClass('collapsed'); 70 | } 71 | else 72 | { 73 | elmt = $(this).data("child"); 74 | $(elmt).fadeOut('fast'); 75 | $(this).parent().parent().removeClass('expanded'); 76 | $(this).parent().parent().addClass('collapsed'); 77 | } 78 | }); 79 | } 80 | ); 81 | }); 82 | -------------------------------------------------------------------------------- /app/assets/javascripts/specs.js: -------------------------------------------------------------------------------- 1 | 2 | function update_page_nb(page_nb) { 3 | 4 | // console.debug("update_page_nb " + page_nb + " (actual => " + current_page() + ")"); 5 | 6 | if( current_page() != parseInt(page_nb)) { 7 | $("#page_jump").val(page_nb); 8 | } 9 | } 10 | 11 | function show_page(page_nb) { 12 | // console.debug("show_page " + page_nb); 13 | if(!is_page_visible(page_nb)) { 14 | update_page_nb(page_nb); 15 | 16 | current_offset = $("#pdf-main").scrollTop(); 17 | next_offset = $("#p" + parseInt(page_nb).toString(16)).parent().offset().top; 18 | // console.debug("scroll to " + current_offset + " " + next_offset); 19 | $("#pdf-main").scrollTop(current_offset + next_offset - 50); 20 | } 21 | } 22 | 23 | function is_page_visible(page_nb) { 24 | if( $("#p" + parseInt(page_nb).toString(16)).length == 0) 25 | return false 26 | 27 | page_top = $("#p" + parseInt(page_nb).toString(16)).offset().top; 28 | page_height = $("#p" + parseInt(page_nb).toString(16)).height(); 29 | page_bottom = page_top + page_height; 30 | 31 | win_height = $("#pdf-main").height(); 32 | 33 | // console.debug(":" + page_nb + " " + page_top + " " + page_bottom + " " + win_height); 34 | 35 | if( (page_bottom < 0) || (page_top > win_height) ) 36 | return false 37 | 38 | return true 39 | } 40 | 41 | function current_page() { 42 | return parseInt($("#page_jump").val()); 43 | } 44 | 45 | function is_current_page_visible() { 46 | return is_page_visible(current_page()); 47 | } 48 | 49 | $(document).ready(function() { 50 | 51 | $("#pdf-main").css("background","none"); 52 | 53 | $("#page_toc").change(function() { 54 | show_page( $(this).val() ); 55 | }); 56 | 57 | $("#page_jump").change( function() { 58 | console.debug("page_jump changed => ");// + page_change_in_progress); 59 | show_page( $(this).val() ); 60 | }); 61 | 62 | $("#pdf-main").scroll(function() { 63 | // console.debug("Current: " + is_current_page_visible()); 64 | 65 | if(true) {//!is_current_page_visible()) { 66 | nb_pages = parseInt($("#page_count").text()); 67 | for(var i = 1; i < nb_pages; i++) { 68 | // console.debug(i + ":" + is_page_visible(i)); 69 | if(is_page_visible(i)) { 70 | update_page_nb(i); 71 | break; 72 | } 73 | } 74 | } 75 | }); 76 | }); -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css.sass: -------------------------------------------------------------------------------- 1 | /* 2 | **= require reset 3 | **= require form 4 | **= require_self 5 | **= require_tree . 6 | */ 7 | 8 | body 9 | margin: 0 10 | padding: 0 11 | background: #ededed image-url('sidebar.png') repeat-y left 12 | font: 75%/1.5 sans-serif 13 | 14 | a 15 | color: lightslategray 16 | text-decoration: none 17 | &:hover 18 | color: #0060a3 19 | &:visited 20 | opacity: .8 21 | filter: alpha(opacity = 80) 22 | 23 | img 24 | border-width: 0px 25 | 26 | #header 27 | width: 100% 28 | height: 30px 29 | background: image-url('header.png') 30 | line-height: 14px 31 | position: fixed 32 | top: 0px 33 | z-index: 999 34 | 35 | img.logo 36 | float: left 37 | 38 | span.main-title a 39 | color: #fcfcfc 40 | font-weight: bold 41 | font-size: 12px 42 | margin-left: 10px 43 | position: relative 44 | top: 2px 45 | 46 | span.sub-title 47 | color: #ccc 48 | font-weight: bold 49 | font-size: 10px 50 | margin-left: 10px 51 | 52 | #nav 53 | width: 100% 54 | height: 24px 55 | background: image-url('nav.png') repeat-x 56 | -webkit-box-shadow: 0px 3px 7px 0px rgba(0, 0, 0, 0.5) 57 | box-shadow: 0px 3px 7px 0px rgba(0, 0, 0, 0.5) 58 | color: white 59 | position: fixed 60 | top: 30px 61 | opacity: 0.8 62 | z-index: 1000 63 | 64 | span.left-arrow 65 | display: block 66 | background: #282828 image-url('left-arrow.png') no-repeat right 67 | min-width: 60px 68 | float: left 69 | height: 24px 70 | line-height: 25px 71 | text-indent: 10px 72 | padding-right: 10px 73 | color: #eee 74 | 75 | span.v_v 76 | font-size: 80% 77 | 78 | span.v_release 79 | color: white 80 | font-weight: bold 81 | font-size: 110% 82 | 83 | #sidebar 84 | width: 35px 85 | position: fixed 86 | top: 55px 87 | left: 0px 88 | z-index: 1001 89 | 90 | div.icon 91 | width: 16px 92 | margin: 7px auto 93 | 94 | a 95 | width: 16px 96 | text-align: center 97 | 98 | img 99 | opacity: 0.7 100 | 101 | a:hover img 102 | opacity: 1.0 103 | 104 | #content 105 | margin-top: 60px 106 | 107 | h1, h2, h3, h4, h5 108 | font-family: Arial, sans-serif 109 | 110 | table.data-table 111 | 112 | th, td 113 | padding: 5px 15px 114 | text-align: left 115 | border-bottom: 1px solid #ccc 116 | 117 | th 118 | border-width: 2px 119 | 120 | td 121 | color: #666 122 | 123 | tr 124 | border-left: 2px solid transparent 125 | border-right: 1px solid transparent 126 | 127 | tr.block-part 128 | border-left: 2px solid #888 129 | border-right: 1px solid #888 130 | background: #fcfcfc 131 | 132 | tr.block-end 133 | @extend tr.block-part 134 | td 135 | border-bottom-color: #888 136 | 137 | tr:last-child th, 138 | tr:last-child td 139 | border-bottom: none 140 | 141 | tr:nth-child(even) 142 | background: #eee 143 | 144 | tr:hover 145 | background: #F2F2F2 146 | 147 | td.link-column 148 | width: 50px 149 | text-align: center 150 | 151 | tr.sub-header td 152 | font-weight: bold 153 | padding: 5px 5px 154 | border-bottom-color: #888 155 | background: white 156 | 157 | nav.breadcrumb 158 | font-size: 12px 159 | color: #ccc 160 | line-height: 24px 161 | position: relative 162 | left: 10px 163 | 164 | a 165 | margin: 0 5px 166 | text-decoration: none 167 | color: #ccc 168 | 169 | a:first-child 170 | margin-left: 0 171 | 172 | a:hover, a:focus 173 | color: #fff 174 | 175 | a:active 176 | color: #666 177 | 178 | strong 179 | margin-left: 5px 180 | font-weight: bold 181 | color: #ddd 182 | -------------------------------------------------------------------------------- /app/assets/stylesheets/form.css: -------------------------------------------------------------------------------- 1 | form fieldset { 2 | margin: 0 0 10px 0; 3 | padding: 0 0 10px 0; 4 | font-size: 14px; 5 | border: 0; 6 | border-bottom: 1px solid #eee; 7 | } 8 | form fieldset a { 9 | font-size: 12px; 10 | border-bottom: 1px dashed gray; 11 | color: gray; 12 | } 13 | form fieldset a:hover { 14 | color: rgb(67, 107, 149); 15 | border-color: rgb(67, 107, 149); 16 | } 17 | form fieldset.form-actions { 18 | margin: 0 0 0 20%; 19 | padding: 0; 20 | border: none; 21 | } 22 | form fieldset.check { 23 | padding-left: 20%; 24 | } 25 | form fieldset label { 26 | float: left; 27 | width: 20%; 28 | margin: 4px 0 5px 0; 29 | font-size: 12px; 30 | } 31 | form fieldset table label { 32 | float: none; 33 | width: auto; 34 | margin: 0 0 0 10px; 35 | vertical-align: middle; 36 | } 37 | form fieldset table input { 38 | vertical-align: middle; 39 | position: relative; 40 | top: -1px; 41 | *overflow: hidden; 42 | } 43 | 44 | form fieldset table td { 45 | vertical-align: middle; 46 | line-height: 15px; 47 | } 48 | form fieldset.check label { 49 | display: inline; 50 | float: none; 51 | width: auto; 52 | font-weight: normal; 53 | } 54 | form fieldset.radio ul li label { 55 | display: inline; 56 | float: none; 57 | width: auto; 58 | font-weight: normal; 59 | } 60 | form input.form-text, 61 | form textarea { 62 | display: block; 63 | font-size: 12px; 64 | border: 1px solid #ddd; 65 | background: #f5f5f5; 66 | -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.05); 67 | -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,.05); 68 | -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.05); 69 | -webkit-border-radius: 4px; 70 | -moz-border-radius: 4px; 71 | border-radius: 4px; 72 | } 73 | form fieldset input.form-text, 74 | form fieldset textarea { 75 | width: 50%; 76 | padding: 5px; 77 | } 78 | form fieldset input.form-text:focus { 79 | border: 1px solid #ccc; 80 | background: #fff; 81 | } 82 | form fieldset textarea { 83 | height: 150px; 84 | } 85 | form fieldset select { 86 | min-width: 25%; 87 | margin: 0; 88 | font-size: 12px; 89 | } 90 | form fieldset.radio ul { 91 | margin: 5px 0 0 20%; 92 | } 93 | form fieldset.radio ul li { 94 | margin: 0 0 5px 0; 95 | } 96 | form fieldset.radio ul li:last-child { 97 | margin: 0; 98 | } 99 | form fieldset p.form-help { 100 | margin: 5px 0 0 20%; 101 | font-size: 12px; 102 | color: #999; 103 | } 104 | form input[type="submit"] { 105 | margin: 0; 106 | padding: 5px 10px; 107 | font-size: 12px; 108 | font-weight: bold; 109 | border: 1px solid #ccc; 110 | background: #eee; 111 | -webkit-border-radius: 4px; 112 | -moz-border-radius: 4px; 113 | border-radius: 4px; 114 | } 115 | form input[type="submit"]:hover, 116 | form input[type="submit"]:focus { 117 | border: 1px solid #bbb; 118 | background: #e5e5e5; 119 | } 120 | form input[type="submit"]:active { 121 | border: 1px solid #ccc; 122 | background: #eee; 123 | } 124 | @media screen and (max-width: 600px) { 125 | form fieldset label { 126 | display: block; 127 | float: none; 128 | width: auto; 129 | margin: 0 0 5px 0; 130 | } 131 | form fieldset.form-actions, 132 | form fieldset.check, 133 | form fieldset.radio ul, 134 | form fieldset p.form-help { 135 | margin-left: 0; 136 | padding-left: 0; 137 | } 138 | form fieldset input.form-text, 139 | form fieldset textarea { 140 | width: 100%; 141 | -webkit-box-sizing: border-box; 142 | -moz-box-sizing: border-box; 143 | box-sizing: border-box; 144 | } 145 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/reset.css: -------------------------------------------------------------------------------- 1 | /* Eric Meyer's Reset CSS v2.0 - http://cssreset.com */ 2 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0} -------------------------------------------------------------------------------- /app/assets/stylesheets/specs.css.sass: -------------------------------------------------------------------------------- 1 | 2 | .data-block 3 | width: 800px 4 | margin: 10px auto 5 | margin-top: 80px 6 | 7 | .block 8 | background: white 9 | border: 1px solid #777 10 | -webkit-border-radius: 3px 11 | border-radius: 3px 12 | -webkit-box-shadow: 3px 3px 6px 0px rgba(0, 0, 0, 0.4) 13 | box-shadow: 3px 3px 6px 0px rgba(0, 0, 0, 0.4) 14 | 15 | h4 16 | padding: 5px 17 | font-weight: bold 18 | margin: 0 10px 19 | border-bottom: 1px solid #AAA 20 | 21 | h5 22 | padding: 5px 23 | margin: 0 10px 24 | 25 | table.data-table 26 | width: 780px 27 | margin: 10px auto 28 | 29 | #notice 30 | @extend .data-block 31 | @extend .block 32 | margin-bottom: -60px 33 | padding: 5px 0px 34 | text-indent: 10px 35 | background: #ffaaaa 36 | 37 | li.block 38 | margin: 10px 0 39 | 40 | li.collapsed h4 41 | border-bottom: 0 42 | 43 | table.data-table 44 | 45 | td.icon-link 46 | width: 20px 47 | text-align: center 48 | padding-left: 5px 49 | padding-right: 5px 50 | 51 | 52 | #nav form input.form-text 53 | background: white 54 | display: inline-block 55 | padding-left: 10px 56 | margin-left: 5px 57 | 58 | #page_jump 59 | width: 20px 60 | text-align: right 61 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end 4 | -------------------------------------------------------------------------------- /app/controllers/specs_controller.rb: -------------------------------------------------------------------------------- 1 | class SpecsController < ApplicationController 2 | respond_to :html, :doc, :pdf 3 | 4 | def specs 5 | @navs = [] 6 | 7 | @page_name = "Specs" 8 | @url_options = {} 9 | title_base = "speX" 10 | @title = title_base 11 | 12 | begin 13 | if params["serie"].nil? 14 | # Display series list 15 | render :list_series 16 | else 17 | @serie = SpecSerie.find_by_index(params["serie"].to_i) 18 | 19 | @navs.push({ :name => @page_name, :link => specs_res_url(@url_options) }) 20 | @page_name = "#{@serie.index} Series" 21 | @title = "#{title_base} :: #{@page_name}" 22 | @url_options[:serie] = params["serie"] 23 | 24 | if params["spec"].nil? 25 | # Display spec list 26 | render :list_specs 27 | else 28 | spec_desc = Document.parse_no(params["spec"]) 29 | @spec = Document.find_by_desc(spec_desc) 30 | 31 | if @spec.nil? 32 | render :status => :not_found 33 | return 34 | end 35 | 36 | @navs.push({ :name => @page_name, :link => specs_res_url(@url_options) }) 37 | @page_name = @spec.name 38 | @title = "#{title_base} :: #{@page_name}" 39 | @url_options[:spec] = params["spec"] 40 | 41 | if params["version"].nil? 42 | # Display version list 43 | render :list_versions 44 | else 45 | version_desc = DocumentVersion.parse_version(params["version"]) 46 | @version = @spec.document_versions.where(version_desc).first 47 | 48 | @navs.push({ :name => @page_name, :link => specs_res_url(@url_options) }) 49 | @page_name = @version.version 50 | @title += " :: #{@page_name}" 51 | @url_options[:version] = params["version"] 52 | 53 | # Display version 54 | 55 | if params["format"].nil? 56 | @content_partial = "version_layout" 57 | render :version 58 | else 59 | respond_to do |format| 60 | format.any(:html, :pdf, :doc) { 61 | file_format = params["format"].to_sym 62 | if @version.has_format?(file_format) or @version.retrieve_format(file_format) 63 | send_file @version.get_file(file_format).local_path, :disposition => 'inline' 64 | else 65 | redirect_to specs_res_url( { 66 | :serie => params["serie"], 67 | :spec => params["spec"], 68 | :version => params["version"] 69 | }), 70 | :notice => "Unable to retrieve format #{file_format}" 71 | end 72 | } 73 | end 74 | end 75 | end 76 | end 77 | end 78 | rescue Exception => e 79 | @exception = e 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/specs_helper.rb: -------------------------------------------------------------------------------- 1 | module SpecsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/mailers/.gitkeep -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/app/models/.gitkeep -------------------------------------------------------------------------------- /app/models/document.rb: -------------------------------------------------------------------------------- 1 | class Document < ActiveRecord::Base 2 | attr_reader :desc 3 | 4 | belongs_to :spec_serie 5 | 6 | has_many :document_versions 7 | 8 | after_initialize :init_desc 9 | 10 | def self.parse_no(spec_no) 11 | m = spec_no.match(/(\d+)[\._](\d+)(?:-(\d))?(U)?/) 12 | raise "Unable to parse '#{spec_no}'" if not m 13 | 14 | res = { 15 | :serie => m[1].to_i, 16 | :number => m[2].to_i 17 | } 18 | 19 | res[:part] = m[3].to_i if not m[3].nil? 20 | res[:u] = true if not m[4].nil? 21 | 22 | res 23 | end 24 | 25 | def self.desc_to_name(desc) 26 | name = if desc[:serie].to_i < 13 or desc[:u] 27 | "%02d.%02d" % [desc[:serie],desc[:number]] 28 | else 29 | "%02d.%03d" % [desc[:serie],desc[:number]] 30 | end 31 | 32 | name += "-#{desc[:part]}" if not desc[:part].nil? 33 | name += "U" if desc[:u] 34 | return name 35 | end 36 | 37 | def self.find_by_desc(desc) 38 | name = self.desc_to_name(desc) 39 | self.find_by_name(name) 40 | end 41 | 42 | def init_desc 43 | @desc = Document.parse_no(name) 44 | end 45 | 46 | def name_3gpp 47 | n = "%02d" % self.spec_serie.index 48 | n += full_spec_number 49 | n += "-#{self.spec_part}" if !self.spec_part.nil? 50 | n += "U" if desc[:u] 51 | n 52 | end 53 | 54 | def full_spec_number 55 | ( ( spec_serie.index < 13 or desc[:u] ) ? "%02d" : "%03d" ) % spec_number 56 | end 57 | 58 | def info_page_url 59 | "http://www.3gpp.org/ftp/Specs/html-info/#{name_3gpp}.htm" 60 | end 61 | 62 | def parse_no 63 | begin 64 | self.spec_serie = SpecSerie.find_by_index(desc[:serie]) 65 | rescue 66 | raise "Unable to find serie #{desc[:serie]} for #{name}" if spec_serie.nil? 67 | end 68 | 69 | self.spec_part = desc[:part] if not desc[:part].nil? 70 | self.spec_number = desc[:number] 71 | 72 | desc 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /app/models/document_file.rb: -------------------------------------------------------------------------------- 1 | class DocumentFile < ActiveRecord::Base 2 | belongs_to :document_version 3 | 4 | has_many :document_tocs 5 | 6 | def self.get_filename(doc_version, format) 7 | name = "%02d%02d" % [doc_version.document.spec_serie.index, doc_version.document.spec_number] 8 | name += "-#{doc_version.document.spec_part}" if !doc_version.document.spec_part.nil? 9 | name += "-#{doc_version.version(:letters)}" 10 | name += ".#{format}" 11 | end 12 | 13 | def self.get_local_path(doc_version, format) 14 | Rails.root.join( 15 | 'specs', 16 | doc_version.document.spec_serie.index.to_s, 17 | doc_version.document.name.gsub('.','_'), 18 | self.get_filename(doc_version, format)) 19 | end 20 | 21 | def local_path 22 | DocumentFile.get_local_path(document_version, format) 23 | end 24 | 25 | PREFIX = %W(To Go Mo Ko o).freeze 26 | 27 | def size_h 28 | s = size.to_f 29 | i = PREFIX.length - 1 30 | while s > 512 && i > 0 31 | i -= 1 32 | s /= 1024 33 | end 34 | ((s > 9 || s.modulo(1) < 0.1 ? '%d' : '%.1f') % s) + ' ' + PREFIX[i] 35 | end 36 | 37 | def analyze 38 | sha1 = Digest::SHA1.new 39 | 40 | File.open(local_path) do |file| 41 | buffer = '' 42 | 43 | while not file.eof 44 | file.read(512, buffer) 45 | sha1.update(buffer) 46 | end 47 | 48 | self.size = file.size 49 | self.sha1 = sha1.to_s 50 | end 51 | 52 | case format 53 | when :pdf then analyze_pdf 54 | end 55 | end 56 | 57 | 58 | def analyze_pdf 59 | 60 | if format != "pdf" 61 | return false 62 | end 63 | 64 | doc = Poppler::Document.new(local_path.to_s) 65 | indexer = Poppler::IndexIter.new(doc) 66 | 67 | # Clean 68 | document_tocs.delete_all 69 | 70 | # Update page count 71 | self.nb_pages ||= doc.n_pages 72 | save 73 | 74 | puts "This is the number of pages #{doc.n_pages}".cyan 75 | 76 | pdf_walk_index(indexer) 77 | 78 | true 79 | end 80 | 81 | def content 82 | File.read( local_path ) 83 | end 84 | 85 | private 86 | 87 | def pdf_walk_index(indexer, parent = nil, depth = 0) 88 | 89 | indexer.each do |i| 90 | 91 | toc_entry = document_tocs.create 92 | toc_entry.title = i.action.title 93 | toc_entry.page = i.action.dest.page_num 94 | toc_entry.parent = parent 95 | toc_entry.level = depth 96 | toc_entry.save 97 | 98 | child = i.child 99 | 100 | pdf_walk_index(child, toc_entry, depth + 1) if child.nil? == false and depth < 1 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /app/models/document_pdf_html.rb: -------------------------------------------------------------------------------- 1 | class DocumentPdfHtml 2 | 3 | def initialize(path) 4 | @source_html = Nokogiri::HTML(open(path)) 5 | end 6 | 7 | def html_content 8 | content = js_content 9 | content += css_content 10 | content += body_content 11 | end 12 | 13 | def page_count 14 | if @page_count.nil? 15 | @page_count = @source_html.xpath('/div[@class="d" and position()=last()]').to_s 16 | puts "Page Count" 17 | puts @page_count.green 18 | end 19 | 20 | @page_count 21 | end 22 | 23 | private 24 | def body_content 25 | @source_html.xpath('//*[@id="pdf-main"]').to_s 26 | end 27 | 28 | def css_content 29 | content = @source_html.xpath('/html/head/style[1]').to_s 30 | content += @source_html.xpath('/html/head/style[2]').to_s 31 | content += @source_html.xpath('/html/head/style[3]').to_s 32 | end 33 | 34 | def js_content 35 | content = @source_html.xpath('/html/head/script[2]').to_s 36 | content += @source_html.xpath('/html/head/script[3]').to_s 37 | end 38 | 39 | end -------------------------------------------------------------------------------- /app/models/document_toc.rb: -------------------------------------------------------------------------------- 1 | class DocumentToc < ActiveRecord::Base 2 | belongs_to :document_file 3 | 4 | belongs_to :parent, :class_name => "DocumentToc" 5 | has_many :childs, :class_name => "DocumentToc" 6 | end 7 | -------------------------------------------------------------------------------- /app/models/document_version.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | 3 | class DocumentVersion < ActiveRecord::Base 4 | belongs_to :release 5 | belongs_to :document 6 | 7 | has_many :document_files 8 | 9 | def self.chars_version 10 | [0,1,2,3,4,5,6,7,8,9, 11 | 'a','b','c','d','e','f','g','h','i', 12 | 'jh','k','l','m','n','o','p','q','r', 13 | 's','t','u','v','w','x','y','z' 14 | ] 15 | end 16 | 17 | def self.parse_version(version) 18 | m = version.match(/(\d+)[\._](\d+)[\._](\d+)/) 19 | 20 | res = { 21 | :major => m[1].to_i, 22 | :technical => m[2].to_i, 23 | :editorial => m[3].to_i 24 | } 25 | end 26 | 27 | def version(format = nil) 28 | case format 29 | when :letters then 30 | "%s%s%s" % [ 31 | DocumentVersion.chars_version[major], 32 | DocumentVersion.chars_version[technical], 33 | DocumentVersion.chars_version[editorial] 34 | ] 35 | else "v#{major}.#{technical}.#{editorial}" 36 | end 37 | end 38 | 39 | def has_format?(format) 40 | # puts "Has format ? #{(document_files.where(:format => format).count != 0)}" 41 | (document_files.where(:format => format).count != 0) 42 | end 43 | 44 | def get_file(format) 45 | document_files.where(:format => format).first 46 | end 47 | 48 | def retrieve_format(format) 49 | 50 | case format 51 | when :pdf then return retrieve_pdf 52 | when :html then return retrieve_html 53 | when :doc then return retrieve_doc 54 | else raise "Unknown format #{format}" 55 | end 56 | 57 | false 58 | end 59 | 60 | def analyze_pdf 61 | pdf_file = get_file(:pdf) 62 | 63 | if !pdf_file.nil? 64 | pdf_file.analyze_pdf 65 | end 66 | end 67 | 68 | private 69 | 70 | # Get PDF from ETSI servers 71 | def retrieve_pdf 72 | puts "Getting PDF".green 73 | 74 | spec_serie = document.spec_serie.index 75 | 76 | lower_bound = ((document.spec_number)/100).to_i * 100 77 | higher_bound = lower_bound + 99 78 | 79 | url_path = "https://www.etsi.org/deliver/etsi_ts/" 80 | url_path += "1%02d%03d_1%02d%03d/" % [spec_serie,lower_bound,spec_serie,higher_bound] 81 | 82 | if document.spec_part.nil? 83 | doc_nb = "%02d%03d" % [spec_serie,document.spec_number] 84 | else 85 | doc_nb = "%02d%03d%02d" % [spec_serie,document.spec_number,document.spec_part] 86 | end 87 | 88 | url_path += "1#{doc_nb}/" 89 | url_path += "%02d.%02d.%02d_60/" % [major,technical,editorial] 90 | url_path += "ts_1#{doc_nb}v%02d%02d%02dp.pdf" % [major,technical,editorial] 91 | 92 | begin 93 | puts "... from #{url_path}".yellow 94 | 95 | uri = URI(url_path) 96 | Net::HTTP.get_response(uri) { |res| 97 | case res 98 | when Net::HTTPSuccess then 99 | local_path = DocumentFile.get_local_path(self, :pdf) 100 | puts "Storing to #{local_path}" 101 | 102 | FileUtils.mkdir_p(local_path.dirname) unless File.exists?(local_path.dirname) 103 | 104 | File.open(local_path, "wb") { |io| 105 | res.read_body do |segment| 106 | io.write(segment) 107 | end 108 | } 109 | 110 | doc_file = document_files.new 111 | doc_file.format = :pdf 112 | doc_file.analyze 113 | doc_file.save 114 | 115 | return true 116 | else 117 | puts " Unable to get PDF @ #{url_path}: #{res}".yellow 118 | end 119 | } 120 | 121 | rescue => e 122 | puts " Error getting PDF @ #{url_path}: #{e}".red 123 | end 124 | 125 | false 126 | end 127 | 128 | def retrieve_html 129 | puts "Getting HTML".green 130 | 131 | if !has_format? :pdf and !retrieve_pdf 132 | return false 133 | end 134 | 135 | path_pdf = DocumentFile.get_local_path(self, :pdf) 136 | path_html = DocumentFile.get_local_path(self, :html) 137 | 138 | begin 139 | if `pdf2htmlEX #{path_pdf} --dest-dir #{Pathname(path_html).dirname}` 140 | doc_file = document_files.new 141 | doc_file.format = :html 142 | doc_file.analyze 143 | doc_file.save 144 | end 145 | rescue 146 | puts "Unable to convert PDF #{path_pdf} to HTML" 147 | end 148 | end 149 | 150 | def retrieve_doc 151 | puts "Getting DOC".green 152 | 153 | spec_serie = document.spec_serie.index 154 | 155 | url_path = "https://www.3gpp.org/ftp/Specs/archive/" 156 | url_path += "%02d_series/" % spec_serie 157 | 158 | if document.spec_part.nil? 159 | suffix = document.name.end_with?("U") ? "U" : "" 160 | url_path += "%02d.%s%s/" % [spec_serie,document.full_spec_number,suffix] 161 | url_path += "%02d%s%s-%s.zip" % [spec_serie,document.full_spec_number,suffix,version(:letters)] 162 | else 163 | url_path += "%02d.%s-%d/" % [spec_serie,document.full_spec_number,document.spec_part] 164 | url_path += "%02d%s-%d-%s.zip" % [spec_serie,document.full_spec_number,document.spec_part,version(:letters)] 165 | end 166 | 167 | begin 168 | puts "... from #{url_path}".yellow 169 | 170 | # Getting zip 171 | Net::HTTP.get_response( URI(url_path) ) { |res| 172 | case res 173 | when Net::HTTPSuccess then 174 | local_path_zip = DocumentFile.get_local_path(self, :zip) 175 | local_path = DocumentFile.get_local_path(self, :doc) 176 | 177 | puts "Storing to #{local_path}" 178 | 179 | FileUtils.mkdir_p(local_path.dirname) unless File.exists?(local_path.dirname) 180 | 181 | File.open(local_path_zip, "wb") { |io| 182 | res.read_body do |segment| 183 | io.write(segment) 184 | end 185 | } 186 | 187 | # Extracting zip 188 | Zip::ZipFile.open(local_path_zip) do |zf| 189 | zf.each do |e| 190 | puts "Extracting ... #{e.name}" 191 | zf.extract(e.name, local_path) 192 | break 193 | end 194 | end 195 | 196 | # Removing zip 197 | File.delete(local_path_zip) 198 | 199 | doc_file = document_files.new 200 | doc_file.format = :doc 201 | doc_file.analyze 202 | doc_file.save 203 | 204 | return true 205 | end 206 | } 207 | 208 | # rescue 209 | # puts "Error getting DOC @ #{url_path}".red 210 | end 211 | 212 | false 213 | end 214 | 215 | end 216 | -------------------------------------------------------------------------------- /app/models/release.rb: -------------------------------------------------------------------------------- 1 | class Release < ActiveRecord::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/models/spec_scope.rb: -------------------------------------------------------------------------------- 1 | class SpecScope < ActiveRecord::Base 2 | has_many :spec_series, :class_name => 'SpecSerie' 3 | end 4 | -------------------------------------------------------------------------------- /app/models/spec_serie.rb: -------------------------------------------------------------------------------- 1 | class SpecSerie < ActiveRecord::Base 2 | belongs_to :spec_scope 3 | has_many :documents 4 | end 5 | -------------------------------------------------------------------------------- /app/views/layouts/_default.html.erb: -------------------------------------------------------------------------------- 1 | 8 | 19 |
20 | 22 |
23 | <% if flash[:warning] or flash[:notice] %> 24 |
class="warning"<% end %>> 25 | <%= flash[:warning] || flash[:notice] %> 26 |
27 | <% end %> 28 | 29 | <%= yield %> 30 |
31 |
32 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= @title.nil? ? "speX" : @title %> 5 | <%= stylesheet_link_tag "application", :media => "all" %> 6 | <%= javascript_include_tag "application" %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | <% @content_partial ||= "layouts/default" %> 11 | <%= render :partial => @content_partial %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/specs/_version_layout.html.erb: -------------------------------------------------------------------------------- 1 | 7 | 21 |
22 | 42 |
43 | <% if flash[:warning] or flash[:notice] %> 44 |
class="warning"<% end %>> 45 | <%= flash[:warning] || flash[:notice] %> 46 |
47 | <% end %> 48 | <%= yield %> 49 |
50 |
51 | -------------------------------------------------------------------------------- /app/views/specs/list_series.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <% SpecScope.all.each do |scope| %> 3 | <% cls = "block" 4 | cls += " expanded" if scope.id == 3 %> 5 |
  • 6 |

    <%= scope.scope %>

    7 | 8 | <% scope.spec_series.each do |serie| %> 9 | 10 | 11 | 12 | 13 | <% end %> 14 |
    <%= link_to "#{serie.index} Series", specs_res_url(:serie => serie.index) %><%= serie.subject %>
    15 |
  • 16 | <% end %> 17 |
-------------------------------------------------------------------------------- /app/views/specs/list_specs.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

<%= @serie.index %> Series: <%= @serie.subject %>

3 | 4 | <% group = nil %> 5 | 6 | <% i = 0 %> 7 | <% doc_count = @serie.documents.count %> 8 | <% @serie.documents.order(:spec_number).each_with_index do |spec, index| %> 9 | <% block_end = false %> 10 | 11 | <% # Bug with multi-spaces in documents %> 12 | <% if not spec.title[/\s{2,}/].nil? %> 13 | <% spec.title = spec.title.gsub(/\s{2,}/,' ') %> 14 | <% spec.save %> 15 | <% end %> 16 | 17 | <% if !group.nil? and spec.title.start_with? group %> 18 | <% title = "⇒ " + spec.title.sub(group,'').strip %> 19 | <% if (index+1) < doc_count and !(@serie.documents[index+1].title.start_with? group) %> 20 | <% block_end = true %> 21 | <% end %> 22 | <% elsif spec.title.include? ';' %> 23 | <% group = spec.title[/[^;]+;/] %> 24 | <% if (index+1) < doc_count and @serie.documents[index+1].title.start_with? group %> 25 | <% title = "⇒ " + spec.title.sub(group,'').strip %> 26 | 27 | <% else %> 28 | <% group = nil %> 29 | <% title = spec.title %> 30 | <% end %> 31 | <% else %> 32 | <% group = nil %> 33 | <% title = spec.title %> 34 | <% end %> 35 | 36 | <% tr_class = nil %> 37 | <% tr_class = "block-part" if !group.nil? %> 38 | <% tr_class = "block-end" if block_end %> 39 | > 40 | 44 | 45 | 46 | <% end %> 47 |
<%= group[/[^;]+/] %>
<%= title %>
48 |
-------------------------------------------------------------------------------- /app/views/specs/list_versions.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

<%= @spec.name %>: <%= @spec.title %>

3 |
<%= link_to "Info Page @ 3GPP", @spec.info_page_url %>
4 | 5 | <% last_rel = 1000 %> 6 | <% for vers in @spec.document_versions.order('major DESC, technical DESC, editorial DESC') %> 7 | <% if last_rel > vers.major %> 8 | <% last_rel = vers.major %> 9 | 10 | <% end %> 11 | 12 | 17 | <% for format in [:html, :pdf, :doc] %> 18 | 34 | <% end %> 35 | 36 | <% end %> 37 |
<%= vers.release.name %>
<%= link_to vers.version, specs_res_url( { 13 | :serie => @spec.spec_serie.index, 14 | :spec => @spec.name.gsub('.','_'), 15 | :version => vers.version.gsub('.','_') } ) %> 16 |
38 |
39 | -------------------------------------------------------------------------------- /app/views/specs/specs.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @exception.nil? %> 2 |
3 |
4 |

<%= ap(@exception).html_safe %>

5 |
Parameters
6 | 7 | <% params.each do |k,p| %> 8 | 9 | <% end %> 10 |
<%= k %> => <%= p %>
11 |
Backtrace
12 | 13 | <% @exception.backtrace.each do |bt| %> 14 | 15 | <% end %> 16 |
<%= bt %>
17 |
18 |
19 | <% end %> -------------------------------------------------------------------------------- /app/views/specs/version.html.erb: -------------------------------------------------------------------------------- 1 | <% if @doc_html.nil? %> 2 |
3 |

<%= @spec.name %>: <%= @spec.title %>

4 |
<%= @version.version %>
5 | 6 | <% for format in [:html, :pdf, :doc] %> 7 | 23 | <% end %> 24 |
8 | <% if @version.has_format? format %> 9 | <%= file = @version.get_file format 10 | link_to file.format, specs_res_url( { 11 | :serie => @spec.spec_serie.index, 12 | :spec => @spec.name.gsub('.','_'), 13 | :version => @version.version.gsub('.','_'), 14 | :format => file.format } ) %> (<%= file.size_h %>) 15 | <% else %> 16 | <%= link_to "Retrieve #{format}", specs_res_url( { 17 | :serie => @spec.spec_serie.index, 18 | :spec => @spec.name.gsub('.','_'), 19 | :version => @version.version.gsub('.','_'), 20 | :format => format } ) %> 21 | <% end %> 22 |
25 |
26 | <% else %> 27 | <%= @doc_html.page_count %> 28 |
29 | <%= @doc_html.html_content.html_safe %> 30 |
31 | <% end %> 32 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run SpexV1::Application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module SpexV1 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 6.0 13 | 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | 18 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 19 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 20 | # config.time_zone = 'Central Time (US & Canada)' 21 | 22 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 23 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 24 | # config.i18n.default_locale = :de 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: async 9 | #adapter: redis 10 | #url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 11 | #channel_prefix: spex_production 12 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | M1efvvzXaf1Oes66Ub8lBew0eJT/ve1G8K1Edb42k2x4bmRbvQAmkL3lJRf6bS3ZtVIWqu078WZ4Bm9T0TUS24Z73AGBsCJ08dVc2hvAypfr+nY8t0B/VVV/ZtlOcJBZ62lE2o5BXYZqxZODHKFfo3IRhgWJ/wP3hUO0RPP5EIxTn1o7eKiKVyBoyAwRb1vK/S+8XyP3EDvochrg4MkLdyzOR7D13zuSEp63WtrYJWD6RKTK76KrCOG8c5EwgvTnqc5Of7PmxS54lwMHMl1mCYo/ebtAYR7X+utSrq9lVwkZu2JZ8G2lXkd+eJ1Olqq/nr0nyk/4pS4H22+zw1c9pOfx5Tl+HFBlxKHdEC+nwRh+kUsnVKqTprV8//fbTBQXWdRL1dSxcvWJTHZK9HQRIl1Fss8LYd3jBY62--p6NVq5ArnhquZZMz--IaLM+hk5xI1W5TcPfsLcug== -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/dev.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: 16 | adapter: sqlite3 17 | database: db/test.sqlite3 18 | pool: 5 19 | timeout: 5000 20 | 21 | production: 22 | adapter: sqlite3 23 | database: db/prod.sqlite3 24 | pool: 5 25 | timeout: 5000 26 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | # Run rails dev:cache to toggle caching. 17 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 18 | config.action_controller.perform_caching = true 19 | config.action_controller.enable_fragment_cache_logging = true 20 | 21 | config.cache_store = :memory_store 22 | config.public_file_server.headers = { 23 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 24 | } 25 | else 26 | config.action_controller.perform_caching = false 27 | 28 | config.cache_store = :null_store 29 | end 30 | 31 | # Store uploaded files on the local file system (see config/storage.yml for options). 32 | config.active_storage.service = :local 33 | 34 | # Don't care if the mailer can't send. 35 | config.action_mailer.raise_delivery_errors = false 36 | 37 | config.action_mailer.perform_caching = false 38 | 39 | # Print deprecation notices to the Rails logger. 40 | config.active_support.deprecation = :log 41 | 42 | # Raise an error on page load if there are pending migrations. 43 | config.active_record.migration_error = :page_load 44 | 45 | # Highlight code that triggered database queries in logs. 46 | config.active_record.verbose_query_logs = true 47 | 48 | # Debug mode disables concatenation and preprocessing of assets. 49 | # This option may cause significant delays in view rendering with a large 50 | # number of complex assets. 51 | config.assets.debug = true 52 | 53 | # Suppress logger output for asset requests. 54 | config.assets.quiet = true 55 | 56 | # Raises error for missing translations. 57 | # config.action_view.raise_on_missing_translations = true 58 | 59 | # Use an evented file watcher to asynchronously detect changes in source code, 60 | # routes, locales, etc. This feature depends on the listen gem. 61 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 62 | end 63 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 35 | # config.action_controller.asset_host = 'http://assets.example.com' 36 | 37 | # Specifies the header that your server uses for sending files. 38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 39 | config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 40 | 41 | # Store uploaded files on the local file system (see config/storage.yml for options). 42 | config.active_storage.service = :local 43 | 44 | # Mount Action Cable outside main process or domain. 45 | # config.action_cable.mount_path = nil 46 | # config.action_cable.url = 'wss://example.com/cable' 47 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 48 | 49 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 50 | # config.force_ssl = true 51 | 52 | # Use the lowest log level to ensure availability of diagnostic information 53 | # when problems arise. 54 | config.log_level = :debug 55 | 56 | # Prepend all log lines with the following tags. 57 | config.log_tags = [ :request_id ] 58 | 59 | # Use a different cache store in production. 60 | # config.cache_store = :mem_cache_store 61 | 62 | # Use a real queuing backend for Active Job (and separate queues per environment). 63 | # config.active_job.queue_adapter = :resque 64 | # config.active_job.queue_name_prefix = "spex_production" 65 | 66 | config.action_mailer.perform_caching = false 67 | 68 | # Ignore bad email addresses and do not raise email delivery errors. 69 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 70 | # config.action_mailer.raise_delivery_errors = false 71 | 72 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 73 | # the I18n.default_locale when a translation cannot be found). 74 | config.i18n.fallbacks = true 75 | 76 | # Send deprecation notices to registered listeners. 77 | config.active_support.deprecation = :notify 78 | 79 | # Use default logging formatter so that PID and timestamp are not suppressed. 80 | config.log_formatter = ::Logger::Formatter.new 81 | 82 | # Use a different logger for distributed setups. 83 | # require 'syslog/logger' 84 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 85 | 86 | if ENV["RAILS_LOG_TO_STDOUT"].present? 87 | logger = ActiveSupport::Logger.new(STDOUT) 88 | logger.formatter = config.log_formatter 89 | config.logger = ActiveSupport::TaggedLogging.new(logger) 90 | end 91 | 92 | # Do not dump schema after migrations. 93 | config.active_record.dump_schema_after_migration = false 94 | 95 | # Inserts middleware to perform automatic connection switching. 96 | # The `database_selector` hash is used to pass options to the DatabaseSelector 97 | # middleware. The `delay` is used to determine how long to wait after a write 98 | # to send a subsequent read to the primary. 99 | # 100 | # The `database_resolver` class is used by the middleware to determine which 101 | # database is appropriate to use based on the time delay. 102 | # 103 | # The `database_resolver_context` class is used by the middleware to set 104 | # timestamps for the last write to the primary. The resolver uses the context 105 | # class timestamps to determine how long to wait before reading from the 106 | # replica. 107 | # 108 | # By default Rails will store a last write timestamp in the session. The 109 | # DatabaseSelector middleware is designed as such you can define your own 110 | # strategy for connection switching and pass that into the middleware through 111 | # these configuration options. 112 | # config.active_record.database_selector = { delay: 2.seconds } 113 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver 114 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session 115 | end 116 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static file server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Randomize the order test cases are executed. 35 | config.active_support.test_order = :random 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :marshal 4 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | Mime::Type.register "application/msword", :doc 6 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | SpexV1::Application.config.secret_token = '40092cf51b7eccb833005fea2f440cc0c9bba928abe9ffa224aa6f2e1d489efe304afda1097b3063a8df5092eb6a80bf8f29c19fc6f6da4d24686d9de1f4e1e4' 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_spex_v1_session' 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # The priority is based upon order of creation: first created -> highest priority. 3 | # See how all your routes lay out with "rake routes". 4 | 5 | root 'specs#specs' 6 | get 'specs(/:serie(/:spec(/:version(.:format))))', :to => "specs#specs", :as => 'specs_res' 7 | 8 | # You can have the root of your site routed with "root" 9 | # root 'welcome#index' 10 | 11 | # Example of regular route: 12 | # get 'products/:id' => 'catalog#view' 13 | 14 | # Example of named route that can be invoked with purchase_url(id: product.id) 15 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase 16 | 17 | # Example resource route (maps HTTP verbs to controller actions automatically): 18 | # resources :products 19 | 20 | # Example resource route with options: 21 | # resources :products do 22 | # member do 23 | # get 'short' 24 | # post 'toggle' 25 | # end 26 | # 27 | # collection do 28 | # get 'sold' 29 | # end 30 | # end 31 | 32 | # Example resource route with sub-resources: 33 | # resources :products do 34 | # resources :comments, :sales 35 | # resource :seller 36 | # end 37 | 38 | # Example resource route with more complex sub-resources: 39 | # resources :products do 40 | # resources :comments 41 | # resources :sales do 42 | # get 'recent', on: :collection 43 | # end 44 | # end 45 | 46 | # Example resource route with concerns: 47 | # concern :toggleable do 48 | # post 'toggle' 49 | # end 50 | # resources :posts, concerns: :toggleable 51 | # resources :photos, concerns: :toggleable 52 | 53 | # Example resource route within a namespace: 54 | # namespace :admin do 55 | # # Directs /admin/products/* to Admin::ProductsController 56 | # # (app/controllers/admin/products_controller.rb) 57 | # resources :products 58 | # end 59 | end 60 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /config/webpacker.yml: -------------------------------------------------------------------------------- 1 | # Note: You must restart bin/webpack-dev-server for changes to take effect 2 | 3 | default: &default 4 | source_path: app/javascript 5 | source_entry_path: packs 6 | public_root_path: public 7 | public_output_path: packs 8 | cache_path: tmp/cache/webpacker 9 | check_yarn_integrity: false 10 | webpack_compile_output: true 11 | 12 | # Additional paths webpack should lookup modules 13 | # ['app/assets', 'engine/foo/app/assets'] 14 | resolved_paths: [] 15 | 16 | # Reload manifest.json on all requests so we reload latest compiled packs 17 | cache_manifest: false 18 | 19 | # Extract and emit a css file 20 | extract_css: false 21 | 22 | static_assets_extensions: 23 | - .jpg 24 | - .jpeg 25 | - .png 26 | - .gif 27 | - .tiff 28 | - .ico 29 | - .svg 30 | - .eot 31 | - .otf 32 | - .ttf 33 | - .woff 34 | - .woff2 35 | 36 | extensions: 37 | - .mjs 38 | - .js 39 | - .sass 40 | - .scss 41 | - .css 42 | - .module.sass 43 | - .module.scss 44 | - .module.css 45 | - .png 46 | - .svg 47 | - .gif 48 | - .jpeg 49 | - .jpg 50 | 51 | development: 52 | <<: *default 53 | compile: true 54 | 55 | # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules 56 | check_yarn_integrity: true 57 | 58 | # Reference: https://webpack.js.org/configuration/dev-server/ 59 | dev_server: 60 | https: false 61 | host: localhost 62 | port: 3035 63 | public: localhost:3035 64 | hmr: false 65 | # Inline should be set to true if using HMR 66 | inline: true 67 | overlay: true 68 | compress: true 69 | disable_host_check: true 70 | use_local_ip: false 71 | quiet: false 72 | pretty: false 73 | headers: 74 | 'Access-Control-Allow-Origin': '*' 75 | watch_options: 76 | ignored: '**/node_modules/**' 77 | 78 | 79 | test: 80 | <<: *default 81 | compile: true 82 | 83 | # Compile test packs to a separate directory 84 | public_output_path: packs-test 85 | 86 | production: 87 | <<: *default 88 | 89 | # Production depends on precompilation of packs prior to booting for performance. 90 | compile: false 91 | 92 | # Extract and emit a css file 93 | extract_css: true 94 | 95 | # Cache manifest.json for performance 96 | cache_manifest: true 97 | -------------------------------------------------------------------------------- /db/migrate/20121102122425_create_documents.rb: -------------------------------------------------------------------------------- 1 | class CreateDocuments < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :documents do |t| 4 | t.string :name 5 | t.string :title 6 | t.integer :spec_serie_id 7 | t.integer :spec_number 8 | t.integer :spec_part 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20121102122714_create_releases.rb: -------------------------------------------------------------------------------- 1 | class CreateReleases < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :releases do |t| 4 | t.string :name 5 | t.integer :version 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20121102122758_create_document_versions.rb: -------------------------------------------------------------------------------- 1 | class CreateDocumentVersions < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :document_versions do |t| 4 | t.integer :major 5 | t.integer :technical 6 | t.integer :editorial 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20121102122817_create_document_files.rb: -------------------------------------------------------------------------------- 1 | class CreateDocumentFiles < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :document_files do |t| 4 | 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20121102123056_add_relationship.rb: -------------------------------------------------------------------------------- 1 | class AddRelationship < ActiveRecord::Migration[6.0] 2 | def change 3 | # belongs_to :document 4 | add_column :document_versions, :document_id, :integer 5 | add_index :document_versions, :document_id 6 | 7 | # belongs_to :release 8 | add_column :document_versions, :release_id, :integer 9 | add_index :document_versions, :release_id 10 | 11 | # belongs_to :document_version 12 | add_column :document_files, :document_version_id, :integer 13 | add_index :document_files, :document_version_id 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20121102124156_create_spec_series.rb: -------------------------------------------------------------------------------- 1 | class CreateSpecSeries < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :spec_series do |t| 4 | t.integer :index 5 | t.integer :spec_scope_id 6 | t.string :subject 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20121102124344_create_spec_scopes.rb: -------------------------------------------------------------------------------- 1 | class CreateSpecScopes < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :spec_scopes do |t| 4 | t.string :scope 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20121105120333_add_stuff_to_document_files.rb: -------------------------------------------------------------------------------- 1 | class AddStuffToDocumentFiles < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :document_files, :format, :string 4 | add_column :document_files, :sha1, :string 5 | add_column :document_files, :size, :integer 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20121106133252_add_stuff_to_document_file.rb: -------------------------------------------------------------------------------- 1 | class AddStuffToDocumentFile < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :document_files, :nb_pages, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121106133427_create_document_tocs.rb: -------------------------------------------------------------------------------- 1 | class CreateDocumentTocs < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :document_tocs do |t| 4 | t.integer :parent_id 5 | t.integer :document_file_id 6 | t.integer :level 7 | t.string :title 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20121106135728_add_page_to_document_toc.rb: -------------------------------------------------------------------------------- 1 | class AddPageToDocumentToc < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :document_tocs, :page, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # This file is the source Rails uses to define your schema when running `rails 6 | # db:schema:load`. When creating a new database, `rails db:schema:load` tends to 7 | # be faster and is potentially less error prone than running all of your 8 | # migrations from scratch. Old migrations may fail to apply correctly if those 9 | # migrations use external dependencies or application code. 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 2012_11_06_135728) do 14 | 15 | create_table "document_files", force: :cascade do |t| 16 | t.datetime "created_at", precision: 6, null: false 17 | t.datetime "updated_at", precision: 6, null: false 18 | t.integer "document_version_id" 19 | t.string "format" 20 | t.string "sha1" 21 | t.integer "size" 22 | t.integer "nb_pages" 23 | t.index ["document_version_id"], name: "index_document_files_on_document_version_id" 24 | end 25 | 26 | create_table "document_tocs", force: :cascade do |t| 27 | t.integer "parent_id" 28 | t.integer "document_file_id" 29 | t.integer "level" 30 | t.string "title" 31 | t.datetime "created_at", precision: 6, null: false 32 | t.datetime "updated_at", precision: 6, null: false 33 | t.integer "page" 34 | end 35 | 36 | create_table "document_versions", force: :cascade do |t| 37 | t.integer "major" 38 | t.integer "technical" 39 | t.integer "editorial" 40 | t.datetime "created_at", precision: 6, null: false 41 | t.datetime "updated_at", precision: 6, null: false 42 | t.integer "document_id" 43 | t.integer "release_id" 44 | t.index ["document_id"], name: "index_document_versions_on_document_id" 45 | t.index ["release_id"], name: "index_document_versions_on_release_id" 46 | end 47 | 48 | create_table "documents", force: :cascade do |t| 49 | t.string "name" 50 | t.string "title" 51 | t.integer "spec_serie_id" 52 | t.integer "spec_number" 53 | t.integer "spec_part" 54 | t.datetime "created_at", precision: 6, null: false 55 | t.datetime "updated_at", precision: 6, null: false 56 | end 57 | 58 | create_table "releases", force: :cascade do |t| 59 | t.string "name" 60 | t.integer "version" 61 | t.datetime "created_at", precision: 6, null: false 62 | t.datetime "updated_at", precision: 6, null: false 63 | end 64 | 65 | create_table "spec_scopes", force: :cascade do |t| 66 | t.string "scope" 67 | t.datetime "created_at", precision: 6, null: false 68 | t.datetime "updated_at", precision: 6, null: false 69 | end 70 | 71 | create_table "spec_series", force: :cascade do |t| 72 | t.integer "index" 73 | t.integer "spec_scope_id" 74 | t.string "subject" 75 | t.datetime "created_at", precision: 6, null: false 76 | t.datetime "updated_at", precision: 6, null: false 77 | end 78 | 79 | end 80 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | 9 | SpecScope.find_or_create_by(scope: "GSM only (< Rel-4)") 10 | SpecScope.find_or_create_by(scope: "GSM only (>= Rel-4)") 11 | SpecScope.find_or_create_by(scope: "3G and beyond / GSM (R99 and later)") 12 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | 3 | volumes: 4 | db-volume: 5 | spex-volume: 6 | 7 | networks: 8 | backend: 9 | 10 | services: 11 | db: 12 | image: mysql:latest 13 | restart: always 14 | environment: 15 | MYSQL_DATABASE: 'spex' 16 | MYSQL_USER: 'spex' 17 | MYSQL_PASSWORD: 'spex' 18 | MYSQL_ROOT_PASSWORD: 'spex' 19 | networks: 20 | - backend 21 | volumes: 22 | - db-volume:/var/lib/mysql 23 | spex: 24 | #image: corfr/spex 25 | build: ./ 26 | restart: always 27 | environment: 28 | MYSQL_PORT_3306_TCP_ADDR: "db" 29 | MYSQL_ENV_DB_PASS: 'spex' 30 | MYSQL_ENV_DB_USER: 'spex' 31 | MYSQL_ENV_DB_NAME: 'spex' 32 | networks: 33 | - backend 34 | volumes: 35 | - spex-volume:/home/app/webapp/specs 36 | ports: 37 | - "3000:80" 38 | 39 | 40 | -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker build -t corfr/spex . 3 | -------------------------------------------------------------------------------- /docker/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/dev.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: 16 | adapter: sqlite3 17 | database: db/test.sqlite3 18 | pool: 5 19 | timeout: 5000 20 | 21 | production: 22 | adapter: mysql2 23 | encoding: utf8 24 | database: <%= ENV['MYSQL_ENV_DB_NAME'] %> 25 | username: <%= ENV['MYSQL_ENV_DB_USER'] %> 26 | password: <%= ENV['MYSQL_ENV_DB_PASS'] %> 27 | host: <%= ENV['MYSQL_PORT_3306_TCP_ADDR'] || "mysql" %> 28 | port: <%= ENV['MYSQL_PORT_3306_TCP_PORT'] || "3306" %> 29 | pool: 5 30 | 31 | -------------------------------------------------------------------------------- /docker/dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | 5 | if docker ps | grep spex-app; then 6 | docker kill spex-app 7 | docker rm spex-app 8 | fi 9 | 10 | if ! docker ps -a | grep spex-mysql; then 11 | docker run \ 12 | --name spex-mysql \ 13 | -e DB_USER='spex'\ 14 | -e DB_PASS='spex' \ 15 | -e DB_NAME='spex' \ 16 | -d \ 17 | sameersbn/mysql:latest 18 | 19 | sleep 5 20 | fi 21 | 22 | TI='' 23 | if [ -n "$1" ]; then 24 | TI="-ti" 25 | fi 26 | 27 | if docker ps -a | grep spex-app; then 28 | docker rm spex-app 29 | fi 30 | 31 | docker run \ 32 | --name spex-app \ 33 | --rm \ 34 | -v /storage/web/specs:/home/app/webapp/specs \ 35 | -v $DIR/database.yml:/home/app/webapp/config/database.yml \ 36 | -v $DIR/webapp.sh:/etc/my_init.d/webapp.sh \ 37 | --link spex-mysql:mysql \ 38 | -p 127.0.0.1:3000:80 \ 39 | $TI \ 40 | corfr/spex $* 41 | 42 | -------------------------------------------------------------------------------- /docker/spex.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=speX WebService 3 | After=docker.service 4 | Requires=docker.service 5 | 6 | [Service] 7 | TimeoutStartSec=0 8 | ExecStartPre=-/usr/bin/docker kill spex 9 | ExecStartPre=-/usr/bin/docker rm spex 10 | ExecStartPre=/usr/bin/docker pull corfr/spex 11 | ExecStart=/usr/bin/docker run \ 12 | --name spex \ 13 | --link mysql \ 14 | -e MYSQL_ENV_DB_NAME=${MYSQL_ENV_DB_NAME} \ 15 | -e MYSQL_ENV_DB_USER=${MYSQL_ENV_DB_USER} \ 16 | -e MYSQL_ENV_DB_PASS=${MYSQL_ENV_DB_PASS} \ 17 | -v /storage/web/specs:/home/app/webapp/specs \ 18 | -p 80:80 \ 19 | corfr/spex 20 | ExecStop=/usr/bin/docker kill spex 21 | -------------------------------------------------------------------------------- /docker/sync_job.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | 3 | if [ $(id -u) -eq 0 ]; then 4 | # Rexecute as app 5 | su app -c "$0" 6 | exit $? 7 | fi 8 | 9 | cd /home/app/webapp 10 | bundle exec script/init_3gpp.rb 11 | -------------------------------------------------------------------------------- /docker/webapp.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | root /home/app/webapp/public; 4 | 5 | passenger_enabled on; 6 | passenger_user app; 7 | 8 | passenger_ruby /usr/bin/ruby2.6; 9 | 10 | proxy_set_header X-Sendfile-Type X-Accel-Redirect; 11 | passenger_env_var HTTP_X_ACCEL_MAPPING /home/app/webapp/specs/=/__send_file_accel/; 12 | passenger_pass_header X-Accel-Redirect; 13 | 14 | location /__send_file_accel { 15 | internal; 16 | alias /home/app/webapp/specs; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docker/webapp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Provide db env vars to passenger 4 | for var in $(env |grep MYSQL | tr '=' ' ' |awk '{print $1}'); do 5 | if [ -n "${!var}" ]; then 6 | sed -i '$i'"passenger_env_var $var ${!var};" /etc/nginx/sites-enabled/webapp.conf 7 | fi 8 | done 9 | 10 | su --preserve-environment -c /bin/bash - app -c "( export RAILS_ENV=production && cd /home/app/webapp && bundle exec rake db:migrate )" 11 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/lib/assets/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/log/.gitkeep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /public/assets/application-8ca4e69f02a06c2ac2d7c67f67d7c918.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/application-8ca4e69f02a06c2ac2d7c67f67d7c918.js.gz -------------------------------------------------------------------------------- /public/assets/application-95526ebe1d6994de8fbc114f40ef662f.css: -------------------------------------------------------------------------------- 1 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0}form fieldset{margin:0 0 10px 0;padding:0 0 10px 0;font-size:14px;border:0;border-bottom:1px solid #eee}form fieldset a{font-size:12px;border-bottom:1px dashed gray;color:gray}form fieldset a:hover{color:#436b95;border-color:#436b95}form fieldset.form-actions{margin:0 0 0 20%;padding:0;border:none}form fieldset.check{padding-left:20%}form fieldset label{float:left;width:20%;margin:4px 0 5px 0;font-size:12px}form fieldset table label{float:none;width:auto;margin:0 0 0 10px;vertical-align:middle}form fieldset table input{vertical-align:middle;position:relative;top:-1px;*overflow:hidden}form fieldset table td{vertical-align:middle;line-height:15px}form fieldset.check label{display:inline;float:none;width:auto;font-weight:normal}form fieldset.radio ul li label{display:inline;float:none;width:auto;font-weight:normal}form input.form-text,form textarea{display:block;font-size:12px;border:1px solid #ddd;background:#f5f5f5;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.05);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}form fieldset input.form-text,form fieldset textarea{width:50%;padding:5px}form fieldset input.form-text:focus{border:1px solid #ccc;background:#fff}form fieldset textarea{height:150px}form fieldset select{min-width:25%;margin:0;font-size:12px}form fieldset.radio ul{margin:5px 0 0 20%}form fieldset.radio ul li{margin:0 0 5px 0}form fieldset.radio ul li:last-child{margin:0}form fieldset p.form-help{margin:5px 0 0 20%;font-size:12px;color:#999}form input[type="submit"]{margin:0;padding:5px 10px;font-size:12px;font-weight:bold;border:1px solid #ccc;background:#eee;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}form input[type="submit"]:hover,form input[type="submit"]:focus{border:1px solid #bbb;background:#e5e5e5}form input[type="submit"]:active{border:1px solid #ccc;background:#eee}@media screen and (max-width: 600px){form fieldset label{display:block;float:none;width:auto;margin:0 0 5px 0}form fieldset.form-actions,form fieldset.check,form fieldset.radio ul,form fieldset p.form-help{margin-left:0;padding-left:0}form fieldset input.form-text,form fieldset textarea{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}}body{margin:0;padding:0;background:#ededed url(/assets/sidebar-a103568d82f55430682674b37127782a.png) repeat-y left;font:75%/1.5 sans-serif}a{color:#789;text-decoration:none}a:hover{color:#0060a3}a:visited{opacity:0.8;filter:alpha(opacity=80)}a img{border-width:0px}#header{width:100%;height:30px;background:url(/assets/header-bb0c527c9f3db33fa76b20981e7f6b78.png);line-height:14px;position:fixed;top:0px;z-index:999}#header img.logo{float:left}#header span.main-title a{color:#fcfcfc;font-weight:bold;font-size:12px;margin-left:10px;position:relative;top:2px}#header span.sub-title{color:#ccc;font-weight:bold;font-size:10px;margin-left:10px}#nav{width:100%;height:24px;background:url(/assets/nav-9ec7b7a925670805ad24430579fb17cd.png) repeat-x;-webkit-box-shadow:0px 3px 7px 0px rgba(0,0,0,0.5);box-shadow:0px 3px 7px 0px rgba(0,0,0,0.5);color:#fff;position:fixed;top:30px;opacity:0.8;z-index:1000}#nav span.left-arrow{display:block;background:#282828 url(/assets/left-arrow-38ca3d012e51a5e30efda45dbe5becbe.png) no-repeat right;min-width:60px;float:left;height:24px;line-height:25px;text-indent:10px;padding-right:10px;color:#eee}#nav span.left-arrow span.v_v{font-size:80%}#nav span.left-arrow span.v_release{color:#fff;font-weight:bold;font-size:110%}#sidebar{width:35px;position:fixed;top:55px;left:0px;z-index:1001}#sidebar div.icon{width:16px;margin:7px auto}#sidebar div.icon a{width:16px;text-align:center}#sidebar div.icon a img{opacity:0.7}#sidebar div.icon a:hover img{opacity:1}#content{margin-top:60px}h1,h2,h3,h4,h5{font-family:Arial,sans-serif}table.data-table th,table.data-table td{padding:5px 15px;text-align:left;border-bottom:1px solid #ccc}table.data-table th{border-width:2px}table.data-table td{color:#666}table.data-table tr{border-left:2px solid transparent;border-right:1px solid transparent}table.data-table tr.block-part,table.data-table tr.block-end{border-left:2px solid #888;border-right:1px solid #888;background:#fcfcfc}table.data-table tr.block-end td{border-bottom-color:#888}table.data-table tr:last-child th,table.data-table tr:last-child td{border-bottom:none}table.data-table tr:nth-child(even){background:#eee}table.data-table tr:hover{background:#f2f2f2}table.data-table td.link-column{width:50px;text-align:center}table.data-table tr.sub-header td{font-weight:bold;padding:5px 5px;border-bottom-color:#888;background:#fff}nav.breadcrumb{font-size:12px;color:#ccc;line-height:24px;position:relative;left:10px}nav.breadcrumb a{margin:0 5px;text-decoration:none;color:#ccc}nav.breadcrumb a:first-child{margin-left:0}nav.breadcrumb a:hover,nav.breadcrumb a:focus{color:#fff}nav.breadcrumb a:active{color:#666}nav.breadcrumb strong{margin-left:5px;font-weight:bold;color:#ddd}.data-block{width:800px;margin:10px auto;margin-top:80px}.block{background:#fff;border:1px solid #777;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:3px 3px 6px 0px rgba(0,0,0,0.4);box-shadow:3px 3px 6px 0px rgba(0,0,0,0.4)}.block h4{padding:5px;font-weight:bold;margin:0 10px;border-bottom:1px solid #aaa}.block h5{padding:5px;margin:0 10px}.block table.data-table{width:780px;margin:10px auto}li.block{margin:10px 0}li.collapsed h4{border-bottom:0}table.data-table td.icon-link{width:20px;text-align:center;padding-left:5px;padding-right:5px}#nav form input.form-text{background:#fff;display:inline-block;padding-left:10px;margin-left:5px}#page_jump{width:20px;text-align:right} 2 | -------------------------------------------------------------------------------- /public/assets/application-95526ebe1d6994de8fbc114f40ef662f.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/application-95526ebe1d6994de8fbc114f40ef662f.css.gz -------------------------------------------------------------------------------- /public/assets/application.css: -------------------------------------------------------------------------------- 1 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0}form fieldset{margin:0 0 10px 0;padding:0 0 10px 0;font-size:14px;border:0;border-bottom:1px solid #eee}form fieldset a{font-size:12px;border-bottom:1px dashed gray;color:gray}form fieldset a:hover{color:#436b95;border-color:#436b95}form fieldset.form-actions{margin:0 0 0 20%;padding:0;border:none}form fieldset.check{padding-left:20%}form fieldset label{float:left;width:20%;margin:4px 0 5px 0;font-size:12px}form fieldset table label{float:none;width:auto;margin:0 0 0 10px;vertical-align:middle}form fieldset table input{vertical-align:middle;position:relative;top:-1px;*overflow:hidden}form fieldset table td{vertical-align:middle;line-height:15px}form fieldset.check label{display:inline;float:none;width:auto;font-weight:normal}form fieldset.radio ul li label{display:inline;float:none;width:auto;font-weight:normal}form input.form-text,form textarea{display:block;font-size:12px;border:1px solid #ddd;background:#f5f5f5;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.05);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}form fieldset input.form-text,form fieldset textarea{width:50%;padding:5px}form fieldset input.form-text:focus{border:1px solid #ccc;background:#fff}form fieldset textarea{height:150px}form fieldset select{min-width:25%;margin:0;font-size:12px}form fieldset.radio ul{margin:5px 0 0 20%}form fieldset.radio ul li{margin:0 0 5px 0}form fieldset.radio ul li:last-child{margin:0}form fieldset p.form-help{margin:5px 0 0 20%;font-size:12px;color:#999}form input[type="submit"]{margin:0;padding:5px 10px;font-size:12px;font-weight:bold;border:1px solid #ccc;background:#eee;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}form input[type="submit"]:hover,form input[type="submit"]:focus{border:1px solid #bbb;background:#e5e5e5}form input[type="submit"]:active{border:1px solid #ccc;background:#eee}@media screen and (max-width: 600px){form fieldset label{display:block;float:none;width:auto;margin:0 0 5px 0}form fieldset.form-actions,form fieldset.check,form fieldset.radio ul,form fieldset p.form-help{margin-left:0;padding-left:0}form fieldset input.form-text,form fieldset textarea{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}}body{margin:0;padding:0;background:#ededed url(/assets/sidebar.png) repeat-y left;font:75%/1.5 sans-serif}a{color:#789;text-decoration:none}a:hover{color:#0060a3}a:visited{opacity:0.8;filter:alpha(opacity=80)}a img{border-width:0px}#header{width:100%;height:30px;background:url(/assets/header.png);line-height:14px;position:fixed;top:0px;z-index:999}#header img.logo{float:left}#header span.main-title a{color:#fcfcfc;font-weight:bold;font-size:12px;margin-left:10px;position:relative;top:2px}#header span.sub-title{color:#ccc;font-weight:bold;font-size:10px;margin-left:10px}#nav{width:100%;height:24px;background:url(/assets/nav.png) repeat-x;-webkit-box-shadow:0px 3px 7px 0px rgba(0,0,0,0.5);box-shadow:0px 3px 7px 0px rgba(0,0,0,0.5);color:#fff;position:fixed;top:30px;opacity:0.8;z-index:1000}#nav span.left-arrow{display:block;background:#282828 url(/assets/left-arrow.png) no-repeat right;min-width:60px;float:left;height:24px;line-height:25px;text-indent:10px;padding-right:10px;color:#eee}#nav span.left-arrow span.v_v{font-size:80%}#nav span.left-arrow span.v_release{color:#fff;font-weight:bold;font-size:110%}#sidebar{width:35px;position:fixed;top:55px;left:0px;z-index:1001}#sidebar div.icon{width:16px;margin:7px auto}#sidebar div.icon a{width:16px;text-align:center}#sidebar div.icon a img{opacity:0.7}#sidebar div.icon a:hover img{opacity:1}#content{margin-top:60px}h1,h2,h3,h4,h5{font-family:Arial,sans-serif}table.data-table th,table.data-table td{padding:5px 15px;text-align:left;border-bottom:1px solid #ccc}table.data-table th{border-width:2px}table.data-table td{color:#666}table.data-table tr{border-left:2px solid transparent;border-right:1px solid transparent}table.data-table tr.block-part,table.data-table tr.block-end{border-left:2px solid #888;border-right:1px solid #888;background:#fcfcfc}table.data-table tr.block-end td{border-bottom-color:#888}table.data-table tr:last-child th,table.data-table tr:last-child td{border-bottom:none}table.data-table tr:nth-child(even){background:#eee}table.data-table tr:hover{background:#f2f2f2}table.data-table td.link-column{width:50px;text-align:center}table.data-table tr.sub-header td{font-weight:bold;padding:5px 5px;border-bottom-color:#888;background:#fff}nav.breadcrumb{font-size:12px;color:#ccc;line-height:24px;position:relative;left:10px}nav.breadcrumb a{margin:0 5px;text-decoration:none;color:#ccc}nav.breadcrumb a:first-child{margin-left:0}nav.breadcrumb a:hover,nav.breadcrumb a:focus{color:#fff}nav.breadcrumb a:active{color:#666}nav.breadcrumb strong{margin-left:5px;font-weight:bold;color:#ddd}.data-block{width:800px;margin:10px auto;margin-top:80px}.block{background:#fff;border:1px solid #777;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:3px 3px 6px 0px rgba(0,0,0,0.4);box-shadow:3px 3px 6px 0px rgba(0,0,0,0.4)}.block h4{padding:5px;font-weight:bold;margin:0 10px;border-bottom:1px solid #aaa}.block h5{padding:5px;margin:0 10px}.block table.data-table{width:780px;margin:10px auto}li.block{margin:10px 0}li.collapsed h4{border-bottom:0}table.data-table td.icon-link{width:20px;text-align:center;padding-left:5px;padding-right:5px}#nav form input.form-text{background:#fff;display:inline-block;padding-left:10px;margin-left:5px}#page_jump{width:20px;text-align:right} 2 | -------------------------------------------------------------------------------- /public/assets/application.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/application.css.gz -------------------------------------------------------------------------------- /public/assets/application.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/application.js.gz -------------------------------------------------------------------------------- /public/assets/header-bb0c527c9f3db33fa76b20981e7f6b78.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/header-bb0c527c9f3db33fa76b20981e7f6b78.png -------------------------------------------------------------------------------- /public/assets/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/header.png -------------------------------------------------------------------------------- /public/assets/icon-doc-bf4dd12d8d9b07a16636a030a5932ce9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-doc-bf4dd12d8d9b07a16636a030a5932ce9.png -------------------------------------------------------------------------------- /public/assets/icon-doc-gray-7651cf7358c8bfe59785db63f0b33aef.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-doc-gray-7651cf7358c8bfe59785db63f0b33aef.png -------------------------------------------------------------------------------- /public/assets/icon-doc-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-doc-gray.png -------------------------------------------------------------------------------- /public/assets/icon-doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-doc.png -------------------------------------------------------------------------------- /public/assets/icon-html-a5de3a543de6e0115ba65eb80bfb6419.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-html-a5de3a543de6e0115ba65eb80bfb6419.png -------------------------------------------------------------------------------- /public/assets/icon-html-gray-875aec4e770e3e0ed99f31a5e40c2eb0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-html-gray-875aec4e770e3e0ed99f31a5e40c2eb0.png -------------------------------------------------------------------------------- /public/assets/icon-html-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-html-gray.png -------------------------------------------------------------------------------- /public/assets/icon-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-html.png -------------------------------------------------------------------------------- /public/assets/icon-pdf-b6b758527b1dd18cc42b74fe279d7fdb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-pdf-b6b758527b1dd18cc42b74fe279d7fdb.png -------------------------------------------------------------------------------- /public/assets/icon-pdf-gray-b8060cfae02696ae90a82e5cc16aec86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-pdf-gray-b8060cfae02696ae90a82e5cc16aec86.png -------------------------------------------------------------------------------- /public/assets/icon-pdf-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-pdf-gray.png -------------------------------------------------------------------------------- /public/assets/icon-pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/icon-pdf.png -------------------------------------------------------------------------------- /public/assets/left-arrow-38ca3d012e51a5e30efda45dbe5becbe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/left-arrow-38ca3d012e51a5e30efda45dbe5becbe.png -------------------------------------------------------------------------------- /public/assets/left-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/left-arrow.png -------------------------------------------------------------------------------- /public/assets/logo-3fec1e5b2f64dfffc913e20f2bba3e48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/logo-3fec1e5b2f64dfffc913e20f2bba3e48.png -------------------------------------------------------------------------------- /public/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/logo.png -------------------------------------------------------------------------------- /public/assets/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | header.png: header-bb0c527c9f3db33fa76b20981e7f6b78.png 3 | icon-doc-gray.png: icon-doc-gray-7651cf7358c8bfe59785db63f0b33aef.png 4 | icon-doc.png: icon-doc-bf4dd12d8d9b07a16636a030a5932ce9.png 5 | icon-html-gray.png: icon-html-gray-875aec4e770e3e0ed99f31a5e40c2eb0.png 6 | icon-html.png: icon-html-a5de3a543de6e0115ba65eb80bfb6419.png 7 | icon-pdf-gray.png: icon-pdf-gray-b8060cfae02696ae90a82e5cc16aec86.png 8 | icon-pdf.png: icon-pdf-b6b758527b1dd18cc42b74fe279d7fdb.png 9 | left-arrow.png: left-arrow-38ca3d012e51a5e30efda45dbe5becbe.png 10 | logo.png: logo-3fec1e5b2f64dfffc913e20f2bba3e48.png 11 | nav.png: nav-9ec7b7a925670805ad24430579fb17cd.png 12 | sidebar.png: sidebar-a103568d82f55430682674b37127782a.png 13 | application.js: application-8ca4e69f02a06c2ac2d7c67f67d7c918.js 14 | application.css: application-95526ebe1d6994de8fbc114f40ef662f.css 15 | -------------------------------------------------------------------------------- /public/assets/nav-9ec7b7a925670805ad24430579fb17cd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/nav-9ec7b7a925670805ad24430579fb17cd.png -------------------------------------------------------------------------------- /public/assets/nav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/nav.png -------------------------------------------------------------------------------- /public/assets/sidebar-a103568d82f55430682674b37127782a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/sidebar-a103568d82f55430682674b37127782a.png -------------------------------------------------------------------------------- /public/assets/sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/assets/sidebar.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /script/analyze_pdf.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'open-uri' 4 | require 'colored' 5 | require 'ap' 6 | require 'net/http' 7 | require 'uri' 8 | require 'nokogiri' 9 | require 'poppler' 10 | 11 | def print_chrono(t_a, t_b) 12 | puts "... took %0.2f sec".yellow % (t_b - t_a) 13 | end 14 | 15 | def load_rails 16 | puts "Loading Rails".cyan 17 | 18 | a = Time.now 19 | ENV['RAILS_ENV'] = ENV['RAILS_ENV'] || 'development' 20 | require File.expand_path(File.dirname(__FILE__) + "/../config/environment") 21 | b = Time.now 22 | 23 | print_chrono a, b 24 | end 25 | 26 | def walk_index(indexer, depth = 0) 27 | indexer.each do |i| 28 | 29 | ap i.action.dest.page_num 30 | ap i.action.dest.top 31 | child = i.child 32 | 33 | walk_index(child, depth + 1) if child.nil? == false and depth < 1 34 | end 35 | end 36 | 37 | def analyze_pdf(path) 38 | doc = Poppler::Document.new(path) 39 | indexer = Poppler::IndexIter.new(doc) 40 | 41 | pages = doc.n_pages 42 | puts "This is the number of pages #{pages}".cyan 43 | 44 | walk_index(indexer) 45 | end 46 | 47 | # Script 48 | if __FILE__ == $0 49 | 50 | puts 'PDF Analyzer'.green 51 | 52 | if ARGV.length == 0 53 | puts "Path required".red 54 | exit -1 55 | end 56 | 57 | load_rails() 58 | 59 | a = Time.now 60 | 61 | analyze_pdf(ARGV[0]) 62 | 63 | b = Time.now 64 | 65 | print_chrono a, b 66 | 67 | puts 68 | end 69 | -------------------------------------------------------------------------------- /script/fix_pdf.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'open-uri' 4 | require 'colored' 5 | require 'ap' 6 | require 'net/http' 7 | require 'uri' 8 | require 'nokogiri' 9 | 10 | def print_chrono(t_a, t_b) 11 | puts "... took %0.2f sec".yellow % (t_b - t_a) 12 | end 13 | 14 | def load_rails 15 | puts "Loading Rails".cyan 16 | 17 | a = Time.now 18 | ENV['RAILS_ENV'] = ENV['RAILS_ENV'] || 'development' 19 | require File.expand_path(File.dirname(__FILE__) + "/../config/environment") 20 | b = Time.now 21 | 22 | print_chrono a, b 23 | end 24 | 25 | # Script 26 | if __FILE__ == $0 27 | 28 | puts 'Fix PDFs'.green 29 | 30 | load_rails() 31 | 32 | a = Time.now 33 | 34 | DocumentFile.includes({:document_version => :document }).where( { :format => :pdf, :nb_pages => nil } ).each do |df| 35 | print "#{df.local_path} => " 36 | res = df.analyze_pdf 37 | puts (res) ? res.to_s.green : res.to_s.red 38 | end 39 | 40 | b = Time.now 41 | 42 | print_chrono a, b 43 | 44 | puts 45 | end 46 | -------------------------------------------------------------------------------- /script/init_3gpp.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "open-uri" 4 | require "colored" 5 | require "ap" 6 | require "net/http" 7 | require "uri" 8 | require "nokogiri" 9 | 10 | def print_chrono(t_a, t_b) 11 | puts "... took %0.2f sec".yellow % (t_b - t_a) 12 | end 13 | 14 | def load_rails 15 | puts "Loading Rails".cyan 16 | 17 | a = Time.now 18 | ENV["RAILS_ENV"] = ENV["RAILS_ENV"] || "development" 19 | require File.expand_path(File.dirname(__FILE__) + "/../config/environment") 20 | b = Time.now 21 | 22 | print_chrono a, b 23 | end 24 | 25 | def init_spec_numbering 26 | puts "Init ... spec numbering".cyan 27 | 28 | source_page = "https://www.3gpp.org/specifications/79-specification-numbering" 29 | source_path = "/html/body/div/div[2]/div[4]/div/div/table/tbody/tr" 30 | 31 | source_html = Nokogiri::HTML(open(source_page)) 32 | 33 | # scopes = SpecScope.scope_gsm_1 34 | # all = scopes[0] 35 | # scope_gsm_2 = scopes[1] 36 | # scope_3g = scopes[2] 37 | 38 | source_html.xpath(source_path).each do |elmt| 39 | title = elmt.elements[0].text 40 | puts title.cyan 41 | 42 | i = 0 43 | scopes = [3, 2, 1] 44 | 45 | elmt.elements[1..3].each do |serie| 46 | if not serie.text[/(\d+) series/].nil? 47 | num = serie.text[/(\d+) series/].to_i 48 | 49 | puts num.to_s.green 50 | 51 | SpecSerie.find_or_create_by({ index: num, spec_scope_id: scopes[i] }) do |spec| 52 | spec.subject = title 53 | end 54 | end 55 | 56 | i += 1 57 | end 58 | end 59 | end 60 | 61 | def process_spec(spec, cache_pdf = false) 62 | 63 | # For each Document 64 | doc = Document.find_or_initialize_by(name: spec[:no]) 65 | if doc.new_record? 66 | begin 67 | puts "\tCreating document #{spec[:no]}: '#{spec[:title]}'".yellow 68 | # If we need to make this 69 | doc.title = spec[:title] 70 | doc.parse_no 71 | 72 | doc.save! 73 | rescue e 74 | puts "\tUnable to create document for #{spec[:no]}: #{e}" 75 | end 76 | else 77 | puts "\tFound document #{spec[:no]} => #{doc.id}".cyan 78 | 79 | if doc.spec_serie_id.nil? 80 | doc.parse_no 81 | doc.save! 82 | end 83 | end 84 | 85 | spec[:versions].each do |version| 86 | if doc.document_versions.where(version[:hash]).count == 0 87 | puts "\t\tCreating version #{version[:hash]}".yellow 88 | doc_version = doc.document_versions.create(version[:hash]) 89 | doc_version.release = version[:release] 90 | doc_version.save! 91 | else 92 | puts "\t\tFound version #{version[:hash]}".cyan 93 | end 94 | 95 | # Auto cache PDF 96 | if cache_pdf 97 | puts doc.document_versions.where(version[:hash]).first.retrieve_format :pdf 98 | end 99 | end 100 | end 101 | 102 | def init_spec_matrix(cache_pdf = false) 103 | puts "Init ... spec matrix".cyan 104 | 105 | source_page = "http://www.3gpp.org/ftp/Specs/html-info/SpecReleaseMatrix.htm" 106 | # source_page = "SpecReleaseMatrix.htm" 107 | 108 | source_html = Nokogiri::HTML(open(source_page)) 109 | 110 | releases = [] 111 | 112 | # Header 113 | puts "Releases".cyan 114 | 115 | source_path = '//*[@id="a3dyntab"]/thead/tr' 116 | source_html.xpath(source_path).first.elements[3..-1].each do |elmt| 117 | puts elmt.text.red 118 | release = Release.find_or_create_by(name: elmt.text) 119 | releases.push(release) 120 | end 121 | 122 | specs = [] 123 | 124 | # Content 125 | source_path = '//*[@id="a3dyntab"]/tbody/tr' 126 | source_html.xpath(source_path).each do |elmt| 127 | if not elmt.xpath("td[2]").text[/withdrawn/] 128 | 129 | # Spec is not withdraw, inserting in db 130 | spec_no = elmt.elements[0].text.strip 131 | spec_title = elmt.elements[1].text.strip 132 | spec_wg = elmt.elements[2].text.strip 133 | 134 | if ARGV.length == 0 or spec_no.starts_with? ARGV[0] 135 | spec = { 136 | :no => spec_no, 137 | :title => spec_title, 138 | :wg => spec_wg, 139 | :versions => [], 140 | } 141 | 142 | # For each Version 143 | idx = 0 144 | elmt.elements[3..-1].each do |elmt| 145 | if (not elmt.text.empty?) and (elmt.text != "none") 146 | version_hash = DocumentVersion.parse_version(elmt.text) 147 | version_info = { 148 | :hash => version_hash, 149 | :release => releases[idx], 150 | } 151 | raise "Unknown rel #{idx}" if version_info[:release].nil? 152 | spec[:versions].push version_info 153 | end 154 | 155 | idx += 1 156 | end 157 | 158 | specs.push spec 159 | end 160 | else 161 | #puts elmt.xpath("td[1]").text.red 162 | end 163 | end 164 | 165 | # Analyze 166 | nb_threads = 1 167 | threads = (1..nb_threads).map do |i| 168 | Thread.new(i) do |i| 169 | idx = 0 170 | loop do 171 | spec_idx = (idx * nb_threads) + i 172 | break if spec_idx >= specs.length 173 | 174 | begin 175 | process_spec specs[spec_idx], cache_pdf 176 | rescue Exception => e 177 | puts "Error while parsing #{spec_idx}: #{e}" 178 | end 179 | 180 | idx += 1 181 | end 182 | end 183 | end 184 | 185 | threads.each { |t| t.join } 186 | end 187 | 188 | # Script 189 | if __FILE__ == $0 190 | puts "Init 3GPP".green 191 | 192 | load_rails() 193 | 194 | a = Time.now 195 | 196 | init_spec_numbering() if ARGV.length == 0 197 | 198 | init_spec_matrix() 199 | 200 | b = Time.now 201 | 202 | print_chrono a, b 203 | 204 | puts 205 | end 206 | -------------------------------------------------------------------------------- /specs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/specs/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/test/fixtures/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/document_files.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: {} 8 | # column: value 9 | # 10 | two: {} 11 | # column: value 12 | -------------------------------------------------------------------------------- /test/fixtures/document_series.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | index: 1 5 | spec_scope_id: 1 6 | subject: MyString 7 | 8 | two: 9 | index: 1 10 | spec_scope_id: 1 11 | subject: MyString 12 | -------------------------------------------------------------------------------- /test/fixtures/document_tocs.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | title: MyString 5 | parent: 1 6 | level: 1 7 | document_file_id: 1 8 | 9 | two: 10 | title: MyString 11 | parent: 1 12 | level: 1 13 | document_file_id: 1 14 | -------------------------------------------------------------------------------- /test/fixtures/document_versions.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | major: 1 5 | technical: 1 6 | editorial: 1 7 | 8 | two: 9 | major: 1 10 | technical: 1 11 | editorial: 1 12 | -------------------------------------------------------------------------------- /test/fixtures/documents.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | name: MyString 5 | 6 | two: 7 | name: MyString 8 | -------------------------------------------------------------------------------- /test/fixtures/releases.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | name: MyString 5 | version: 1 6 | 7 | two: 8 | name: MyString 9 | version: 1 10 | -------------------------------------------------------------------------------- /test/fixtures/spec_scopes.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | scope: MyString 5 | 6 | two: 7 | scope: MyString 8 | -------------------------------------------------------------------------------- /test/functional/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/test/functional/.gitkeep -------------------------------------------------------------------------------- /test/functional/specs_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SpecsControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/test/integration/.gitkeep -------------------------------------------------------------------------------- /test/performance/browsing_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'rails/performance_test_help' 3 | 4 | class BrowsingTest < ActionDispatch::PerformanceTest 5 | # Refer to the documentation for all available options 6 | # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory] 7 | # :output => 'tmp/performance', :formats => [:flat] } 8 | 9 | def test_homepage 10 | get '/' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] = "test" 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. 7 | # 8 | # Note: You'll currently still have to declare fixtures explicitly in integration tests 9 | # -- they do not yet inherit this setting 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | end 14 | -------------------------------------------------------------------------------- /test/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/test/unit/.gitkeep -------------------------------------------------------------------------------- /test/unit/document_file_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DocumentFileTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/document_serie_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DocumentSerieTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/document_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DocumentTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/document_toc_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DocumentTocTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/document_version_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DocumentVersionTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/helpers/specs_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SpecsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/unit/release_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ReleaseTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/spec_scope_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SpecScopeTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/vendor/assets/javascripts/.gitkeep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/vendor/assets/stylesheets/.gitkeep -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoRfr/spex-3gpp/70d876e4c2076ae91f57d7dc1ab1e1869ad50170/vendor/plugins/.gitkeep --------------------------------------------------------------------------------